/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2013 University of Bonn. 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/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.hasTimesAtomicNumber( 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 parameters_t &model_params = model->getParameters(); const size_t model_dim = model_params.size(); 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); } } } 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(); std::copy(params.begin(), params.end(), iter); ASSERT( iter != parameters.end(), "CompoundPotential::getParameters() - iter already 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)) ); } std::vector CompoundPotential::splitUpArgumentsByModels( const arguments_t &arguments) const { std::vector partial_args(models.size()); std::vector::iterator partialiter = partial_args.begin(); arguments_t::const_iterator argiter = arguments.begin(); BOOST_FOREACH( const SerializablePotential::ParticleTypes_t &types, particletypes_per_model) { // we always expect N(N-1)/2 distances for N particle types arguments_t::const_iterator enditer = argiter+(types.size()*(types.size()-1)/2); ASSERT( argiter != arguments.end(), "CompoundPotential::splitUpArgumentsByModels() - incorrect number of arguments."); std::copy(argiter, enditer, std::back_inserter(*partialiter++)); argiter = enditer; } ASSERT( argiter == arguments.end(), "CompoundPotential::splitUpArgumentsByModels() - incorrect number of arguments."); return partial_args; } CompoundPotential::results_t CompoundPotential::operator()(const arguments_t &arguments) const { // first, we have to split up the given arguments std::vector partial_args = splitUpArgumentsByModels(arguments); // then, with each bunch of arguments, we call the specific model results_t results(1,0.); std::vector partial_results(models.size()); std::transform( models.begin(), models.end(), partial_args.begin(), partial_results.begin(), boost::bind(&FunctionModel::operator(), _1, _2) ); std::for_each(partial_results.begin(), partial_results.end(), std::cout << (boost::lambda::_1)[0] << "\t"); std::for_each(partial_results.begin(), partial_results.end(), results[0] += (boost::lambda::_1)[0]); return results; } CompoundPotential::results_t CompoundPotential::parameter_derivative(const arguments_t &arguments, const size_t index) const { // first, we have to split up the given arguments std::vector partial_args = splitUpArgumentsByModels(arguments); // 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 value not less than index std::vector::const_iterator iter = std::lower_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) ); std::vector::const_iterator argiter = partial_args.begin(); std::advance(argiter, std::distance(const_cast &>(dimensions).begin(), iter) ); // evaluate with correct relative index and return const size_t indexbase = (iter == dimensions.begin()) ? 0 : *(--iter); CompoundPotential::results_t results = (*modeliter)->parameter_derivative(*argiter, index-indexbase); return results; } 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(); std::copy(params.begin(), params.end(), iter); ASSERT( iter != constraints.end(), "CompoundPotential::getParameters() - iter already 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(); std::copy(params.begin(), params.end(), iter); ASSERT( iter != constraints.end(), "CompoundPotential::getParameters() - iter already at end."); } return constraints; } FunctionModel::extractor_t CompoundPotential::getFragmentSpecificExtractor() const { // create initial returnfunction particletypes_per_model_t::const_iterator typesiter = particletypes_per_model.begin(); Fragment::charges_t charges; { charges.resize((*typesiter).size()); std::transform((*typesiter).begin(), (*typesiter).end(), charges.begin(), boost::lambda::_1); } FunctionModel::extractor_t returnfunction = boost::bind(&Extractors::gatherAllDistancesFromFragment, boost::bind(&Fragment::getPositions, _1), boost::bind(&Fragment::getCharges, _1), charges, // no crefs here as are temporaries! _2); // every following fragments combines its arguments with the initial function for (; typesiter != particletypes_per_model.end(); ++typesiter) { Fragment::charges_t charges; { charges.resize((*typesiter).size()); std::transform((*typesiter).begin(), (*typesiter).end(), charges.begin(), boost::lambda::_1); } returnfunction = boost::bind(&Extractors::concatenateArguments, boost::bind(returnfunction, _1, _2), boost::bind(&Extractors::gatherAllDistancesFromFragment, boost::bind(&Fragment::getPositions, _1), boost::bind(&Fragment::getCharges, _1), charges, // no crefs here as are temporaries! _2) ); } return returnfunction; }