1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.potential.nonbonded;
39
40 import static ffx.numerics.math.ScalarMath.binomial;
41 import static java.util.Arrays.fill;
42
43 import ffx.potential.nonbonded.pme.LambdaMode;
44 import ffx.potential.parameters.ForceField;
45 import java.util.logging.Level;
46 import java.util.logging.Logger;
47 import org.apache.commons.math3.fitting.leastsquares.EvaluationRmsChecker;
48 import org.apache.commons.math3.fitting.leastsquares.LeastSquaresFactory;
49 import org.apache.commons.math3.fitting.leastsquares.LeastSquaresOptimizer;
50 import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem;
51 import org.apache.commons.math3.fitting.leastsquares.LevenbergMarquardtOptimizer;
52 import org.apache.commons.math3.fitting.leastsquares.MultivariateJacobianFunction;
53 import org.apache.commons.math3.linear.Array2DRowRealMatrix;
54 import org.apache.commons.math3.linear.ArrayRealVector;
55 import org.apache.commons.math3.linear.RealMatrix;
56 import org.apache.commons.math3.linear.RealVector;
57 import org.apache.commons.math3.optim.ConvergenceChecker;
58 import org.apache.commons.math3.util.Pair;
59
60
61
62
63
64
65
66 public class ScfPredictor {
67
68 private static final Logger logger = Logger.getLogger(ScfPredictor.class.getName());
69
70 private static final double eps = 1.0e-4;
71
72 private final PredictorMode predictorMode;
73
74 private final int predictorOrder;
75
76 protected double[][][] inducedDipole;
77
78 protected double[][][] inducedDipoleCR;
79
80 private int nAtoms;
81
82 private int mode;
83
84 private double[][][][] predictorInducedDipole;
85
86 private double[][][][] predictorInducedDipoleCR;
87
88 private int predictorStartIndex;
89
90 private int predictorCount;
91
92
93
94
95 private LeastSquaresPredictor leastSquaresPredictor;
96
97
98
99
100
101
102
103 public ScfPredictor(PredictorMode mode, int order, ForceField ff) {
104 predictorMode = mode;
105 predictorOrder = order;
106 predictorCount = 0;
107 predictorStartIndex = 0;
108 if (predictorMode != PredictorMode.NONE) {
109 if (predictorMode == PredictorMode.LS) {
110 leastSquaresPredictor = new LeastSquaresPredictor(eps);
111 }
112 if (ff.getBoolean("LAMBDATERM", false) ) {
113 predictorInducedDipole = new double[3][predictorOrder][nAtoms][3];
114 predictorInducedDipoleCR = new double[3][predictorOrder][nAtoms][3];
115 } else {
116 predictorInducedDipole = new double[1][predictorOrder][nAtoms][3];
117 predictorInducedDipoleCR = new double[1][predictorOrder][nAtoms][3];
118 }
119 }
120 }
121
122
123
124
125
126
127 public void run(LambdaMode lambdaMode) {
128 if (predictorMode == PredictorMode.NONE) {
129 return;
130 }
131 switch (lambdaMode) {
132 case CONDENSED_NO_LIGAND:
133 mode = 1;
134 break;
135 case VAPOR:
136 mode = 2;
137 break;
138 default:
139 mode = 0;
140 }
141 if (predictorMode != PredictorMode.NONE) {
142 switch (predictorMode) {
143 case ASPC:
144 aspcPredictor();
145 break;
146 case LS:
147 leastSquaresPredictor();
148 break;
149 case POLY:
150 polynomialPredictor();
151 break;
152 case NONE:
153 default:
154 break;
155 }
156 }
157 }
158
159
160
161
162
163
164
165
166
167 public void saveMutualInducedDipoles(
168 double[][][] inducedDipole,
169 double[][][] inducedDipoleCR,
170 double[][] directDipole,
171 double[][] directDipoleCR) {
172
173
174 predictorStartIndex--;
175 if (predictorStartIndex < 0) {
176 predictorStartIndex = predictorOrder - 1;
177 }
178
179 if (predictorCount < predictorOrder) {
180 predictorCount++;
181 }
182
183 for (int i = 0; i < nAtoms; i++) {
184 for (int j = 0; j < 3; j++) {
185 predictorInducedDipole[mode][predictorStartIndex][i][j] =
186 inducedDipole[0][i][j] - directDipole[i][j];
187 predictorInducedDipoleCR[mode][predictorStartIndex][i][j] =
188 inducedDipoleCR[0][i][j] - directDipoleCR[i][j];
189 }
190 }
191 }
192
193
194
195
196
197
198
199
200 public void setInducedDipoleReferences(
201 double[][][] inducedDipole, double[][][] inducedDipoleCR, boolean lambdaTerm) {
202 this.inducedDipole = inducedDipole;
203 this.inducedDipoleCR = inducedDipoleCR;
204 if (lambdaTerm) {
205 predictorInducedDipole = new double[3][predictorOrder][nAtoms][3];
206 predictorInducedDipoleCR = new double[3][predictorOrder][nAtoms][3];
207 } else {
208 predictorInducedDipole = new double[1][predictorOrder][nAtoms][3];
209 predictorInducedDipoleCR = new double[1][predictorOrder][nAtoms][3];
210 }
211 }
212
213
214 @Override
215 public String toString() {
216 return predictorMode.toString();
217 }
218
219
220
221
222
223 private void leastSquaresPredictor() {
224 if (predictorCount < 2) {
225 return;
226 }
227 try {
228
229
230
231
232 leastSquaresPredictor.updateJacobianAndTarget();
233 int maxEvals, maxIter;
234 maxEvals = maxIter = 1000;
235 LeastSquaresOptimizer.Optimum optimum = leastSquaresPredictor.predict(maxEvals, maxIter);
236 double[] optimalValues = optimum.getPoint().toArray();
237 if (logger.isLoggable(Level.FINEST)) {
238 logger.finest(String.format("\n LS RMS: %10.6f", optimum.getRMS()));
239 logger.finest(String.format(" LS Iterations: %10d", optimum.getIterations()));
240 logger.finest(String.format(" Jacobian Evals: %10d", optimum.getEvaluations()));
241 logger.finest(String.format(" Root Mean Square: %10.6f", optimum.getRMS()));
242 logger.finest(" LS Coefficients");
243 for (int i = 0; i < predictorOrder - 1; i++) {
244 logger.finest(String.format(" %2d %10.6f", i + 1, optimalValues[i]));
245 }
246 }
247
248
249 int index = predictorStartIndex;
250
251
252
253
254 for (int k = 0; k < predictorOrder - 1; k++) {
255
256 double c = optimalValues[k];
257 for (int i = 0; i < nAtoms; i++) {
258 for (int j = 0; j < 3; j++) {
259 inducedDipole[0][i][j] += c * predictorInducedDipole[mode][index][i][j];
260 inducedDipoleCR[0][i][j] += c * predictorInducedDipoleCR[mode][index][i][j];
261 }
262 }
263 index++;
264 if (index >= predictorOrder) {
265 index = 0;
266 }
267 }
268 } catch (Exception e) {
269 logger.log(Level.WARNING, " Exception computing predictor coefficients", e);
270 }
271 }
272
273
274 private void aspcPredictor() {
275
276 if (predictorCount < 6) {
277 return;
278 }
279
280 final double[] aspc = {
281 22.0 / 7.0, -55.0 / 14.0, 55.0 / 21.0, -22.0 / 21.0, 5.0 / 21.0, -1.0 / 42.0
282 };
283
284 int index = predictorStartIndex;
285
286 for (int k = 0; k < 6; k++) {
287
288 double c = aspc[k];
289 for (int i = 0; i < nAtoms; i++) {
290 for (int j = 0; j < 3; j++) {
291 inducedDipole[0][i][j] += c * predictorInducedDipole[mode][index][i][j];
292 inducedDipoleCR[0][i][j] += c * predictorInducedDipoleCR[mode][index][i][j];
293 }
294 }
295 index++;
296 if (index >= predictorOrder) {
297 index = 0;
298 }
299 }
300 }
301
302
303 private void polynomialPredictor() {
304
305 if (predictorCount == 0) {
306 return;
307 }
308
309
310 int n = predictorOrder;
311 if (predictorCount < predictorOrder) {
312 n = predictorCount;
313 }
314
315 int index = predictorStartIndex;
316
317 double sign = -1.0;
318
319 for (int k = 0; k < n; k++) {
320
321 sign *= -1.0;
322 double c = sign * binomial(n, k);
323 for (int i = 0; i < nAtoms; i++) {
324 for (int j = 0; j < 3; j++) {
325 inducedDipole[0][i][j] += c * predictorInducedDipole[mode][index][i][j];
326 inducedDipoleCR[0][i][j] += c * predictorInducedDipoleCR[mode][index][i][j];
327 }
328 }
329 index++;
330 if (index >= predictorOrder) {
331 index = 0;
332 }
333 }
334 }
335
336 public enum PredictorMode {
337 NONE,
338 LS,
339 POLY,
340 ASPC
341 }
342
343 private class LeastSquaresPredictor {
344
345 double[] weights;
346 double[] target;
347 double[][] jacobian;
348 double[] initialSolution;
349 double tolerance;
350 RealVector valuesVector;
351 RealVector targetVector;
352 RealMatrix jacobianMatrix;
353 LeastSquaresOptimizer optimizer;
354 ConvergenceChecker<LeastSquaresProblem.Evaluation> checker;
355 Pair<RealVector, RealMatrix> test = new Pair<>(targetVector, jacobianMatrix);
356 MultivariateJacobianFunction function =
357 new MultivariateJacobianFunction() {
358 @Override
359 public Pair<RealVector, RealMatrix> value(RealVector point) {
360 return new Pair<>(targetVector, jacobianMatrix);
361 }
362 };
363
364 public LeastSquaresPredictor(double eps) {
365 tolerance = eps;
366 weights = new double[2 * nAtoms * 3];
367 target = new double[2 * nAtoms * 3];
368 jacobian = new double[2 * nAtoms * 3][predictorOrder - 1];
369 initialSolution = new double[predictorOrder - 1];
370 fill(weights, 1.0);
371 fill(initialSolution, 0.0);
372 initialSolution[0] = 1.0;
373 optimizer = new LevenbergMarquardtOptimizer().withParameterRelativeTolerance(eps);
374 checker = new EvaluationRmsChecker(eps);
375 }
376
377 public LeastSquaresOptimizer.Optimum predict(int maxEval, int maxIter) {
378 RealVector start = new ArrayRealVector(initialSolution);
379 LeastSquaresProblem lsp =
380 LeastSquaresFactory.create(function, targetVector, start, checker, maxEval, maxIter);
381
382 LeastSquaresOptimizer.Optimum optimum = optimizer.optimize(lsp);
383 if (true)
384 logger.info(
385 String.format(
386 " LS Optimization parameters:" + " %s %s\n" + " %s %s\n" + " %d %d",
387 function,
388 targetVector.toString(),
389 start,
390 checker.toString(),
391 maxIter,
392 maxEval));
393 return optimum;
394 }
395
396 public void updateJacobianAndTarget() {
397
398
399 int index = 0;
400 for (int i = 0; i < nAtoms; i++) {
401 target[index++] = predictorInducedDipole[mode][predictorStartIndex][i][0];
402 target[index++] = predictorInducedDipole[mode][predictorStartIndex][i][1];
403 target[index++] = predictorInducedDipole[mode][predictorStartIndex][i][2];
404 target[index++] = predictorInducedDipoleCR[mode][predictorStartIndex][i][0];
405 target[index++] = predictorInducedDipoleCR[mode][predictorStartIndex][i][1];
406 target[index++] = predictorInducedDipoleCR[mode][predictorStartIndex][i][2];
407 }
408 targetVector = new ArrayRealVector(target);
409
410
411 index = predictorStartIndex + 1;
412 if (index >= predictorOrder) {
413 index = 0;
414 }
415 for (int j = 0; j < predictorOrder - 1; j++) {
416 int ji = 0;
417 for (int i = 0; i < nAtoms; i++) {
418 jacobian[ji++][j] = predictorInducedDipole[mode][index][i][0];
419 jacobian[ji++][j] = predictorInducedDipole[mode][index][i][1];
420 jacobian[ji++][j] = predictorInducedDipole[mode][index][i][2];
421 jacobian[ji++][j] = predictorInducedDipoleCR[mode][index][i][0];
422 jacobian[ji++][j] = predictorInducedDipoleCR[mode][index][i][1];
423 jacobian[ji++][j] = predictorInducedDipoleCR[mode][index][i][2];
424 }
425 index++;
426 if (index >= predictorOrder) {
427 index = 0;
428 }
429 }
430 jacobianMatrix = new Array2DRowRealMatrix(jacobian);
431 }
432
433 private RealVector value(double[] variables) {
434
435 double[] values = new double[2 * nAtoms * 3];
436 for (int i = 0; i < nAtoms; i++) {
437 int index = 6 * i;
438 values[index] = 0;
439 values[index + 1] = 0;
440 values[index + 2] = 0;
441 values[index + 3] = 0;
442 values[index + 4] = 0;
443 values[index + 5] = 0;
444 int pi = predictorStartIndex + 1;
445 if (pi >= predictorOrder) {
446 pi = 0;
447 }
448 for (int j = 0; j < predictorOrder - 1; j++) {
449 values[index] += variables[j] * predictorInducedDipole[mode][pi][i][0];
450 values[index + 1] += variables[j] * predictorInducedDipole[mode][pi][i][1];
451 values[index + 2] += variables[j] * predictorInducedDipole[mode][pi][i][2];
452 values[index + 3] += variables[j] * predictorInducedDipoleCR[mode][pi][i][0];
453 values[index + 4] += variables[j] * predictorInducedDipoleCR[mode][pi][i][1];
454 values[index + 5] += variables[j] * predictorInducedDipoleCR[mode][pi][i][2];
455 pi++;
456 if (pi >= predictorOrder) {
457 pi = 0;
458 }
459 }
460 }
461 return new ArrayRealVector(values);
462 }
463 }
464 }