source: src/Actions/FragmentationAction/FitPotentialAction.cpp@ 48d20d

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 48d20d was 48d20d, checked in by Frederik Heber <heber@…>, 12 years ago

Added new action FitPotentialAction to fit empirical potentials.

  • moved functionality from levmartester into new Action.
  • removed levmartester.
  • needs both enable-levmar and path to libs/include with-levmar. This allows checking as distinct enable switch.
  • added regression test Fragmentation/FitPotential for morse and harmonic_angle fit to water molecule. Using awk to check on L2 error.
  • added take-best-of option such that fits is done as many times and best (in terms of l2 error) is used. This should make regression test FitPotential more stable (right now we take best of 5).
  • DOCU: extended construct documentation due to new PotentialTypes construct.
  • DOCU: made construct lists items appear alphabetically.
  • DOCU: extended installation documentation with VTK and levmar.
  • DOCU: also URLs for scafacos, VTK, and levmar.
  • Property mode set to 100644
File size: 11.0 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2013 University of Bonn. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * FitPotentialAction.cpp
25 *
26 * Created on: Apr 09, 2013
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35// needs to come before MemDebug due to placement new
36#include <boost/archive/text_iarchive.hpp>
37
38#include "CodePatterns/MemDebug.hpp"
39
40#include <algorithm>
41#include <boost/bind.hpp>
42#include <boost/filesystem.hpp>
43#include <boost/foreach.hpp>
44#include <map>
45#include <string>
46#include <sstream>
47
48#include "Actions/FragmentationAction/FitPotentialAction.hpp"
49
50#include "CodePatterns/Log.hpp"
51
52#include "Element/element.hpp"
53#include "Fragmentation/Homology/HomologyContainer.hpp"
54#include "Fragmentation/Homology/HomologyGraph.hpp"
55#include "Fragmentation/Summation/SetValues/Fragment.hpp"
56#include "FunctionApproximation/Extractors.hpp"
57#include "FunctionApproximation/FunctionApproximation.hpp"
58#include "FunctionApproximation/FunctionModel.hpp"
59#include "FunctionApproximation/TrainingData.hpp"
60#include "FunctionApproximation/writeDistanceEnergyTable.hpp"
61#include "Potentials/PotentialFactory.hpp"
62#include "Potentials/SerializablePotential.hpp"
63
64using namespace MoleCuilder;
65
66// and construct the stuff
67#include "FitPotentialAction.def"
68#include "Action_impl_pre.hpp"
69/** =========== define the function ====================== */
70
71HomologyGraph getFirstGraphwithSpecifiedElements(
72 const HomologyContainer &homologies,
73 const SerializablePotential::ParticleTypes_t &types)
74{
75 ASSERT( !types.empty(),
76 "getFirstGraphwithSpecifiedElements() - charges is empty?");
77 // create charges
78 Fragment::charges_t charges;
79 charges.resize(types.size());
80 std::transform(types.begin(), types.end(),
81 charges.begin(), boost::lambda::_1);
82 // convert into count map
83 Extractors::elementcounts_t counts_per_charge =
84 Extractors::_detail::getElementCounts(charges);
85 ASSERT( !counts_per_charge.empty(),
86 "getFirstGraphwithSpecifiedElements() - charge counts are empty?");
87 LOG(2, "DEBUG: counts_per_charge is " << counts_per_charge << ".");
88 // we want to check each (unique) key only once
89 HomologyContainer::const_key_iterator olditer = homologies.key_end();
90 for (HomologyContainer::const_key_iterator iter =
91 homologies.key_begin(); iter != homologies.key_end(); olditer = iter++) {
92 // if it's the same as the old one, skip it
93 if (*olditer == *iter)
94 continue;
95 // if it's a new key, check if every element has the right number of counts
96 Extractors::elementcounts_t::const_iterator countiter = counts_per_charge.begin();
97 for (; countiter != counts_per_charge.end(); ++countiter)
98 if (!(*iter).hasTimesAtomicNumber(countiter->first,countiter->second))
99 break;
100 if( countiter == counts_per_charge.end())
101 return *iter;
102 }
103 return HomologyGraph();
104}
105
106Action::state_ptr FragmentationFitPotentialAction::performCall() {
107 // charges specify the potential type
108 SerializablePotential::ParticleTypes_t chargenumbers;
109 {
110 const std::vector<const element *> &charges = params.charges.get();
111 std::transform(charges.begin(), charges.end(), std::back_inserter(chargenumbers),
112 boost::bind(&element::getAtomicNumber, _1));
113 }
114 // fragment specifies the homology fragment to use
115 SerializablePotential::ParticleTypes_t fragmentnumbers;
116 {
117 const std::vector<const element *> &fragment = params.fragment.get();
118 std::transform(fragment.begin(), fragment.end(), std::back_inserter(fragmentnumbers),
119 boost::bind(&element::getAtomicNumber, _1));
120 }
121
122 // parse homologies into container
123 HomologyContainer homologies;
124 if (boost::filesystem::exists(params.homology_file.get())) {
125 std::ifstream returnstream(params.homology_file.get().string().c_str());
126 if (returnstream.good()) {
127 boost::archive::text_iarchive ia(returnstream);
128 ia >> homologies;
129 } else {
130 ELOG(0, "Failed to parse from " << params.homology_file.get().string() << ".");
131 return Action::failure;
132 }
133 returnstream.close();
134 } else {
135 ELOG(0, params.homology_file.get() << " does not exist.");
136 return Action::failure;
137 }
138
139 // first we try to look into the HomologyContainer
140 LOG(1, "INFO: Listing all present homologies ...");
141 for (HomologyContainer::container_t::const_iterator iter =
142 homologies.begin(); iter != homologies.end(); ++iter) {
143 LOG(1, "INFO: graph " << iter->first << " has Fragment " << iter->second.first
144 << " and associated energy " << iter->second.second << ".");
145 }
146
147 LOG(0, "STATUS: I'm training now a " << params.potentialtype.get() << " potential on charges "
148 << chargenumbers << " on data from " << params.homology_file.get() << ".");
149
150 /******************** TRAINING ********************/
151 // fit potential
152 FunctionModel *model =
153 PotentialFactory::getInstance().createInstance(
154 params.potentialtype.get(),
155 chargenumbers);
156 ASSERT( model != NULL,
157 "main() - model returned from PotentialFactory is NULL.");
158 FunctionModel::parameters_t bestparams(model->getParameterDimension(), 0.);
159 {
160 // then we ought to pick the right HomologyGraph ...
161 const HomologyGraph graph = getFirstGraphwithSpecifiedElements(homologies,fragmentnumbers);
162 if (graph != HomologyGraph()) {
163 LOG(1, "First representative graph containing fragment "
164 << fragmentnumbers << " is " << graph << ".");
165
166 // Afterwards we go through all of this type and gather the distance and the energy value
167 TrainingData data(model->getFragmentSpecificExtractor());
168 data(homologies.getHomologousGraphs(graph));
169
170 // print distances and energies if desired for debugging
171 if (!data.getTrainingInputs().empty()) {
172 // print which distance is which
173 size_t counter=1;
174 if (DoLog(3)) {
175 const FunctionModel::arguments_t &inputs = data.getTrainingInputs()[0];
176 for (FunctionModel::arguments_t::const_iterator iter = inputs.begin();
177 iter != inputs.end(); ++iter) {
178 const argument_t &arg = *iter;
179 LOG(3, "DEBUG: distance " << counter++ << " is between (#"
180 << arg.indices.first << "c" << arg.types.first << ","
181 << arg.indices.second << "c" << arg.types.second << ").");
182 }
183 }
184
185 // print table
186 LOG(3, "DEBUG: I gathered the following training data:\n" <<
187 _detail::writeDistanceEnergyTable(data.getDistanceEnergyTable()));
188 }
189
190 // now perform the function approximation by optimizing the model function
191 FunctionApproximation approximator(data, *model);
192 if (model->isBoxConstraint() && approximator.checkParameterDerivatives()) {
193 double l2error = std::numeric_limits<double>::infinity();
194 // seed with current time
195 srand((unsigned)time(0));
196 for (unsigned int runs=0; runs < params.best_of_howmany.get(); ++runs) {
197 // generate new random initial parameter values
198 model->setParametersToRandomInitialValues(data);
199 LOG(1, "INFO: Initial parameters of run " << runs << " are "
200 << model->getParameters() << ".");
201 approximator(FunctionApproximation::ParameterDerivative);
202 LOG(1, "INFO: Final parameters of run " << runs << " are "
203 << model->getParameters() << ".");
204 const double new_l2error = data.getL2Error(*model);
205 if (new_l2error < l2error) {
206 // store currently best parameters
207 l2error = new_l2error;
208 bestparams = model->getParameters();
209 LOG(1, "STATUS: New fit from run " << runs
210 << " has better error of " << l2error << ".");
211 }
212 }
213 // reset parameters from best fit
214 model->setParameters(bestparams);
215 LOG(1, "INFO: Best parameters with L2 error of "
216 << l2error << " are " << model->getParameters() << ".");
217 } else {
218 ELOG(0, "We require parameter derivatives for a box constraint minimization.");
219 return Action::failure;
220 }
221
222 // create a map of each fragment with error.
223 typedef std::multimap< double, size_t > WorseFragmentMap_t;
224 WorseFragmentMap_t WorseFragmentMap;
225 HomologyContainer::range_t fragmentrange = homologies.getHomologousGraphs(graph);
226 // fragments make it into the container in reversed order, hence count from top down
227 size_t index= std::distance(fragmentrange.first, fragmentrange.second)-1;
228 for (HomologyContainer::const_iterator iter = fragmentrange.first;
229 iter != fragmentrange.second;
230 ++iter) {
231 const Fragment& fragment = iter->second.first;
232 const double &energy = iter->second.second;
233
234 // create arguments from the fragment
235 FunctionModel::extractor_t extractor = model->getFragmentSpecificExtractor();
236 FunctionModel::arguments_t args = extractor(fragment, 1);
237
238 // calculate value from potential
239 const double fitvalue = (*model)(args)[0];
240
241 // insert difference into map
242 const double error = fabs(energy - fitvalue);
243 WorseFragmentMap.insert( std::make_pair( error, index-- ) );
244
245 {
246 // give only the distances in the debugging text
247 std::stringstream streamargs;
248 BOOST_FOREACH (argument_t arg, args) {
249 streamargs << " " << arg.distance*AtomicLengthToAngstroem;
250 }
251 LOG(2, "DEBUG: frag.#" << index+1 << "'s error is |" << energy << " - " << fitvalue
252 << "| = " << error << " for args " << streamargs.str() << ".");
253 }
254 }
255 LOG(0, "RESULT: WorstFragmentMap " << WorseFragmentMap << ".");
256
257 SerializablePotential *potential = dynamic_cast<SerializablePotential *>(model);
258 if (potential != NULL) {
259 LOG(1, "STATUS: Resulting parameters are " << std::endl << *potential);
260 } else {
261 LOG(1, "INFO: FunctionModel is no serializable potential.");
262 }
263 }
264 }
265 delete model;
266
267 return Action::success;
268}
269
270Action::state_ptr FragmentationFitPotentialAction::performUndo(Action::state_ptr _state) {
271 return Action::success;
272}
273
274Action::state_ptr FragmentationFitPotentialAction::performRedo(Action::state_ptr _state){
275 return Action::success;
276}
277
278bool FragmentationFitPotentialAction::canUndo() {
279 return false;
280}
281
282bool FragmentationFitPotentialAction::shouldUndo() {
283 return false;
284}
285/** =========== end of function ====================== */
Note: See TracBrowser for help on using the repository browser.