/*
* Project: MoleCuilder
* Description: creates and alters molecular systems
* Copyright (C) 2013 University of Bonn. All rights reserved.
* Copyright (C) 2013 Frederik Heber. All rights reserved.
*
*
* This file is part of MoleCuilder.
*
* MoleCuilder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* MoleCuilder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MoleCuilder. If not, see .
*/
/*
* CompoundPotential.cpp
*
* Created on: May 8, 2013
* Author: heber
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
#include "CodePatterns/MemDebug.hpp"
#include "Potentials/CompoundPotential.hpp"
#include
#include
#include
#include
#include
#include
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "Element/element.hpp"
#include "Fragmentation/Homology/HomologyGraph.hpp"
#include "Fragmentation/Summation/SetValues/Fragment.hpp"
#include "FunctionApproximation/Extractors.hpp"
#include "Potentials/EmpiricalPotential.hpp"
#include "Potentials/helpers.hpp"
#include "Potentials/PotentialRegistry.hpp"
CompoundPotential::CompoundPotential(const HomologyGraph &graph)
{
LOG(1, "INFO: Creating CompoundPotential for graph " << graph << ".");
// look though graph and place all matching FunctionModel's in
// PotentialRegistry in models
PotentialRegistry::const_iterator potentialiter =
PotentialRegistry::getInstance().getBeginIter();
while (potentialiter != PotentialRegistry::getInstance().getEndIter()) {
// get model and types
EmpiricalPotential * const potential = potentialiter->second;
const SerializablePotential::ParticleTypes_t &types =
potential->getParticleTypes();
// create charges
Fragment::charges_t charges;
charges.resize(types.size());
std::transform(types.begin(), types.end(),
charges.begin(), boost::lambda::_1);
// convert into count map
Extractors::elementcounts_t counts_per_charge =
Extractors::_detail::getElementCounts(charges);
// ASSERT( !counts_per_charge.empty(),
// "getFirstGraphwithSpecifiedElements() - charge counts are empty?");
LOG(2, "DEBUG: counts_per_charge is " << counts_per_charge << ".");
// check whether graph contains suitable types
Extractors::elementcounts_t::const_iterator countiter = counts_per_charge.begin();
for (; countiter != counts_per_charge.end(); ++countiter)
if (!graph.hasGreaterEqualTimesAtomicNumber(
static_cast(countiter->first),
static_cast(countiter->second))
)
break;
// if we have a match for every count, store model
if( countiter == counts_per_charge.end()) {
LOG(1, "INFO: Potential " << potentialiter->first << " matches with fragment.");
models.push_back(static_cast(potential));
particletypes_per_model.push_back(types);
}
++potentialiter;
}
// check that models and particletypes_per_model match
ASSERT( models.size() == particletypes_per_model.size(),
"CompoundPotential::CompoundPotential() - particletypes not stored for all models?");
}
CompoundPotential::~CompoundPotential()
{
// clear all models and internally stored particletypes
models.clear();
particletypes_per_model.clear();
}
void CompoundPotential::setParameters(const parameters_t &_params)
{
size_t dim = _params.size();
parameters_t::const_iterator iter = _params.begin();
BOOST_FOREACH( FunctionModel* model, models) {
const size_t model_dim = model->getParameterDimension();
if (dim > 0) {
parameters_t subparams;
if (dim < model_dim) {
std::copy(iter, iter+dim, std::back_inserter(subparams));
iter += dim;
dim = 0;
} else {
std::copy(iter, iter+model_dim, std::back_inserter(subparams));
iter += model_dim;
dim -= model_dim;
}
model->setParameters(subparams);
}
}
#ifndef NDEBUG
parameters_t check_params(getParameters());
check_params.resize(_params.size()); // truncate to same size
ASSERT( check_params == _params,
"CompoundPotential::setParameters() - failed, mismatch in to be set "
+toString(_params)+" and set "+toString(check_params)+" params.");
#endif
}
CompoundPotential::parameters_t CompoundPotential::getParameters() const
{
const size_t dimension = getParameterDimension();
CompoundPotential::parameters_t parameters(dimension);
CompoundPotential::parameters_t::iterator iter = parameters.begin();
BOOST_FOREACH( const FunctionModel* model, models) {
const CompoundPotential::parameters_t ¶ms = model->getParameters();
ASSERT( iter != parameters.end(),
"CompoundPotential::getParameters() - iter already at end.");
iter = std::copy(params.begin(), params.end(), iter);
}
ASSERT( iter == parameters.end(),
"CompoundPotential::getParameters() - iter not at end.");
return parameters;
}
void CompoundPotential::setParametersToRandomInitialValues(const TrainingData &data)
{
std::for_each(models.begin(), models.end(),
boost::bind(&FunctionModel::setParametersToRandomInitialValues, _1, boost::cref(data))
);
}
size_t CompoundPotential::getParameterDimension() const
{
std::vector dimensions(models.size(), 0);
std::transform(models.begin(), models.end(), dimensions.begin(),
boost::bind(&FunctionModel::getParameterDimension, _1));
return std::accumulate(dimensions.begin(), dimensions.end(), 0, std::plus());
}
void CompoundPotential::setTriplefunction(triplefunction_t &_triplefunction)
{
std::for_each(models.begin(), models.end(),
boost::bind(&FunctionModel::setTriplefunction, _1, boost::ref(_triplefunction))
);
}
bool CompoundPotential::areValidArguments(
const SerializablePotential::ParticleTypes_t &_types,
const arguments_t &args) const
{
// /this function does much the same as Extractors::reorderArgumentsByParticleTypes()
typedef std::list< argument_t > ListArguments_t;
ListArguments_t availableList(args.begin(), args.end());
/// basically, we have two choose any two pairs out of types but only those
/// where the first is less than the letter. Hence, we start the second
/// iterator at the current position of the first one and skip the equal case.
for (SerializablePotential::ParticleTypes_t::const_iterator firstiter = _types.begin();
firstiter != _types.end();
++firstiter) {
for (SerializablePotential::ParticleTypes_t::const_iterator seconditer = firstiter;
seconditer != _types.end();
++seconditer) {
if (seconditer == firstiter)
continue;
// search the right one in _args (we might allow switching places of
// firstiter and seconditer, as distance is symmetric).
// we remove the matching argument to make sure we don't pick it twice
ListArguments_t::iterator iter = availableList.begin();
for (;iter != availableList.end(); ++iter) {
LOG(3, "DEBUG: Current args is " << *iter << ".");
if ((iter->types.first == *firstiter)
&& (iter->types.second == *seconditer)) {
availableList.erase(iter);
break;
}
else if ((iter->types.first == *seconditer)
&& (iter->types.second == *firstiter)) {
availableList.erase(iter);
break;
}
}
if ( iter == availableList.end())
return false;
}
}
return true;
}
CompoundPotential::arguments_by_model_t CompoundPotential::splitUpArgumentsByModelsFilter(
const arguments_t &arguments) const
{
arguments_by_model_t partial_args;
// go through each model and have it filter out its arguments, this already
// returns a list of tuples associated with the specific model
for(models_t::const_iterator modeliter = models.begin();
modeliter != models.end(); ++modeliter) {
FunctionModel::filter_t filterfunction = (*modeliter)->getSpecificFilter();
list_of_arguments_t tempargs = filterfunction(arguments);
// then split up all the bunches, too.
for (list_of_arguments_t::const_iterator argiter = tempargs.begin();
argiter != tempargs.end(); ++argiter) {
const arguments_t &args = *argiter;
partial_args.push_back(
std::make_pair(
*modeliter,
args
)
);
}
}
return partial_args;
}
CompoundPotential::arguments_by_model_t CompoundPotential::splitUpArgumentsByModels(
const list_of_arguments_t &listarguments) const
{
arguments_by_model_t partial_args;
particletypes_per_model_t::const_iterator typesiter = particletypes_per_model.begin();
models_t::const_iterator modeliter = models.begin();
/// add constant model (which is always first model) with empty args if present
if (typesiter->empty()) {
partial_args.push_back(
std::pair(*modeliter, arguments_t())
);
++modeliter;
++typesiter;
}
// then check other models
/// we only have to check whether the current model still matches or whether
/// have to use the next model.
for (list_of_arguments_t::const_iterator argiter = listarguments.begin();
argiter != listarguments.end(); ++argiter) {
const arguments_t &arguments = *argiter;
if (typesiter+1 != particletypes_per_model.end()) {
// check whether next argument bunch is for same model or different one
// we extract both partial_arguments, if the latter fits, we take the latter.
const SerializablePotential::ParticleTypes_t &types = *typesiter;
const SerializablePotential::ParticleTypes_t &nexttypes = *(typesiter+1);
// we always expect N(N-1)/2 distances for N particle types
// check first from sizes alone
const size_t tuplesize = types.size()*(types.size()-1)/2;
const size_t nexttuplesize = nexttypes.size()*(nexttypes.size()-1)/2;
if ((tuplesize != nexttuplesize)) {
if ((arguments.size() == tuplesize) && areValidArguments(types, arguments)) {
// only former still matches, don't increment
partial_args.push_back(
std::make_pair(*modeliter, arguments)
);
} else if ((arguments.size() == nexttuplesize) && areValidArguments(nexttypes, arguments)) {
// latter matches, increment
++typesiter;
partial_args.push_back(
std::make_pair(*(++modeliter), arguments)
);
} else {
ASSERT(0,
"CompoundPotential::splitUpArgumentsByModels() - neither this model nor next model match (size) with current tuple.");
}
} else { // same size, now we have to check the types individually
size_t encodeValidity = 0;
encodeValidity += 1*areValidArguments(types, arguments);
encodeValidity += 2*areValidArguments(nexttypes, arguments);
switch (encodeValidity) {
case 1:
// only former still matches, don't increment
partial_args.push_back(
std::make_pair(*modeliter, arguments)
);
break;
case 2:
++typesiter;
partial_args.push_back(
std::make_pair(*(++modeliter), arguments)
);
break;
case 0:
case 3:
default:
ASSERT(0,
"CompoundPotential::splitUpArgumentsByModels() - neither this model nor next model match (type) with current tuple.");
break;
}
}
} else {
const SerializablePotential::ParticleTypes_t &types = *typesiter;
if (areValidArguments(types, arguments)) {
// only former matches, don't increment
partial_args.push_back(
std::make_pair(*modeliter, arguments)
);
} else {
ASSERT(0,
"CompoundPotential::splitUpArgumentsByModels() - last model does not match with current tuple.");
}
}
}
return partial_args;
}
CompoundPotential::results_t CompoundPotential::operator()(
const list_of_arguments_t &listarguments) const
{
/// first, we have to split up the given arguments
arguments_by_model_t partial_args =
splitUpArgumentsByModels(listarguments);
// print split up argument list for debugging
if (DoLog(4)) {
LOG(4, "Arguments by model are: ");
for(arguments_by_model_t::const_iterator iter = partial_args.begin();
iter != partial_args.end(); ++iter) {
LOG(4, "\tModel with " << iter->first->getParameterDimension()
<< " parameters " << iter->first->getParameters()
<< " and arguments: " << iter->second);
}
}
/// then, with each bunch of arguments, we call the specific model
results_t results(1,0.);
std::vector partial_results;
for(arguments_by_model_t::const_iterator iter = partial_args.begin();
iter != partial_args.end(); ++iter) {
partial_results.push_back(
(*iter->first)(
FunctionModel::list_of_arguments_t(1, iter->second))
);
}
// print partial results for debugging
if (DoLog(4)) {
std::stringstream output;
output << "Partial results are: ";
std::for_each(partial_results.begin(), partial_results.end(),
output << (boost::lambda::_1)[0] << "\t");
LOG(4, output.str());
}
/// Finally, sum up all results and return
std::for_each(partial_results.begin(), partial_results.end(),
results[0] += (boost::lambda::_1)[0]);
return results;
}
CompoundPotential::results_t CompoundPotential::parameter_derivative(
const list_of_arguments_t &listarguments,
const size_t index) const
{
// first, we have to split up the given arguments
arguments_by_model_t partial_args =
splitUpArgumentsByModels(listarguments);
// then, with each bunch of arguments, we call the specific model
// get parameter dimensions per model
std::vector dimensions(models.size(), 0);
std::transform(models.begin(), models.end(), dimensions.begin(),
boost::bind(&FunctionModel::getParameterDimension, _1));
// convert to index end+1 per model
std::partial_sum(dimensions.begin(), dimensions.end(), dimensions.begin());
// look for first value greater than index
std::vector::const_iterator iter =
std::upper_bound(dimensions.begin(), dimensions.end(), index);
// step forward to same model
models_t::const_iterator modeliter = models.begin();
std::advance(modeliter,
std::distance(const_cast &>(dimensions).begin(), iter) );
CompoundPotential::results_t returnresults;
for(arguments_by_model_t::const_iterator argiter = partial_args.begin();
argiter != partial_args.end(); ++argiter) {
const FunctionModel *model = argiter->first;
// for every matching model evaluate
if (model == *modeliter) {
// evaluate with correct relative index and return
const size_t indexbase = (iter == dimensions.begin()) ? 0 : *(iter-1);
CompoundPotential::results_t results =
model->parameter_derivative(
FunctionModel::list_of_arguments_t(1, argiter->second), index-indexbase);
// either set results or add
if (returnresults.empty())
returnresults = results;
else
std::transform(
results.begin(), results.end(),
returnresults.begin(),
returnresults.begin(),
std::plus());
}
}
return returnresults;
}
bool CompoundPotential::isBoxConstraint() const
{
std::vector constraints(models.size(), 0);
std::transform(models.begin(), models.end(), constraints.begin(),
boost::bind(&FunctionModel::getParameterDimension, _1));
return std::accumulate(constraints.begin(), constraints.end(), true,
std::logical_and());
}
CompoundPotential::parameters_t CompoundPotential::getLowerBoxConstraints() const
{
const size_t dimension = getParameterDimension();
CompoundPotential::parameters_t constraints(dimension);
CompoundPotential::parameters_t::iterator iter = constraints.begin();
BOOST_FOREACH( FunctionModel* model, models) {
const CompoundPotential::parameters_t params = model->getLowerBoxConstraints();
ASSERT( iter != constraints.end(),
"CompoundPotential::getLowerBoxConstraints() - iter already at end.");
iter = std::copy(params.begin(), params.end(), iter);
}
ASSERT( iter == constraints.end(),
"CompoundPotential::getLowerBoxConstraints() - iter not at end.");
return constraints;
}
CompoundPotential::parameters_t CompoundPotential::getUpperBoxConstraints() const
{
const size_t dimension = getParameterDimension();
CompoundPotential::parameters_t constraints(dimension);
CompoundPotential::parameters_t::iterator iter = constraints.begin();
BOOST_FOREACH( FunctionModel* model, models) {
const CompoundPotential::parameters_t params = model->getUpperBoxConstraints();
ASSERT( iter != constraints.end(),
"CompoundPotential::getUpperBoxConstraints() - iter already at end.");
iter = std::copy(params.begin(), params.end(), iter);
}
ASSERT( iter == constraints.end(),
"CompoundPotential::getUpperBoxConstraints() - iter not at end.");
return constraints;
}
FunctionModel::filter_t CompoundPotential::getSpecificFilter() const
{
// we must concatenate all filtered arguments here
// create initial returnfunction
FunctionModel::filter_t returnfunction =
boost::bind(&Helpers::returnEmptyListArguments);
// every following fragments combines its arguments with the initial function
for (models_t::const_iterator modeliter = models.begin();
modeliter != models.end(); ++modeliter) {
returnfunction =
boost::bind(&Extractors::concatenateListOfArguments,
boost::bind(returnfunction, _1),
boost::bind((*modeliter)->getSpecificFilter(), _1)
);
}
return returnfunction;
}
size_t CompoundPotential::getSpecificArgumentCount() const
{
std::vector argument_counts(models.size(), 0);
std::transform(models.begin(), models.end(), argument_counts.begin(),
boost::bind(&FunctionModel::getSpecificArgumentCount, _1));
return std::accumulate(argument_counts.begin(), argument_counts.end(), 0,
std::plus());
}