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