View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.xray;
39  
40  import ffx.algorithms.Terminatable;
41  import ffx.crystal.ReflectionList;
42  import ffx.numerics.optimization.LBFGS;
43  import ffx.numerics.optimization.LineSearch.LineSearchResult;
44  import ffx.numerics.optimization.OptimizationListener;
45  import ffx.xray.SplineEnergy.SplineType;
46  
47  import javax.annotation.Nullable;
48  import java.util.logging.Level;
49  import java.util.logging.Logger;
50  
51  import static ffx.utilities.Constants.NS2SEC;
52  import static java.lang.String.format;
53  import static java.lang.System.arraycopy;
54  
55  /**
56   * SplineMinimize class.
57   *
58   * @author Timothy D. Fenn
59   * @since 1.0
60   */
61  public class SplineMinimize implements OptimizationListener, Terminatable {
62  
63    private static final Logger logger = Logger.getLogger(SplineEnergy.class.getName());
64  
65    private final SplineEnergy splineEnergy;
66    private final SplineType splineType;
67    private final int n;
68    private final double[] x;
69    private final double[] grad;
70    private final double[] scaling;
71    private boolean done = false;
72    private boolean terminate = false;
73    private long time;
74    private double grms;
75    private int nSteps;
76  
77    /**
78     * Constructor for SplineMinimize.
79     *
80     * @param reflectionList a {@link ffx.crystal.ReflectionList} object.
81     * @param refinementData a {@link ffx.xray.DiffractionRefinementData} object.
82     * @param x              an array of double.
83     * @param type           the SplineType.
84     */
85    public SplineMinimize(ReflectionList reflectionList,
86                          DiffractionRefinementData refinementData, double[] x, SplineType type) {
87      this.x = x;
88      this.splineType = type;
89      n = x.length;
90      splineEnergy = new SplineEnergy(reflectionList, refinementData, n, type);
91      grad = new double[n];
92      scaling = new double[n];
93      for (int i = 0; i < n; i++) {
94        if (type == SplineType.FOTOESQ || type == SplineType.FCTOESQ) {
95          x[i] = 0.1;
96        } else {
97          x[i] = 1.0;
98        }
99        scaling[i] = 1.0;
100     }
101   }
102 
103   /**
104    * getCoordinates.
105    *
106    * @param x the array to populate with parameters or null to create a new array.
107    * @return an array containing the parameters.
108    */
109   public double[] getCoordinates(@Nullable double[] x) {
110     if (x == null) {
111       x = new double[this.x.length];
112     }
113     arraycopy(this.x, 0, x, 0, this.x.length);
114     return x;
115   }
116 
117   /**
118    * getNumberOfVariables.
119    *
120    * @return a int.
121    */
122   public int getNumberOfVariables() {
123     return x.length;
124   }
125 
126   /**
127    * minimize
128    *
129    * @param eps a double.
130    * @return a {@link ffx.xray.SplineEnergy} object.
131    */
132   public SplineEnergy minimize(double eps) {
133     return minimize(7, eps);
134   }
135 
136   /**
137    * Minimize
138    *
139    * @param m   a int.
140    * @param eps a double.
141    * @return a {@link ffx.xray.SplineEnergy} object.
142    */
143   public SplineEnergy minimize(int m, double eps) {
144 
145     splineEnergy.setScaling(scaling);
146 
147     double e = splineEnergy.energyAndGradient(x, grad);
148 
149     done = false;
150     time = -System.nanoTime();
151     int status = LBFGS.minimize(n, m, x, e, grad, eps, splineEnergy, this);
152     done = true;
153     switch (status) {
154       case 0:
155         logger.fine(format("\n Optimization achieved convergence criteria: %8.5f\n", grms));
156         break;
157       case 1:
158         logger.fine(format("\n Optimization terminated at step %d.\n", nSteps));
159         break;
160       default:
161         logger.warning("\n Spline Optimization failed.\n");
162     }
163 
164     splineEnergy.setScaling(null);
165 
166     return splineEnergy;
167   }
168 
169   /**
170    * {@inheritDoc}
171    */
172   @Override
173   public boolean optimizationUpdate(int iter, int nBFGS, int nfun, double grms, double xrms, double f,
174                                     double df, double angle, LineSearchResult info) {
175 
176     long currentTime = System.nanoTime();
177     Double seconds = (currentTime - time) * NS2SEC;
178     time = currentTime;
179     this.grms = grms;
180     this.nSteps = iter;
181 
182     Level level = Level.FINE;
183 
184     if (logger.isLoggable(level)) {
185       if (iter == 0) {
186         String name = splineType.toString();
187         if (nBFGS > 0) {
188           logger.log(level, format("\n Limited Memory BFGS Quasi-Newton Optimization of %s", name));
189         } else {
190           logger.log(level, format("\n Steepest Decent Optimization of %s", name));
191         }
192         logger.log(level, format(" Number of Parameters: %d\n", n));
193         logger.log(level," Cycle       Energy      G RMS    Delta E   Delta X    Angle  Evals     Time");
194       }
195       if (info == null) {
196         logger.log(level, format("%6d %12.2e %10.2e", iter, f, grms));
197       } else {
198         if (info == LineSearchResult.Success) {
199           logger.log(level, format("%6d %12.2e %10.2e %10.5e %9.5f %8.2f %6d %8.3f",
200               iter, f, grms, df, xrms, angle, nfun, seconds));
201         } else {
202           logger.log(level, format("%6d %12.2e %10.2e %10.5e %9.5f %8.2f %6d %8s",
203               iter, f, grms, df, xrms, angle, nfun, info));
204         }
205       }
206     }
207 
208     if (terminate) {
209       logger.info(" The optimization received a termination request.");
210       // Tell the L-BFGS optimizer to terminate.
211       return false;
212     }
213     return true;
214   }
215 
216   /**
217    * {@inheritDoc}
218    */
219   @Override
220   public void terminate() {
221     terminate = true;
222     while (!done) {
223       synchronized (this) {
224         try {
225           wait(1);
226         } catch (Exception e) {
227           logger.log(Level.WARNING, "Exception terminating minimization.\n", e);
228         }
229       }
230     }
231   }
232 
233   /**
234    * Getter for the field <code>splineEnergy</code>.
235    *
236    * @return a {@link ffx.xray.SplineEnergy} object.
237    */
238   SplineEnergy getSplineEnergy() {
239     return splineEnergy;
240   }
241 }