source: src/Dynamics/BondVectors.cpp@ f433ec

AutomationFragmentation_failures Candidate_v1.6.1 ChemicalSpaceEvaluator Exclude_Hydrogens_annealWithBondGraph ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_contraction-expansion Gui_displays_atomic_force_velocity PythonUI_with_named_parameters StoppableMakroAction TremoloParser_IncreasedPrecision
Last change on this file since f433ec was f433ec, checked in by Frederik Heber <frederik.heber@…>, 7 years ago

We now obtain weights via levmar minimization.

  • this should yield the best possible weights within the interval of [1/n,1.].
  • note that we cannot always get an exact solution because of this constraint.
  • 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) 2017 Frederik Heber. 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 * BondVectors.cpp
25 *
26 * Created on: Jun 13, 2017
27 * Author: heber
28 */
29
30
31// include config.h
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36//#include "CodePatterns/MemDebug.hpp"
37
38#include "BondVectors.hpp"
39
40#include <algorithm>
41#include <functional>
42#include <iterator>
43#include <numeric>
44
45#include "CodePatterns/Assert.hpp"
46#include "CodePatterns/Log.hpp"
47
48#include <levmar.h>
49
50#include "Atom/atom.hpp"
51#include "Bond/bond.hpp"
52#include "Helpers/defs.hpp"
53
54void BondVectors::recalculateBondVectorsAtStep(
55 const size_t &_step) const
56{
57 current_mapped_vectors.clear();
58
59 ASSERT( !container.empty(),
60 "BondVectors::getBondVectors() - container empty, not set properly?");
61 for (container_t::const_iterator iter = container.begin();
62 iter != container.end(); ++iter) {
63 const bond::ptr &current_bond = *iter;
64 Vector BondVector = current_bond->leftatom->getPositionAtStep(_step)
65 - current_bond->rightatom->getPositionAtStep(_step);
66 BondVector.Normalize();
67 current_mapped_vectors.insert( std::make_pair(current_bond, BondVector) );
68 }
69 ASSERT( current_mapped_vectors.size() == container.size(),
70 "BondVectors::getBondVectors() - not same amount of bond vectors as bonds?");
71
72 map_is_dirty = false;
73 current_step_for_map = _step;
74}
75
76size_t BondVectors::getIndexForBond(const bond::ptr &_bond) const
77{
78 std::pair<
79 container_t::const_iterator,
80 container_t::const_iterator> iters =
81 std::equal_range(container.begin(), container.end(), _bond);
82 if (iters.first != container.end())
83 return std::distance(container.begin(), iters.first);
84 else
85 return (size_t)-1;
86}
87
88std::vector<Vector> BondVectors::getAtomsBondVectorsAtStep(
89 const atom &_walker,
90 const size_t &_step) const
91{
92 if (map_is_dirty || (current_step_for_map != _step))
93 recalculateBondVectorsAtStep(_step);
94
95 std::vector<Vector> BondVectors;
96 // gather subset of BondVectors for the current atom
97 const BondList& ListOfBonds = _walker.getListOfBonds();
98 for(BondList::const_iterator bonditer = ListOfBonds.begin();
99 bonditer != ListOfBonds.end(); ++bonditer) {
100 const bond::ptr &current_bond = *bonditer;
101 const BondVectors::mapped_t::const_iterator bviter =
102 current_mapped_vectors.find(current_bond);
103 ASSERT( bviter != current_mapped_vectors.end(),
104 "ForceAnnealing() - cannot find current_bond ?");
105 ASSERT( bviter != current_mapped_vectors.end(),
106 "ForceAnnealing - cannot find current bond "+toString(*current_bond)
107 +" in bonds.");
108 BondVectors.push_back(bviter->second);
109 }
110 LOG(4, "DEBUG: BondVectors for atom #" << _walker.getId() << ": " << BondVectors);
111
112 return BondVectors;
113}
114
115struct WeightsMinimization {
116
117 static void evaluate(double *p, double *x, int m, int n, void *data)
118 {
119 // current weights in p, output to x
120 double *matrix = static_cast<double*>(data);
121 for(size_t i=0;i<(size_t)n;++i) {
122 x[i] = 0.;
123 for(size_t j=0;j<(size_t)n;++j)
124 x[i] += p[j]*matrix[i*n+j];
125// x[i] = .5*x[i]*x[i];
126 }
127 }
128
129 static void evaluate_derivative(double *p, double *jac, int m, int n, void *data)
130 {
131 // current weights in p, output to x
132 double *matrix = static_cast<double*>(data);
133// // tmp = (Bx - 1)
134// double *tmp = new double[n];
135// for(size_t i=0;i<(size_t)n;++i) {
136// tmp[i] = -1.;
137// for(size_t j=0;j<(size_t)n;++j)
138// tmp[i] += p[j]*matrix[i*n+j];
139// }
140// // tmp(i) * B_(ij)
141// for(size_t i=0;i<(size_t)n;++i)
142// for(size_t j=0;j<(size_t)n;++j)
143// jac[i*n+j] = tmp[i]*matrix[i*n+j];
144// delete[] tmp ;
145 for(size_t i=0;i<(size_t)n;++i)
146 for(size_t j=0;j<(size_t)n;++j)
147 jac[i*n+j] = matrix[i*n+j];
148 }
149
150 static double* getMatrixFromBondVectors(
151 const std::vector<Vector> &_bondvectors
152 )
153 {
154 const size_t n = _bondvectors.size();
155 double *matrix = new double[n*n];
156 size_t i=0;
157 for (std::vector<Vector>::const_iterator iter = _bondvectors.begin();
158 iter != _bondvectors.end(); ++iter, ++i) {
159 size_t j=0;
160 for (std::vector<Vector>::const_iterator otheriter = _bondvectors.begin();
161 otheriter != _bondvectors.end(); ++otheriter, ++j) {
162 // only magnitude is important not the sign
163 matrix[i*n+j] = fabs((*iter).ScalarProduct(*otheriter));
164 }
165 }
166
167 return matrix;
168 }
169};
170
171
172BondVectors::weights_t BondVectors::getWeightsForAtomAtStep(
173 const atom &_walker,
174 const std::vector<Vector> &_bondvectors,
175 const size_t &_step) const
176{
177 // let levmar optimize
178 register int i, j;
179 int ret;
180 double *p;
181 double *x;
182 int n=_bondvectors.size();
183 double opts[LM_OPTS_SZ], info[LM_INFO_SZ];
184
185 double *matrix = WeightsMinimization::getMatrixFromBondVectors(_bondvectors);
186
187 weights_t weights(n, 0.);
188
189 // minim. options [\tau, \epsilon1, \epsilon2, \epsilon3]. Respectively the scale factor for initial \mu,
190 // * stopping thresholds for ||J^T e||_inf, ||Dp||_2 and ||e||_2.
191 opts[0]=LM_INIT_MU; opts[1]=1E-15; opts[2]=1E-15; opts[3]=1E-20;
192 opts[4]= LM_DIFF_DELTA; // relevant only if the Jacobian is approximated using finite differences; specifies forward differencing
193 //opts[4]=-LM_DIFF_DELTA; // specifies central differencing to approximate Jacobian; more accurate but more expensive to compute!
194
195 // prepare initial values for weights
196 p = new double[n];
197 for (i=0;i<n;++i)
198 p[i] = 1.;
199 // prepare output value, i.e. the row sums
200 x = new double[n];
201 for (i=0;i<n;++i)
202 x[i] = 1.;
203
204 {
205 double *work, *covar;
206 work=(double *)malloc((LM_DIF_WORKSZ(n, n)+n*n)*sizeof(double));
207 if(!work){
208 ELOG(0, "BondVectors::getWeightsForAtomAtStep() - memory allocation request failed.");
209 return weights;
210 }
211 covar=work+LM_DIF_WORKSZ(n, n);
212
213 // give this pointer as additional data to construct function pointer in
214 // LevMarCallback and call
215 double *lb = new double[n];
216 double *ub = new double[n];
217 for (i=0;i<n;++i) {
218 lb[i] = 1./(double)n;
219 ub[i] = 1.;
220 }
221 ret=dlevmar_bc_der(
222 &WeightsMinimization::evaluate,
223 &WeightsMinimization::evaluate_derivative,
224 p, x, n, n, lb, ub, NULL, 1000, opts, info, work, covar, matrix); // no Jacobian, caller allocates work memory, covariance estimated
225 delete[] lb;
226 delete[] ub;
227
228 if (0)
229 {
230 std::stringstream covar_msg;
231 covar_msg << "Covariance of the fit:\n";
232 for(i=0; i<n; ++i){
233 for(j=0; j<n; ++j)
234 covar_msg << covar[i*n+j] << " ";
235 covar_msg << std::endl;
236 }
237 covar_msg << std::endl;
238 LOG(1, "INFO: " << covar_msg.str());
239 }
240
241 free(work);
242 }
243
244 if (DoLog(4)) {
245 std::stringstream result_msg;
246 result_msg << "Levenberg-Marquardt returned " << ret << " in " << info[5] << " iter, reason " << info[6] << "\nSolution: ";
247 for(i=0; i<n; ++i)
248 result_msg << p[i] << " ";
249 result_msg << "\n\nMinimization info:\n";
250 std::vector<std::string> infonames(LM_INFO_SZ);
251 infonames[0] = std::string("||e||_2 at initial p");
252 infonames[1] = std::string("||e||_2");
253 infonames[2] = std::string("||J^T e||_inf");
254 infonames[3] = std::string("||Dp||_2");
255 infonames[4] = std::string("mu/max[J^T J]_ii");
256 infonames[5] = std::string("# iterations");
257 infonames[6] = std::string("reason for termination");
258 infonames[7] = std::string(" # function evaluations");
259 infonames[8] = std::string(" # Jacobian evaluations");
260 infonames[9] = std::string(" # linear systems solved");
261 for(i=0; i<LM_INFO_SZ; ++i)
262 result_msg << infonames[i] << ": " << info[i] << " ";
263 result_msg << std::endl;
264 LOG(4, "DEBUG: " << result_msg.str());
265 }
266
267 std::copy(p, p+n, weights.begin());
268 LOG(4, "DEBUG: Weights for atom #" << _walker.getId() << ": " << weights);
269
270 delete[] p;
271 delete[] x;
272 delete[] matrix;
273
274 return weights;
275}
276
277BondVectors::weights_t BondVectors::getWeightsForAtomAtStep(
278 const atom &_walker,
279 const size_t &_step) const
280{
281 const std::vector<Vector> BondVectors =
282 getAtomsBondVectorsAtStep(_walker, _step);
283
284 return getWeightsForAtomAtStep(_walker, BondVectors, _step);
285}
286
287Vector BondVectors::getRemnantGradientForAtomAtStep(
288 const atom &_walker,
289 const std::vector<Vector> _BondVectors,
290 const BondVectors::weights_t &_weights,
291 const size_t &_step,
292 forcestore_t _forcestore) const
293{
294 const Vector &walkerGradient = _walker.getAtomicForceAtStep(_step);
295 BondVectors::weights_t::const_iterator weightiter = _weights.begin();
296 std::vector<Vector>::const_iterator vectoriter = _BondVectors.begin();
297 const BondList& ListOfBonds = _walker.getListOfBonds();
298
299 Vector forcesum;
300 for(BondList::const_iterator bonditer = ListOfBonds.begin();
301 bonditer != ListOfBonds.end(); ++bonditer, ++weightiter, ++vectoriter) {
302 const bond::ptr &current_bond = *bonditer;
303 const Vector &BondVector = *vectoriter;
304
305 const double temp = (*weightiter)*walkerGradient.ScalarProduct(BondVector);
306 _forcestore(_walker, current_bond, _step, temp);
307 LOG(4, "DEBUG: BondVector " << BondVector << " receives projected force of "
308 << temp);
309 forcesum += temp * BondVector;
310 }
311 ASSERT( weightiter == _weights.end(),
312 "BondVectors::getRemnantGradientForAtomAtStep() - weightiter is not at end when it should be.");
313 ASSERT( vectoriter == _BondVectors.end(),
314 "BondVectors::getRemnantGradientForAtomAtStep() - vectoriter is not at end when it should be.");
315
316 return walkerGradient-forcesum;
317}
318
319bool BondVectors::getCheckWeightSumForAtomAtStep(
320 const atom &_walker,
321 const std::vector<Vector> _BondVectors,
322 const BondVectors::weights_t &_weights,
323 const size_t &_step) const
324{
325 bool status = true;
326 for (std::vector<Vector>::const_iterator iter = _BondVectors.begin();
327 iter != _BondVectors.end(); ++iter) {
328 std::vector<double> scps;
329 scps.reserve(_BondVectors.size());
330 std::transform(
331 _BondVectors.begin(), _BondVectors.end(),
332 _weights.begin(),
333 std::back_inserter(scps),
334 boost::bind(static_cast< double (*)(double) >(&fabs),
335 boost::bind(std::multiplies<double>(),
336 boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1),
337 _2))
338 );
339 const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.);
340 if (fabs(scp_sum -1.) > MYEPSILON)
341 status = false;
342 }
343
344 return status;
345}
Note: See TracBrowser for help on using the repository browser.