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.numerics.estimator;
39
40 import ffx.numerics.math.RunningStatistics;
41 import ffx.numerics.math.SummaryStatistics;
42
43 import java.util.Random;
44 import java.util.concurrent.ThreadLocalRandom;
45 import java.util.logging.Logger;
46
47 import static java.lang.String.format;
48 import static java.util.Arrays.stream;
49 import static org.apache.commons.math3.util.FastMath.sqrt;
50
51
52
53
54 public class EstimateBootstrapper {
55
56 private static final Logger logger = Logger.getLogger(EstimateBootstrapper.class.getName());
57 private static final int DEFAULT_LOG_INTERVAL = 100;
58
59 private final BootstrappableEstimator estimate;
60 private final int nWindows;
61 private final SummaryStatistics[] freeEnergyDifferenceResults;
62 private final SummaryStatistics[] enthalpyResults;
63
64
65
66
67
68
69 public EstimateBootstrapper(BootstrappableEstimator estimator) {
70 this.estimate = estimator;
71 nWindows = estimate.getNumberOfBins();
72 freeEnergyDifferenceResults = new SummaryStatistics[nWindows];
73 enthalpyResults = new SummaryStatistics[nWindows];
74 }
75
76
77
78
79
80
81
82 public static int[] getBootstrapIndices(int length) {
83 return getBootstrapIndices(length, ThreadLocalRandom.current());
84 }
85
86
87
88
89
90
91
92
93 public static int[] getBootstrapIndices(int length, Random random) {
94 return getBootstrapIndices(length, random, Math.min(2, length));
95 }
96
97
98
99
100
101
102
103
104
105 public static int[] getBootstrapIndices(int length, Random random, int minDistinct) {
106
107 switch (length) {
108 case 0 -> {
109 return new int[0];
110 }
111 case 1 -> {
112 return new int[]{0};
113 }
114 case 2 -> {
115 int[] indices = new int[2];
116 indices[0] = random.nextBoolean() ? 0 : 1;
117 indices[1] = random.nextBoolean() ? 0 : 1;
118 return indices;
119 }
120
121 }
122
123
124 int[] indices = random.ints(length, 0, length).toArray();
125 long distinctVal = stream(indices).distinct().count();
126 int ctr = 0;
127 while (distinctVal <= minDistinct) {
128 logger.info(
129 format(" Regenerating array (iteration %d): only %d distinct values found for length %d.",
130 ++ctr, distinctVal, length));
131 indices = random.ints(length, 0, length).toArray();
132 distinctVal = stream(indices).distinct().count();
133 }
134 return indices;
135 }
136
137
138
139
140
141
142 public SummaryStatistics[] getEnthalpyResults() {
143 return enthalpyResults;
144 }
145
146
147
148
149
150
151 public void bootstrap(long trials) {
152 bootstrap(trials, DEFAULT_LOG_INTERVAL);
153 }
154
155
156
157
158
159
160
161 public void bootstrap(long trials, long logInterval) {
162 RunningStatistics[] windows = new RunningStatistics[nWindows];
163 RunningStatistics[] enthalpyWindows = new RunningStatistics[nWindows];
164 for (int i = 0; i < nWindows; i++) {
165 windows[i] = new RunningStatistics();
166 enthalpyWindows[i] = new RunningStatistics();
167 }
168
169 for (long i = 0; i < trials; i++) {
170 if ((i + 1) % logInterval == 0) {
171 logger.fine(format(" Bootstrap Trial %d", i + 1));
172 }
173
174 estimate.estimateDG(true);
175
176 double[] fe = estimate.getFreeEnergyDifferences();
177 double[] enthalpy = estimate.getEnthalpyDifferences();
178 for (int j = 0; j < nWindows; j++) {
179 windows[j].addValue(fe[j]);
180 enthalpyWindows[j].addValue(enthalpy[j]);
181 }
182 }
183
184 for (int i = 0; i < nWindows; i++) {
185 freeEnergyDifferenceResults[i] = new SummaryStatistics(windows[i]);
186 enthalpyResults[i] = new SummaryStatistics(enthalpyWindows[i]);
187 }
188 }
189
190
191
192
193
194
195 public double getTotalFreeEnergyDifference() {
196 return getTotalFreeEnergyDifference(getFreeEnergyDifferences());
197 }
198
199
200
201
202
203
204 public double getTotalEnthalpyChange() {
205 return getTotalEnthalpyChange(getEnthalpyChanges());
206 }
207
208
209
210
211
212
213 public double getTotalEntropyChange() {
214
215 double dG = getTotalFreeEnergyDifference();
216 double dH = getTotalEnthalpyChange();
217 return dG - dH;
218 }
219
220
221
222
223
224
225
226
227 public double[] getFreeEnergyDifferences() {
228 return stream(freeEnergyDifferenceResults).mapToDouble(SummaryStatistics::getMean).toArray();
229 }
230
231
232
233
234
235
236 public double[] getEnthalpyChanges() {
237 return stream(enthalpyResults).mapToDouble(SummaryStatistics::getMean).toArray();
238 }
239
240
241
242
243
244
245 public double[] getEntropyChanges() {
246 double[] dG = getFreeEnergyDifferences();
247 double[] dH = getEnthalpyChanges();
248 double[] dS = new double[nWindows];
249 for (int i = 0; i < nWindows; i++) {
250 dS[i] = dG[i] - dH[i];
251 }
252 return dS;
253 }
254
255
256
257
258
259
260 public double getTotalFEDifferenceUncertainty() {
261 return getTotalFEDifferenceUncertainty(getFEDifferenceVariances());
262 }
263
264
265
266
267
268
269 public double getTotalEnthalpyUncertainty() {
270 return getTotalEnthalpyUncertainty(getEnthalpyVariances());
271 }
272
273
274
275
276
277
278
279 public double getTotalEntropyUncertainty() {
280 double dG = getTotalFEDifferenceUncertainty();
281 double dH = getTotalEnthalpyUncertainty();
282 return sqrt(dG * dG + dH * dH);
283 }
284
285
286
287
288
289
290
291 public double[] getFEDifferenceStdDevs() {
292 return stream(freeEnergyDifferenceResults).mapToDouble(SummaryStatistics::getSd).toArray();
293 }
294
295
296
297
298
299
300 public double[] getEnthalpyStdDevs() {
301 return stream(enthalpyResults).mapToDouble(SummaryStatistics::getSd).toArray();
302 }
303
304
305
306
307
308
309
310
311 public double[] getEntropyStdDevs() {
312 double[] dG = getFEDifferenceStdDevs();
313 double[] dH = getEnthalpyStdDevs();
314 double[] dS = new double[nWindows];
315 for (int i = 0; i < nWindows; i++) {
316 dS[i] = sqrt(dG[i] * dG[i] + dH[i] * dH[i]);
317 }
318 return dS;
319 }
320
321
322
323
324
325
326
327 public double getTotalFreeEnergyDifference(double[] freeEnergyDifferences) {
328 return estimate.getTotalFreeEnergyDifference(freeEnergyDifferences);
329 }
330
331
332
333
334
335
336 public double[] getFEDifferenceVariances() {
337 return stream(freeEnergyDifferenceResults).mapToDouble(SummaryStatistics::getVar).toArray();
338 }
339
340
341
342
343
344
345
346 public double getTotalFEDifferenceUncertainty(double[] variances) {
347 return estimate.getTotalFEDifferenceUncertainty(variances);
348 }
349
350
351
352
353
354
355
356 public double getTotalEnthalpyChange(double[] enthalpyChanges) {
357 return estimate.getTotalFreeEnergyDifference(enthalpyChanges);
358 }
359
360
361
362
363
364
365
366 public double getTotalEnthalpyUncertainty(double[] variances) {
367 return estimate.getTotalEnthalpyUncertainty(variances);
368 }
369
370
371
372
373
374
375 public double[] getEnthalpyVariances() {
376 return stream(enthalpyResults).mapToDouble(SummaryStatistics::getVar).toArray();
377 }
378 }