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.algorithms.optimize;
39
40 import static java.lang.String.format;
41 import static java.util.Arrays.fill;
42
43 import ffx.algorithms.AlgorithmListener;
44 import ffx.algorithms.Terminatable;
45 import ffx.algorithms.dynamics.MolecularDynamics;
46 import ffx.algorithms.dynamics.MDEngine;
47 import ffx.numerics.Potential;
48 import ffx.numerics.optimization.LBFGS;
49 import ffx.numerics.optimization.LineSearch;
50 import ffx.numerics.optimization.OptimizationListener;
51 import ffx.potential.ForceFieldEnergy;
52 import ffx.potential.Platform;
53 import ffx.potential.openmm.OpenMMEnergy;
54 import ffx.potential.MolecularAssembly;
55
56 import java.util.EnumSet;
57 import java.util.logging.Level;
58 import java.util.logging.Logger;
59
60 import org.apache.commons.configuration2.CompositeConfiguration;
61
62
63
64
65
66
67
68 public class Minimize implements OptimizationListener, Terminatable {
69
70 private static final Logger logger = Logger.getLogger(Minimize.class.getName());
71
72
73
74
75 protected final MolecularAssembly molecularAssembly;
76
77
78
79 protected final Potential potential;
80
81
82
83 protected final AlgorithmListener algorithmListener;
84
85
86
87 protected final int n;
88
89
90
91 protected final double[] x;
92
93
94
95 protected final double[] grad;
96
97
98
99 protected final double[] scaling;
100
101
102
103 protected boolean done = false;
104
105
106
107 protected boolean terminate = false;
108
109
110
111 protected long time;
112
113
114
115 protected double energy;
116
117
118
119 protected int status;
120
121
122
123 protected int nSteps;
124
125
126
127 double rmsGradient;
128
129
130
131
132
133
134
135
136 public static final int DEFAULT_LBFGS_VECTORS = 7;
137
138
139
140
141
142
143
144
145 public Minimize(MolecularAssembly molecularAssembly, Potential potential,
146 AlgorithmListener algorithmListener) {
147 this.molecularAssembly = molecularAssembly;
148 this.algorithmListener = algorithmListener;
149 this.potential = potential;
150 n = potential.getNumberOfVariables();
151 x = new double[n];
152 grad = new double[n];
153 scaling = new double[n];
154 fill(scaling, 12.0);
155 }
156
157
158
159
160
161
162
163 public Minimize(MolecularAssembly molecularAssembly, AlgorithmListener algorithmListener) {
164 this.molecularAssembly = molecularAssembly;
165 this.algorithmListener = algorithmListener;
166 if (molecularAssembly.getPotentialEnergy() == null) {
167 molecularAssembly.setPotential(ForceFieldEnergy.energyFactory(molecularAssembly));
168 }
169 potential = molecularAssembly.getPotentialEnergy();
170 n = potential.getNumberOfVariables();
171 x = new double[n];
172 grad = new double[n];
173 scaling = new double[n];
174 fill(scaling, 12.0);
175 }
176
177 public static MinimizationEngine defaultEngine(MolecularAssembly molecularAssembly,
178 Potential potentialEnergy) {
179 CompositeConfiguration properties = molecularAssembly.getProperties();
180 String minimizeEngine = properties.getString("minimize-engine", null);
181 if (minimizeEngine != null) {
182 if (minimizeEngine.equalsIgnoreCase("OMM")) {
183 return MinimizationEngine.OPENMM;
184 } else {
185 return MinimizationEngine.FFX;
186 }
187 } else {
188 if (potentialEnergy instanceof OpenMMEnergy) {
189 return MinimizationEngine.OPENMM;
190 } else {
191 return MinimizationEngine.FFX;
192 }
193 }
194 }
195
196
197
198
199
200
201
202
203
204
205 public static Minimize minimizeFactory(MolecularAssembly assembly, Potential potentialEnergy,
206 AlgorithmListener listener, MinimizationEngine engine) {
207 return switch (engine) {
208 case OPENMM -> new MinimizeOpenMM(assembly, (OpenMMEnergy) potentialEnergy, listener);
209 default -> new Minimize(assembly, potentialEnergy, listener);
210 };
211 }
212
213
214
215
216
217
218 public double getEnergy() {
219 return energy;
220 }
221
222
223
224
225
226
227 public double getRMSGradient() {
228 return rmsGradient;
229 }
230
231
232
233
234
235
236 public int getStatus() {
237 return status;
238 }
239
240
241
242
243
244
245 public int getIterations() {
246 return nSteps;
247 }
248
249
250
251
252
253
254 public Potential minimize() {
255 return minimize(DEFAULT_LBFGS_VECTORS, 1.0, Integer.MAX_VALUE);
256 }
257
258
259
260
261
262
263
264 public Potential minimize(double eps) {
265 return minimize(DEFAULT_LBFGS_VECTORS, eps, Integer.MAX_VALUE);
266 }
267
268
269
270
271
272
273
274
275 public Potential minimize(double eps, int maxIterations) {
276 return minimize(DEFAULT_LBFGS_VECTORS, eps, maxIterations);
277 }
278
279
280
281
282
283
284
285
286
287 public Potential minimize(int m, double eps, int maxIterations) {
288 time = System.nanoTime();
289 potential.getCoordinates(x);
290 potential.setScaling(scaling);
291
292
293 for (int i = 0; i < n; i++) {
294 x[i] *= scaling[i];
295 }
296
297 done = false;
298 energy = potential.energyAndGradient(x, grad);
299
300 if (logger.isLoggable(Level.FINE)) {
301 logger.fine(format(" Minimize initial energy: %16.8f", energy));
302 }
303
304 status = LBFGS.minimize(n, m, x, energy, grad, eps, maxIterations, potential, this);
305 done = true;
306
307 switch (status) {
308 case 0 -> logger.info(format("\n Optimization achieved convergence criteria: %8.5f", rmsGradient));
309 case 1 -> logger.info(format("\n Optimization terminated at step %d.", nSteps));
310 default -> logger.warning("\n Optimization failed.");
311 }
312
313 potential.setScaling(null);
314 return potential;
315 }
316
317
318
319
320
321
322
323
324 @Override
325 public boolean optimizationUpdate(int iteration, int nBFGS, int functionEvaluations,
326 double rmsGradient, double rmsCoordinateChange, double energy, double energyChange,
327 double angle, LineSearch.LineSearchResult lineSearchResult) {
328 long currentTime = System.nanoTime();
329 Double seconds = (currentTime - time) * 1.0e-9;
330 time = currentTime;
331 this.rmsGradient = rmsGradient;
332 this.nSteps = iteration;
333 this.energy = energy;
334
335 if (iteration == 0) {
336 if (nBFGS > 0) {
337 logger.info("\n Limited Memory BFGS Quasi-Newton Optimization: \n");
338 } else {
339 logger.info("\n Steepest Decent Optimization: \n");
340 }
341 logger.info(" Cycle Energy G RMS Delta E Delta X Angle Evals Time\n");
342 }
343 if (lineSearchResult == null) {
344 logger.info(format("%6d%13.4f%11.4f", iteration, energy, rmsGradient));
345 } else {
346 if (lineSearchResult == LineSearch.LineSearchResult.Success) {
347 logger.info(
348 format("%6d%13.4f%11.4f%11.4f%10.4f%9.2f%7d %8.3f", iteration, energy, rmsGradient,
349 energyChange, rmsCoordinateChange, angle, functionEvaluations, seconds));
350 } else {
351 logger.info(format("%6d%13.4f%11.4f%11.4f%10.4f%9.2f%7d %8s", iteration, energy, rmsGradient,
352 energyChange, rmsCoordinateChange, angle, functionEvaluations, lineSearchResult));
353 }
354 }
355
356 if (algorithmListener != null) {
357 algorithmListener.algorithmUpdate(molecularAssembly);
358 }
359
360 if (terminate) {
361 logger.info(" The optimization received a termination request.");
362
363 return false;
364 }
365 return true;
366 }
367
368
369
370
371 @Override
372 public void terminate() {
373 terminate = true;
374 while (!done) {
375 synchronized (this) {
376 try {
377 wait(1);
378 } catch (Exception e) {
379 logger.log(Level.WARNING, "Exception terminating minimization.\n", e);
380 }
381 }
382 }
383 }
384
385
386
387
388
389
390
391
392 public enum MinimizationEngine {
393 FFX(true, true), OPENMM(false, true);
394
395
396
397 private final EnumSet<Platform> platforms = EnumSet.noneOf(Platform.class);
398
399
400
401
402
403
404
405 MinimizationEngine(boolean ffx, boolean openMM) {
406 if (ffx) {
407 platforms.add(Platform.FFX);
408 }
409 if (openMM) {
410 platforms.add(Platform.OMM);
411 platforms.add(Platform.OMM_REF);
412 platforms.add(Platform.OMM_CUDA);
413 platforms.add(Platform.OMM_OPENCL);
414 platforms.add(Platform.OMM_CPU);
415 }
416 }
417
418
419
420
421
422
423 public EnumSet<Platform> getSupportedPlatforms() {
424 return EnumSet.copyOf(platforms);
425 }
426
427
428
429
430
431
432
433 public boolean supportsPlatform(Platform platform) {
434 return platforms.contains(platform);
435 }
436 }
437 }