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.xray;
39
40 import static java.lang.Double.isNaN;
41 import static java.lang.String.format;
42 import static java.lang.System.arraycopy;
43 import static org.apache.commons.math3.util.FastMath.pow;
44 import static org.apache.commons.math3.util.FastMath.sqrt;
45
46 import edu.rit.pj.ParallelTeam;
47 import ffx.algorithms.Terminatable;
48 import ffx.crystal.Crystal;
49 import ffx.crystal.HKL;
50 import ffx.crystal.ReflectionList;
51 import ffx.crystal.ReflectionSpline;
52 import ffx.numerics.math.ComplexNumber;
53 import ffx.numerics.optimization.LBFGS;
54 import ffx.numerics.optimization.LineSearch.LineSearchResult;
55 import ffx.numerics.optimization.OptimizationListener;
56
57 import javax.annotation.Nullable;
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60
61
62
63
64
65
66
67 public class SigmaAMinimize implements OptimizationListener, Terminatable {
68
69 private static final Logger logger = Logger.getLogger(SigmaAMinimize.class.getName());
70 private static final double toSeconds = 1.0e-9;
71 protected final DiffractionRefinementData refinementData;
72 private final ReflectionList reflectionList;
73 private final Crystal crystal;
74 private final SigmaAEnergy sigmaAEnergy;
75 private final int n;
76 private final double[] x;
77 private final double[] grad;
78 private final double[] scaling;
79 private boolean done = false;
80 private boolean terminate = false;
81 private long time;
82 private double grms;
83 private int nSteps;
84
85
86
87
88
89
90
91
92 SigmaAMinimize(
93 ReflectionList reflectionList,
94 DiffractionRefinementData refinementData,
95 ParallelTeam parallelTeam) {
96 this.reflectionList = reflectionList;
97 this.refinementData = refinementData;
98 this.crystal = reflectionList.crystal;
99
100 n = refinementData.nBins * 2;
101 sigmaAEnergy = new SigmaAEnergy(reflectionList, refinementData, parallelTeam);
102 x = new double[n];
103 grad = new double[n];
104 scaling = new double[n];
105
106 for (int i = 0; i < refinementData.nBins; i++) {
107
108 x[i] = refinementData.sigmaA[i] - 1.0;
109 scaling[i] = 1.0;
110 x[i + refinementData.nBins] = refinementData.sigmaW[i];
111 scaling[i + refinementData.nBins] = 2.0;
112 }
113
114
115 int type = SplineEnergy.Type.FCTOESQ;
116 SplineMinimize splineMinimize =
117 new SplineMinimize(reflectionList, refinementData, refinementData.esqFc, type);
118 splineMinimize.minimize(7, 1.0);
119
120 type = SplineEnergy.Type.FOTOESQ;
121 splineMinimize = new SplineMinimize(reflectionList, refinementData, refinementData.esqFo, type);
122 splineMinimize.minimize(7, 1.0);
123
124 setWEstimate();
125 }
126
127
128
129
130
131
132 public double calculateLikelihoodFree() {
133 sigmaAEnergy.setScaling(scaling);
134 double energy = sigmaAEnergy.energyAndGradient(x, grad);
135 sigmaAEnergy.setScaling(null);
136 return energy;
137 }
138
139
140
141
142
143
144
145 public double[] getCoordinates(@Nullable double[] x) {
146 if (x == null) {
147 x = new double[this.x.length];
148 }
149 arraycopy(this.x, 0, x, 0, this.x.length);
150 return x;
151 }
152
153
154
155
156
157
158 public int getNumberOfVariables() {
159 return x.length;
160 }
161
162
163
164
165
166
167 public SigmaAEnergy minimize() {
168 return minimize(0.5);
169 }
170
171
172
173
174
175
176
177 public SigmaAEnergy minimize(double eps) {
178 return minimize(7, eps);
179 }
180
181
182
183
184
185
186
187
188 public SigmaAEnergy minimize(int m, double eps) {
189
190 sigmaAEnergy.setScaling(scaling);
191
192 double e = sigmaAEnergy.energyAndGradient(x, grad);
193
194 long mTime = -System.nanoTime();
195 time = -System.nanoTime();
196 done = false;
197 int status = LBFGS.minimize(n, m, x, e, grad, eps, sigmaAEnergy, this);
198 done = true;
199
200 switch (status) {
201 case 0:
202 logger.info(format("\n Optimization achieved convergence criteria: %8.5f\n", grms));
203 break;
204 case 1:
205 logger.info(format("\n Optimization terminated at step %d.\n", nSteps));
206 break;
207 default:
208 logger.warning("\n Optimization failed.\n");
209 }
210
211 for (int i = 0; i < refinementData.nBins; i++) {
212 refinementData.sigmaA[i] = 1.0 + x[i] / scaling[i];
213 int index = i + refinementData.nBins;
214 refinementData.sigmaW[i] = x[index] / scaling[index];
215 }
216
217 if (logger.isLoggable(Level.INFO)) {
218 StringBuilder sb = new StringBuilder();
219 mTime += System.nanoTime();
220 sb.append(format(" Optimization time: %8.3f (sec)\n", mTime * toSeconds));
221 logger.info(sb.toString());
222 }
223
224 sigmaAEnergy.setScaling(null);
225 return sigmaAEnergy;
226 }
227
228
229 @Override
230 public boolean optimizationUpdate(
231 int iter,
232 int nBFGS,
233 int nfun,
234 double grms,
235 double xrms,
236 double f,
237 double df,
238 double angle,
239 LineSearchResult info) {
240 long currentTime = System.nanoTime();
241 Double seconds = (currentTime - time) * 1.0e-9;
242 time = currentTime;
243 this.grms = grms;
244 this.nSteps = iter;
245
246 if (iter == 0) {
247 if (nBFGS > 0) {
248 logger.info("\n Limited Memory BFGS Quasi-Newton Optimization of SigmaA Parameters\n");
249 } else {
250 logger.info("\n Steepest Decent Optimization of SigmaA Parameters\n");
251 }
252 logger.info(" Cycle Energy G RMS Delta E Delta X Angle Evals Time");
253 }
254 if (info == null) {
255 logger.info(format("%6d %12.2f %10.2f", iter, f, grms));
256 } else {
257 if (info == LineSearchResult.Success) {
258 logger.info(format("%6d %12.2f %10.2f %10.5f %9.5f %8.2f %6d %8.3f",
259 iter, f, grms, df, xrms, angle, nfun, seconds));
260 } else {
261 logger.info(format("%6d %12.2f %10.2f %10.5f %9.5f %8.2f %6d %8s",
262 iter, f, grms, df, xrms, angle, nfun, info));
263 }
264 }
265 if (terminate) {
266 logger.info(" The optimization recieved a termination request.");
267
268 return false;
269 }
270 return true;
271 }
272
273
274 @Override
275 public void terminate() {
276 terminate = true;
277 while (!done) {
278 synchronized (this) {
279 try {
280 wait(1);
281 } catch (Exception e) {
282 logger.log(Level.WARNING, "Exception terminating minimization.\n", e);
283 }
284 }
285 }
286 }
287
288 private void setWEstimate() {
289
290 ReflectionSpline spline = new ReflectionSpline(reflectionList, refinementData.nBins);
291 int[] nMean = new int[refinementData.nBins];
292 for (int i = 0; i < refinementData.nBins; i++) {
293 nMean[i] = 0;
294 }
295 double mean = 0.0;
296 double tot = 0.0;
297 double[][] fcTot = refinementData.fcTot;
298 double[][] fSigF = refinementData.fSigF;
299 for (HKL ih : reflectionList.hklList) {
300 int i = ih.getIndex();
301 if (ih.getAllowed() == 0.0 || isNaN(fcTot[i][0]) || isNaN(fSigF[i][0])) {
302 continue;
303 }
304
305 double s2 = crystal.invressq(ih);
306 double epsc = ih.epsilonc();
307 ComplexNumber fct = new ComplexNumber(fcTot[i][0], fcTot[i][1]);
308 double ecscale = spline.f(s2, refinementData.esqFc);
309 double eoscale = spline.f(s2, refinementData.esqFo);
310 double ec = fct.times(sqrt(ecscale)).abs();
311 double eo = fSigF[i][0] * sqrt(eoscale);
312 double wi = pow(eo - ec, 2.0) / epsc;
313
314 nMean[spline.i1()]++;
315 tot++;
316
317 x[spline.i1() + refinementData.nBins] +=
318 (wi - x[spline.i1() + refinementData.nBins]) / nMean[spline.i1()];
319
320 mean += (wi - mean) / tot;
321 }
322 logger.info(format(" Starting mean w: %8.3f", mean));
323 double initialScale = 0.01;
324 if (mean > 0.0) {
325 initialScale = 1.0 / mean;
326 }
327 logger.info(format(" Starting w scaling: %8.3f", initialScale));
328 for (int i = 0; i < refinementData.nBins; i++) {
329 x[i] -= x[i + refinementData.nBins];
330 x[i] *= scaling[i];
331 scaling[i + refinementData.nBins] = initialScale;
332 x[i + refinementData.nBins] *= scaling[i + refinementData.nBins];
333 }
334 }
335
336
337
338
339
340
341 SigmaAEnergy getSigmaAEnergy() {
342 return sigmaAEnergy;
343 }
344
345
346
347
348
349
350 double calculateLikelihood() {
351 sigmaAEnergy.setScaling(scaling);
352 sigmaAEnergy.energyAndGradient(x, grad);
353 sigmaAEnergy.setScaling(null);
354
355 return refinementData.llkR;
356 }
357 }