1 /*
2 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.numerical.optimization;
17
18 import com.irurueta.numerical.EvaluationException;
19 import com.irurueta.numerical.InvalidBracketRangeException;
20 import com.irurueta.numerical.LockedException;
21 import com.irurueta.numerical.NotAvailableException;
22 import com.irurueta.numerical.NotReadyException;
23 import com.irurueta.numerical.SingleDimensionFunctionEvaluatorListener;
24
25 /**
26 * This class searches for brackets of values containing a minimum in a single
27 * dimension function.
28 * A bracket is a set of points: "a" a minimum evaluation point,
29 * "b" a middle evaluation point and "c" a maximum evaluation where a <= b
30 * <= c, and where f(b) <= f(a) and f(b) <= f(c).
31 * This class uses a downhill algorithm that is better suited to continuous
32 * functions. Other functions might not obtain reliable results when using this
33 * algorithm to obtain a bracket of points.
34 * Some subclasses of this class will implement algorithms to refine the
35 * solution obtained in a bracket in order to find an accurate estimation of a
36 * minimum.
37 * Some algorithms might not need to previously compute a bracket and will
38 * simply search for a minimum in all the range of possible values, whereas
39 * other algorithms will require first the computation of a bracket.
40 * In either case, computing a bracket prior estimating a minimum will always
41 * ensure that a more reliable solution will be found.
42 * Besides, bracket computation is required when a function contains several
43 * minima and search of an accurate minimum estimation is desired to be
44 * restricted to a certain range of values.
45 */
46 public abstract class BracketedSingleOptimizer extends SingleOptimizer {
47
48 /**
49 * The default ratio by which intervals are magnified and.
50 */
51 public static final double GOLD = 1.618034;
52
53 /**
54 * The maximum magnification allowed for a parabolic-fit step.
55 */
56 public static final double GLIMIT = 100.0;
57
58 /**
59 * Small value representing machine precision.
60 */
61 public static final double TINY = 1e-20;
62
63 /**
64 * Default minimum evaluation point where the bracket is supposed to start
65 * By default, if no bracket is computed, the whole range of values is used
66 * for minimum estimation.
67 */
68 public static final double DEFAULT_MIN_EVAL_POINT = -Double.MAX_VALUE;
69
70 /**
71 * Default middle evaluation point where the bracket is supposed to start
72 * By default, if no bracket is computed, the whole range of values is used
73 * for minimum estimation.
74 */
75 public static final double DEFAULT_MIDDLE_EVAL_POINT = 0.0;
76
77 /**
78 * Default maximum evaluation point where the bracket is supposed to start
79 * By default, if no bracket is computed, the whole range of values is used
80 * for minimum estimation.
81 */
82 public static final double DEFAULT_MAX_EVAL_POINT = Double.MAX_VALUE;
83
84 /**
85 * Minimum evaluation point inside the bracket.
86 */
87 protected double ax;
88
89 /**
90 * Middle evaluation point inside the bracket.
91 */
92 protected double bx;
93
94 /**
95 * Maximum evaluation point inside the bracket.
96 */
97 protected double cx;
98
99 /**
100 * Boolean indicating whether a bracket has been provided or computed.
101 */
102 private boolean bracketAvailable;
103
104 /**
105 * Function evaluation value at minimum evaluation point inside the bracket.
106 */
107 private double fa;
108
109 /**
110 * Function evaluation value at middle evaluation point inside the bracket.
111 */
112 private double fb;
113
114 /**
115 * Function evaluation value at maximum evaluation point inside the bracket.
116 */
117 private double fc;
118
119 /**
120 * Boolean indicating whether function evaluation at bracket limits and
121 * middle point are available or not.
122 */
123 private boolean bracketEvaluationAvailable;
124
125 /**
126 * Constructor. Creates an instance with provided bracket of values.
127 *
128 * @param minEvalPoint Minimum bracket evaluation point.
129 * @param middleEvalPoint Middle bracket evaluation point.
130 * @param maxEvalPoint Maximum bracket evaluation point.
131 * @throws InvalidBracketRangeException Raised if the following condition is
132 * not met: minEvalPoint <= middleEvalPoint <= maxEvalPoint.
133 */
134 protected BracketedSingleOptimizer(
135 final double minEvalPoint, final double middleEvalPoint, final double maxEvalPoint)
136 throws InvalidBracketRangeException {
137 internalSetBracket(minEvalPoint, middleEvalPoint, maxEvalPoint);
138 }
139
140 /**
141 * Empty Constructor. Creates an instance using default bracket values.
142 */
143 protected BracketedSingleOptimizer() {
144 ax = DEFAULT_MIN_EVAL_POINT;
145 bx = DEFAULT_MIDDLE_EVAL_POINT;
146 cx = DEFAULT_MAX_EVAL_POINT;
147 bracketAvailable = true;
148 }
149
150 /**
151 * Constructor. Creates an instance with provided bracket of values and a
152 * listener to get single dimension function evaluations.
153 *
154 * @param listener Listener to evaluate a function.
155 * @param minEvalPoint Minimum bracket evaluation point.
156 * @param middleEvalPoint Middle bracket evaluation point.
157 * @param maxEvalPoint Maximum bracket evaluation point.
158 * @throws InvalidBracketRangeException Raised if the following condition is
159 * not met: minEvalPoint <= middleEvalPoint <= maxEvalPoint.
160 */
161 protected BracketedSingleOptimizer(
162 final SingleDimensionFunctionEvaluatorListener listener, final double minEvalPoint,
163 final double middleEvalPoint, final double maxEvalPoint) throws InvalidBracketRangeException {
164 super(listener);
165 internalSetBracket(minEvalPoint, middleEvalPoint, maxEvalPoint);
166 }
167
168 /**
169 * Sets a bracket of values to later search for a minimum. A local minimum
170 * will only be search within the minimum and maximum evaluation points of
171 * a given bracket.
172 * If bracket is not provided, it can also be computed from a default or
173 * coarse set of points in order to obtain a more refined bracket so that
174 * a minimum search can be estimated more precisely.
175 *
176 * @param minEvalPoint Minimum bracket evaluation point.
177 * @param middleEvalPoint Middle bracket evaluation point.
178 * @param maxEvalPoint Maximum bracket evaluation point.
179 * @throws InvalidBracketRangeException Raised if the following condition is
180 * not met: minEvalPoint <= middleEvalPoint <= maxEvalPoint.
181 * @throws LockedException Raised if this instance is locked. This instance
182 * will be locked while doing some operations. Attempting to change any
183 * parameter while being locked will raise this exception.
184 */
185 public void setBracket(
186 final double minEvalPoint, final double middleEvalPoint, final double maxEvalPoint) throws LockedException,
187 InvalidBracketRangeException {
188
189 if (isLocked()) {
190 throw new LockedException();
191 }
192 internalSetBracket(minEvalPoint, middleEvalPoint, maxEvalPoint);
193 }
194
195 /**
196 * Returns boolean indicating whether a bracket has been provided or
197 * computed and is available for retrieval.
198 *
199 * @return true if a bracket has been provided, false otherwise.
200 */
201 public boolean isBracketAvailable() {
202 return bracketAvailable;
203 }
204
205 /**
206 * Returns minimum evaluation point where the bracket starts
207 *
208 * @return Minimum evaluation point.
209 * @throws NotAvailableException Raised if not provided or computed.
210 */
211 public double getMinEvaluationPoint() throws NotAvailableException {
212 if (!isBracketAvailable()) {
213 throw new NotAvailableException();
214 }
215
216 return ax;
217 }
218
219 /**
220 * Returns middle evaluation point within the bracket.
221 *
222 * @return Middle evaluation point.
223 * @throws NotAvailableException Raised if not provided or computed.
224 */
225 public double getMiddleEvaluationPoint() throws NotAvailableException {
226 if (!isBracketAvailable()) {
227 throw new NotAvailableException();
228 }
229
230 return bx;
231 }
232
233 /**
234 * Returns maximum evaluation point whether the bracket finishes.
235 *
236 * @return Maximum evaluation point.
237 * @throws NotAvailableException Raised if not provided or computed.
238 */
239 public double getMaxEvaluationPoint() throws NotAvailableException {
240 if (!isBracketAvailable()) {
241 throw new NotAvailableException();
242 }
243
244 return cx;
245 }
246
247 /**
248 * Returns single dimension function evaluation at provided or computed
249 * minimum evaluation point where the bracket starts.
250 *
251 * @return Function evaluation at bracket's minimum evaluation point.
252 * @throws NotAvailableException Raised if bracket evaluations are not
253 * available.
254 */
255 public double getEvaluationAtMin() throws NotAvailableException {
256 if (!areBracketEvaluationsAvailable()) {
257 throw new NotAvailableException();
258 }
259
260 return fa;
261 }
262
263 /**
264 * Returns single dimension function evaluation at provided or computed
265 * middle evaluation point within the bracket.
266 *
267 * @return Function evaluation at bracket's middle evaluation point.
268 * @throws NotAvailableException Raised if bracket evaluations are not
269 * available.
270 */
271 public double getEvaluationAtMiddle() throws NotAvailableException {
272 if (!areBracketEvaluationsAvailable()) {
273 throw new NotAvailableException();
274 }
275
276 return fb;
277 }
278
279 /**
280 * Returns single dimension function evaluation at provided or computed
281 * maximum evaluation point where the bracket finishes.
282 *
283 * @return Function evaluation at bracket's maximum evaluation point.
284 * @throws NotAvailableException Raised if bracket evaluations are not
285 * available.
286 */
287 public double getEvaluationAtMax() throws NotAvailableException {
288 if (!areBracketEvaluationsAvailable()) {
289 throw new NotAvailableException();
290 }
291
292 return fc;
293 }
294
295 /**
296 * Computes a bracket of values using provided values as a starting point.
297 * Given a function f, and given distinct initial points ax and bx, this
298 * routine searches in the downhill direction (defined by the function as
299 * evaluated at the initial points) and returns.
300 * ax (minimum evaluation point), bx (middle evaluation point), cx (maximum
301 * evaluation point) that bracket a minimum of the function. Also returned
302 * are the function values at the three points fa, fb, and fc, which are the
303 * function evaluations at minimum, middle and maximum bracket points.
304 *
305 * @param minEvalPoint Initial minimum evaluation point of bracket.
306 * @param middleEvalPoint Initial middle evaluation point of bracket.
307 * @throws LockedException Raised if this instance is locked. This instance
308 * will be locked while doing some operations. Attempting to change any
309 * parameter while being locked will raise this exception.
310 * @throws NotReadyException Raised if this instance is not ready because a
311 * listener has not yet been provided.
312 * @throws InvalidBracketRangeException Raised if minEvalPoint <
313 * middleEvalPoint.
314 * @throws OptimizationException Raised if a bracket couldn't be found .
315 * because convergence was not achieved or function evaluation failed.
316 */
317 @SuppressWarnings("Duplicates")
318 public void computeBracket(final double minEvalPoint, final double middleEvalPoint) throws LockedException,
319 NotReadyException, InvalidBracketRangeException, OptimizationException {
320
321 if (isLocked()) {
322 throw new LockedException();
323 }
324 if (!isReady()) {
325 throw new NotReadyException();
326 }
327 if (minEvalPoint > middleEvalPoint) {
328 throw new InvalidBracketRangeException();
329 }
330
331 locked = true;
332
333 final var a = new double[1];
334 final var b = new double[1];
335 final var c = new double[1];
336
337 try {
338 ax = minEvalPoint;
339 bx = middleEvalPoint;
340 double fu;
341 fa = listener.evaluate(ax);
342 fb = listener.evaluate(bx);
343
344 //switch roles of a and b so that we can go downhill in the
345 //direction from a to b
346 if (fb > fa) {
347 a[0] = ax;
348 b[0] = bx;
349 swap(a, b);
350 ax = a[0];
351 bx = b[0];
352
353 a[0] = fa;
354 b[0] = fb;
355 swap(a, b);
356 a[0] = fa;
357 b[0] = fb;
358 }
359
360 //First guess for c
361 cx = bx + GOLD * (bx - ax);
362 fc = listener.evaluate(cx);
363
364 //Keep returning here until we bracket.
365 while (fb > fc) {
366 //Compute u by parabolic extrapolation from a, b, c. TINY is
367 //used to prevent any possible division by zero.
368 final var r = (bx - ax) * (fb - fc);
369 final var q = (bx - cx) * (fb - fa);
370 var u = bx - ((bx - cx) * q - (bx - ax) * r) /
371 (2.0 * sign(Math.max(Math.abs(q - r), TINY), q - r));
372 final var ulim = bx + GLIMIT * (cx - bx);
373
374 //We won't go farther than this. Test various possibilities:
375 if ((bx - u) * (u - cx) > 0.0) {
376 //Parabolic u is between b and c: try it.
377 fu = listener.evaluate(u);
378 if (fu < fc) {
379 //Got a minimum between b and c.
380 ax = bx;
381 bx = u;
382 fa = fb;
383 fb = fu;
384 break;
385
386 } else if (fu > fb) {
387 //Got a minimum between a and u
388 cx = u;
389 fc = fu;
390 break;
391 }
392
393 //Parabolic fit was no use. Use default magnification.
394 u = cx + GOLD * (cx - bx);
395 fu = listener.evaluate(u);
396
397 } else if ((cx - u) * (u - ulim) > 0.0) {
398 //Parabolic fit is between c and its allowed limit
399 fu = listener.evaluate(u);
400
401 if (fu < fc) {
402 a[0] = bx;
403 b[0] = cx;
404 c[0] = u;
405 shift3(a, b, c, u + GOLD * (u - cx));
406 bx = a[0];
407 cx = b[0];
408 u = c[0];
409
410 a[0] = fb;
411 b[0] = fc;
412 c[0] = fu;
413 shift3(a, b, c, listener.evaluate(u));
414 fb = a[0];
415 fc = b[0];
416 fu = c[0];
417 }
418 } else if ((u - ulim) * (ulim - cx) >= 0.0) {
419 //Limit parabolic u to maximum allowed value.
420 u = ulim;
421 fu = listener.evaluate(u);
422 } else {
423 //Reject parabolic u, use default magnification
424 u = cx + GOLD * (cx - bx);
425 fu = listener.evaluate(u);
426 }
427 //Eliminate the oldest point and continue
428 a[0] = ax;
429 b[0] = bx;
430 c[0] = cx;
431 shift3(a, b, c, u);
432 ax = a[0];
433 bx = b[0];
434 cx = c[0];
435
436 a[0] = fa;
437 b[0] = fb;
438 c[0] = fc;
439 shift3(a, b, c, fu);
440 fa = a[0];
441 fb = b[0];
442 fc = c[0];
443 }
444 } catch (final EvaluationException e) {
445 throw new OptimizationException(e);
446 } finally {
447 locked = false;
448 }
449
450 bracketAvailable = true;
451 bracketEvaluationAvailable = true;
452 }
453
454 /**
455 * Computes a bracket of values using provided value as a starting point,
456 * and assuming that bracket finishes at Double.MAX_VALUE.
457 * Given a function f, and given distinct initial points ax and bx = 0.0,
458 * this routine searches in the downhill direction (defined by the function
459 * as evaluated at the initial points) and returns
460 * ax (minimum evaluation point), bx (middle evaluation point), cx (maximum
461 * evaluation point) that bracket a minimum of the function. Also returned
462 * are the function values at the three points fa, fb, and fc, which are the
463 * function evaluations at minimum, middle and maximum bracket points.
464 *
465 * @param minEvalPoint Initial minimum evaluation point of bracket.
466 * @throws LockedException Raised if this instance is locked. This instance
467 * will be locked while doing some operations. Attempting to change any
468 * parameter while being locked will raise this exception.
469 * @throws NotReadyException Raised if this instance is not ready because a
470 * listener has not yet been provided.
471 * @throws InvalidBracketRangeException Raised if minEvalPoint < 0.0.
472 * @throws OptimizationException Raised if a bracket couldn't be found
473 * because convergence was not achieved or function evaluation failed.
474 */
475 public void computeBracket(final double minEvalPoint) throws LockedException, NotReadyException,
476 OptimizationException, InvalidBracketRangeException {
477 computeBracket(minEvalPoint, DEFAULT_MIDDLE_EVAL_POINT);
478 }
479
480 /**
481 * Computes a bracket of values using the whole range of possible values as
482 * an initial guess.
483 * Given a function f, and given distinct initial points ax =
484 * -Double.MAX_VALUE and bx = 0.0, this
485 * routine searches in the downhill direction (defined by the function as
486 * evaluated at the initial points) and returns
487 * ax (minimum evaluation point), bx (middle evaluation point), cx (maximum
488 * evaluation point) that bracket a minimum of the function. Also returned
489 * are the function values at the three points fa, fb, and fc, which are the
490 * function evaluations at minimum, middle and maximum bracket points
491 *
492 * @throws LockedException Raised if this instance is locked. This instance
493 * will be locked while doing some operations. Attempting to change any
494 * parameter while being locked will raise this exception.
495 * @throws NotReadyException Raised if this instance is not ready because a
496 * listener has not yet been provided.
497 * @throws OptimizationException Raised if a bracket couldn't be found
498 * because convergence was not achieved or function evaluation failed.
499 */
500 public void computeBracket() throws LockedException, NotReadyException, OptimizationException {
501 try {
502 computeBracket(DEFAULT_MIN_EVAL_POINT, DEFAULT_MIDDLE_EVAL_POINT);
503 } catch (InvalidBracketRangeException ignore) {
504 //never happens
505 }
506 }
507
508 /**
509 * Computes function evaluations at provided or estimated bracket locations.
510 * After calling this method bracket evaluations will be available.
511 *
512 * @throws LockedException Raised if this instance is locked. This instance
513 * will be locked while doing some operations. Attempting to change any
514 * parameter while being locked will raise this exception.
515 * @throws NotReadyException Raised if this instance is not ready because a
516 * listener has not yet been provided.
517 * @throws OptimizationException Raised if function evaluation failed.
518 */
519 public void evaluateBracket() throws LockedException, NotReadyException, OptimizationException {
520
521 if (isLocked()) {
522 throw new LockedException();
523 }
524 if (!isReady()) {
525 throw new NotReadyException();
526 }
527
528 locked = true;
529
530 try {
531 fa = listener.evaluate(ax);
532 fb = listener.evaluate(bx);
533 fc = listener.evaluate(cx);
534 } catch (final EvaluationException e) {
535 throw new OptimizationException(e);
536 } finally {
537 locked = false;
538 }
539
540 bracketEvaluationAvailable = true;
541 }
542
543 /**
544 * Returns boolean indicating whether bracket evaluations are available for
545 * retrieval.
546 *
547 * @return True if bracket evaluations are available, false otherwise.
548 */
549 public boolean areBracketEvaluationsAvailable() {
550 return bracketEvaluationAvailable;
551 }
552
553 /**
554 * Internal method to determine whether a and b have the same sign.
555 *
556 * @param a Value to be compared.
557 * @param b Value to be compared.
558 * @return Returns "a" if "a" and "b" have the same sign or "-a" otherwise.
559 */
560 protected double sign(final double a, final double b) {
561 if (b >= 0.0) {
562 return a >= 0.0 ? a : -a;
563 } else {
564 return a >= 0.0 ? -a : a;
565 }
566 }
567
568 /**
569 * Pushes "b" value into "a", and "c" value into "b". "a" and "b" are in/out parameters.
570 * Results will be available at a[0] and b[0] after executing this method.
571 *
572 * @param a a value to be lost.
573 * @param b a value to be shifted into "a".
574 * @param c a value to be shifted into "b".
575 */
576 protected void shift2(final double[] a, final double[] b, final double c) {
577 a[0] = b[0];
578 b[0] = c;
579 }
580
581 /**
582 * Pushes "b" value into "a", and "c" value into "b" and "d" value into "c". "a", "b" and "c"
583 * are in/out parameters.
584 * Results will be available at a[0], b[0] and c[0] after executing this
585 * method.
586 *
587 * @param a a value to be lost.
588 * @param b a value to be shifted into "a".
589 * @param c a value to be shifted into "b".
590 * @param d a value to be shifted into "c".
591 */
592 protected void shift3(final double[] a, final double[] b, final double[] c, final double d) {
593 a[0] = b[0];
594 b[0] = c[0];
595 c[0] = d;
596 }
597
598 /**
599 * Moves d, e and f into a[0], b[0] and c[0]. Previously existing values
600 * into a, b, c will be lost after executing this method.
601 *
602 * @param a a value to be set.
603 * @param b a value to be set.
604 * @param c a value to be set.
605 * @param d a value to be copied.
606 * @param e a value to be copied.
607 * @param f a value to be copied.
608 */
609 protected void mov3(final double[] a, final double[] b, final double[] c, final double d, final double e,
610 final double f) {
611 a[0] = d;
612 b[0] = e;
613 c[0] = f;
614 }
615
616 /**
617 * Internal method to swap two values. Value inside a[0] will be swapped
618 * with value provided in b[0].
619 *
620 * @param a Value to be swapped.
621 * @param b Value to be swapped.
622 */
623 private void swap(final double[] a, final double[] b) {
624 final var tmp = a[0];
625 a[0] = b[0];
626 b[0] = tmp;
627 }
628
629 /**
630 * Internal method to set a bracket of values. This method does not check
631 * whether this instance is locked.
632 *
633 * @param minEvalPoint Minimum bracket evaluation point.
634 * @param middleEvalPoint Middle bracket evaluation point.
635 * @param maxEvalPoint Maximum bracket evaluation point.
636 * @throws InvalidBracketRangeException Raised if the following condition is
637 * not met: minEvalPoint <= middleEvalPoint <= maxEvalPoint.
638 */
639 private void internalSetBracket(final double minEvalPoint, final double middleEvalPoint,
640 final double maxEvalPoint) throws InvalidBracketRangeException {
641
642 if ((minEvalPoint > middleEvalPoint) || (middleEvalPoint > maxEvalPoint)) {
643 //which also means || (minEvalPoint > maxEvalPoint))
644 throw new InvalidBracketRangeException();
645 }
646
647 ax = minEvalPoint;
648 bx = middleEvalPoint;
649 cx = maxEvalPoint;
650
651 bracketAvailable = true;
652 }
653 }