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