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 |
|
---|
54 | void 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 ¤t_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 |
|
---|
76 | size_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 |
|
---|
88 | std::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 ¤t_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 |
|
---|
115 | struct 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 |
|
---|
172 | BondVectors::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 |
|
---|
277 | BondVectors::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 |
|
---|
287 | Vector 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 ¤t_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 |
|
---|
319 | bool 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 | }
|
---|