1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.numerical.optimization;
17
18 import com.irurueta.algebra.AlgebraException;
19 import com.irurueta.algebra.Matrix;
20 import com.irurueta.numerical.EvaluationException;
21 import com.irurueta.numerical.GradientFunctionEvaluatorListener;
22 import com.irurueta.numerical.LockedException;
23 import com.irurueta.numerical.MultiDimensionFunctionEvaluatorListener;
24 import com.irurueta.numerical.NotAvailableException;
25 import com.irurueta.numerical.NotReadyException;
26
27
28
29
30
31
32
33 public class QuasiNewtonMultiOptimizer extends MultiOptimizer {
34
35
36
37 public static final int ITMAX = 200;
38
39
40
41
42 public static final double EPS = 1e-12;
43
44
45
46
47 public static final double TOLX = 4.0 * EPS;
48
49
50
51
52 public static final double STPMX = 100.0;
53 public static final double ALF = 1e-4;
54 public static final double TOLX2 = 1e-12;
55
56
57
58
59
60 public static final double DEFAULT_TOLERANCE = 3e-8;
61
62
63
64
65 public static final double MIN_TOLERANCE = 0.0;
66
67
68
69
70 private double[] p;
71
72
73
74
75
76 private double tolerance;
77
78
79
80
81
82 private int iter;
83
84
85
86
87
88
89
90 GradientFunctionEvaluatorListener gradientListener;
91
92
93
94
95 double fret;
96
97
98
99
100 public QuasiNewtonMultiOptimizer() {
101 super();
102 tolerance = DEFAULT_TOLERANCE;
103 p = null;
104 iter = 0;
105 gradientListener = null;
106 }
107
108
109
110
111
112
113
114
115
116
117
118 public QuasiNewtonMultiOptimizer(
119 final MultiDimensionFunctionEvaluatorListener listener,
120 final GradientFunctionEvaluatorListener gradientListener, final double tolerance) {
121 super(listener);
122 internalSetTolerance(tolerance);
123 p = null;
124 iter = 0;
125 this.gradientListener = gradientListener;
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 public QuasiNewtonMultiOptimizer(
144 final MultiDimensionFunctionEvaluatorListener listener,
145 final GradientFunctionEvaluatorListener gradientListener, final double[] startPoint,
146 final double tolerance) {
147 super(listener);
148 internalSetTolerance(tolerance);
149 p = startPoint;
150 iter = 0;
151 this.gradientListener = gradientListener;
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175 @Override
176 public void minimize() throws LockedException, NotReadyException, OptimizationException {
177
178 if (isLocked()) {
179 throw new LockedException();
180 }
181 if (!isReady()) {
182 throw new NotReadyException();
183 }
184
185 locked = true;
186
187 var validResult = false;
188 final var n = p.length;
189
190 try {
191 double den;
192 double fac;
193 double fad;
194 double fae;
195 double fp;
196 final double stpmax;
197 var sum = 0.0;
198 double sumdg;
199 double sumxi;
200 double temp;
201 double test;
202 final var dg = new double[n];
203 final var g = new double[n];
204 final var hdg = new double[n];
205 final var pnew = new double[n];
206 final var xi = new double[n];
207 final var fretArray = new double[1];
208 final var check = new boolean[1];
209
210 final var hessin = new Matrix(n, n);
211 fp = listener.evaluate(p);
212 gradientListener.evaluateGradient(p, g);
213
214 for (var i = 0; i < n; i++) {
215 for (var j = 0; j < n; j++) {
216 hessin.setElementAt(i, j, 0.0);
217 }
218 hessin.setElementAt(i, i, 1.0);
219 xi[i] = -g[i];
220 sum += p[i] * p[i];
221 }
222 stpmax = STPMX * Math.max(Math.sqrt(sum), n);
223 for (var its = 0; its < ITMAX; its++) {
224 iter = its;
225 lnsrch(p, fp, g, xi, pnew, fretArray, stpmax, check);
226 fret = fretArray[0];
227 fp = fret;
228 for (var i = 0; i < n; i++) {
229 xi[i] = pnew[i] - p[i];
230 p[i] = pnew[i];
231 }
232 test = 0.0;
233 for (var i = 0; i < n; i++) {
234 temp = Math.abs(xi[i]) / Math.max(Math.abs(p[i]), 1.0);
235 if (temp > test) {
236 test = temp;
237 }
238 }
239 if (test < TOLX) {
240
241 validResult = true;
242 break;
243 }
244
245 System.arraycopy(g, 0, dg, 0, n);
246
247 gradientListener.evaluateGradient(p, g);
248
249 test = 0.0;
250 den = Math.max(Math.abs(fret), 1.0);
251
252 for (var i = 0; i < n; i++) {
253 temp = Math.abs(g[i]) * Math.max(Math.abs(p[i]), 1.0) / den;
254 if (temp > test) {
255 test = temp;
256 }
257 }
258 if (test < tolerance) {
259
260 validResult = true;
261 break;
262 }
263
264 for (var i = 0; i < n; i++) {
265 dg[i] = g[i] - dg[i];
266 }
267
268 for (var i = 0; i < n; i++) {
269 hdg[i] = 0.0;
270 for (var j = 0; j < n; j++) {
271 hdg[i] += hessin.getElementAt(i, j) * dg[j];
272 }
273 }
274 fac = fae = sumdg = sumxi = 0.0;
275 for (var i = 0; i < n; i++) {
276 fac += dg[i] * xi[i];
277 fae += dg[i] * hdg[i];
278 sumdg += sqr(dg[i]);
279 sumxi += sqr(xi[i]);
280 }
281
282 if (fac > Math.sqrt(EPS * sumdg * sumxi)) {
283 fac = 1.0 / fac;
284 fad = 1.0 / fae;
285 for (var i = 0; i < n; i++) {
286 dg[i] = fac * xi[i] - fad * hdg[i];
287 }
288
289 for (var i = 0; i < n; i++) {
290 for (var j = i; j < n; j++) {
291 hessin.setElementAt(i, j, hessin.getElementAt(i, j) + fac * xi[i] * xi[j]
292 - fad * hdg[i] * hdg[j] + fae * dg[i] * dg[j]);
293 hessin.setElementAt(j, i, hessin.getElementAt(i, j));
294 }
295 }
296 }
297 for (var i = 0; i < n; i++) {
298 xi[i] = 0.0;
299 for (var j = 0; j < n; j++) {
300 xi[i] -= hessin.getElementAt(i, j) * g[j];
301 }
302 }
303
304 if (iterationCompletedListener != null) {
305 iterationCompletedListener.onIterationCompleted(this, its, ITMAX);
306 }
307 }
308
309 if (!validResult) {
310
311 locked = false;
312 throw new OptimizationException();
313 }
314 } catch (final AlgebraException | EvaluationException e) {
315 throw new OptimizationException(e);
316 } finally {
317 locked = false;
318 }
319
320
321 xmin = p;
322 resultAvailable = true;
323 fmin = fret;
324 }
325
326
327
328
329
330
331
332
333
334 @Override
335 public boolean isReady() {
336 return isListenerAvailable() && isGradientListenerAvailable() && isStartPointAvailable();
337 }
338
339
340
341
342
343
344 public double getTolerance() {
345 return tolerance;
346 }
347
348
349
350
351
352
353
354
355
356
357
358 private void internalSetTolerance(final double tolerance) {
359 if (tolerance < MIN_TOLERANCE) {
360 throw new IllegalArgumentException();
361 }
362 this.tolerance = tolerance;
363 }
364
365
366
367
368
369
370
371
372
373
374 public void setTolerance(final double tolerance) throws LockedException {
375 if (isLocked()) {
376 throw new LockedException();
377 }
378 internalSetTolerance(tolerance);
379 }
380
381
382
383
384
385
386
387 public boolean isStartPointAvailable() {
388 return p != null;
389 }
390
391
392
393
394
395
396
397
398 public void setStartPoint(final double[] startPoint) throws LockedException {
399 if (isLocked()) {
400 throw new LockedException();
401 }
402 p = startPoint;
403 }
404
405
406
407
408
409
410
411
412
413 public double[] getStartPoint() throws NotAvailableException {
414 if (!isStartPointAvailable()) {
415 throw new NotAvailableException();
416 }
417 return p;
418 }
419
420
421
422
423
424
425
426
427
428 public GradientFunctionEvaluatorListener getGradientListener() throws NotAvailableException {
429 if (!isGradientListenerAvailable()) {
430 throw new NotAvailableException();
431 }
432 return gradientListener;
433 }
434
435
436
437
438
439
440
441
442 public void setGradientListener(final GradientFunctionEvaluatorListener gradientListener) throws LockedException {
443 if (isLocked()) {
444 throw new LockedException();
445 }
446 this.gradientListener = gradientListener;
447 }
448
449
450
451
452
453
454
455 public boolean isGradientListenerAvailable() {
456 return gradientListener != null;
457 }
458
459
460
461
462
463
464 public int getIterations() {
465 return iter;
466 }
467
468
469
470
471
472
473
474 private double sqr(final double x) {
475 return x * x;
476 }
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492 private void lnsrch(final double[] xold, final double fold, final double[] g, final double[] p, final double[] x,
493 final double[] f, final double stpmax, final boolean[] check) throws OptimizationException {
494 double a;
495 double alam;
496 var alam2 = 0.0;
497 final double alamin;
498 double b;
499 double disc;
500 var f2 = 0.0;
501 double rhs1;
502 double rhs2;
503 var slope = 0.0;
504 var sum = 0.0;
505 double temp;
506 double test;
507 double tmplam;
508 int i;
509 final var n = xold.length;
510 check[0] = false;
511
512 for (i = 0; i < n; i++) {
513 sum += p[i] * p[i];
514 }
515 sum = Math.sqrt(sum);
516
517 if (sum > stpmax) {
518 for (i = 0; i < n; i++) {
519 p[i] *= stpmax / sum;
520 }
521 }
522 for (i = 0; i < n; i++) {
523 slope += g[i] * p[i];
524 }
525 if (slope >= 0.0) {
526 throw new OptimizationException();
527 }
528 test = 0.0;
529 for (i = 0; i < n; i++) {
530 temp = Math.abs(p[i]) / Math.max(Math.abs(xold[i]), 1.0);
531 if (temp > test) {
532 test = temp;
533 }
534 }
535 alamin = TOLX2 / test;
536 alam = 1.0;
537 for (; ; ) {
538 for (i = 0; i < n; i++) {
539 x[i] = xold[i] + alam * p[i];
540 }
541 try {
542 f[0] = listener.evaluate(x);
543 } catch (final EvaluationException e) {
544 throw new OptimizationException(e);
545 }
546
547 if (alam < alamin) {
548 System.arraycopy(xold, 0, x, 0, n);
549 check[0] = true;
550 return;
551 } else if (f[0] <= fold + ALF * alam * slope) {
552 return;
553 } else {
554 if (alam == 1.0) {
555 tmplam = -slope / (2.0 * (f[0] - fold - slope));
556 } else {
557 rhs1 = f[0] - fold - alam * slope;
558 rhs2 = f2 - fold - alam2 * slope;
559 a = (rhs1 / (alam * alam) - rhs2 / (alam2 * alam2)) / (alam - alam2);
560 b = (-alam2 * rhs1 / (alam * alam) + alam * rhs2 / (alam2 * alam2)) / (alam - alam2);
561 if (a == 0.0) {
562 tmplam = -slope / (2.0 * b);
563 } else {
564 disc = b * b - 3.0 * a * slope;
565 if (disc < 0.0) {
566 tmplam = 0.5 * alam;
567 } else if (b <= 0.0) {
568 tmplam = (-b + Math.sqrt(disc)) / (3.0 * a);
569 } else {
570 tmplam = -slope / (b + Math.sqrt(disc));
571 }
572 }
573 if (tmplam > 0.5 * alam) {
574 tmplam = 0.5 * alam;
575 }
576 }
577 }
578 alam2 = alam;
579 f2 = f[0];
580 alam = Math.max(tmplam, 0.1 * alam);
581 }
582 }
583 }