/* * 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()); }