View Javadoc
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.roots;
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   * Computes a root for a single dimension function inside a given bracket of
27   * values, in other words, root will only be searched within provided minimum
28   * and maximum evaluation points.
29   */
30  public abstract class BracketedSingleRootEstimator extends SingleRootEstimator {
31  
32      /**
33       * Number tries to automatically compute a bracket of values for a given
34       * function.
35       */
36      public static final int NTRY = 50;
37  
38      /**
39       * Factor to use to modify initial values when searching for a bracket.
40       */
41      public static final double FACTOR = 1.6;
42  
43      /**
44       * Default minimum evaluation point.
45       */
46      public static final double DEFAULT_MIN_EVAL_POINT = -Double.MAX_VALUE;
47  
48      /**
49       * Default maximum evaluation point.
50       */
51      public static final double DEFAULT_MAX_EVAL_POINT = Double.MAX_VALUE;
52  
53      /**
54       * Constant defining the value by which the largest bracket evaluation value
55       * is increased respect the minimum.
56       */
57      public static final double BRACKET_EPS = 1e-8;
58  
59      /**
60       * Minimum evaluation point.
61       */
62      protected double minEvalPoint;
63  
64      /**
65       * Maximum evaluation point.
66       */
67      protected double maxEvalPoint;
68  
69      /**
70       * Boolean indicating whether a bracket has been computed and is available.
71       */
72      protected boolean bracketAvailable;
73  
74      /**
75       * Empty constructor.
76       */
77      protected BracketedSingleRootEstimator() {
78          super();
79          minEvalPoint = DEFAULT_MIN_EVAL_POINT;
80          maxEvalPoint = DEFAULT_MAX_EVAL_POINT;
81          bracketAvailable = true;
82      }
83  
84      /**
85       * Constructor.
86       *
87       * @param listener     Listener to evaluate a single dimension function f(x)
88       *                     to find its roots.
89       * @param minEvalPoint Smallest value inside the bracket of values where the
90       *                     root will be searched.
91       * @param maxEvalPoint Largest value inside the bracket of values where the
92       *                     root will be searched.
93       * @throws InvalidBracketRangeException Raised if minEvalPoint <
94       *                                      maxEvalPoint.
95       */
96      protected BracketedSingleRootEstimator(
97              final SingleDimensionFunctionEvaluatorListener listener, final double minEvalPoint,
98              final double maxEvalPoint) throws InvalidBracketRangeException {
99          super(listener);
100         internalSetBracket(minEvalPoint, maxEvalPoint);
101     }
102 
103     /**
104      * Sets the bracket of values (i.e. range of values) where the root will be
105      * searched.
106      *
107      * @param minEvalPoint Smallest value inside the bracket of values where the
108      *                     root will be searched.
109      * @param maxEvalPoint Largest value inside the bracket of values where the
110      *                     root will be searched.
111      * @throws LockedException              Raised if this instance is locked while doing
112      *                                      some computations.
113      * @throws InvalidBracketRangeException Raised if minEvalPoint <
114      *                                      maxEvalPoint.
115      */
116     public void setBracket(final double minEvalPoint, final double maxEvalPoint) throws LockedException,
117             InvalidBracketRangeException {
118         if (isLocked()) {
119             throw new LockedException();
120         }
121         internalSetBracket(minEvalPoint, maxEvalPoint);
122     }
123 
124     /**
125      * Internal method to set the bracket of values (i.e. range of values) where
126      * the root will be searched.
127      *
128      * @param minEvalPoint Smallest value inside the bracket of values where the
129      *                     root will be searched.
130      * @param maxEvalPoint Largest value inside the bracket of values where the
131      *                     root will be searched.
132      * @throws InvalidBracketRangeException Raised if minEvalPoint <
133      *                                      maxEvalPoint.
134      */
135     private void internalSetBracket(final double minEvalPoint, final double maxEvalPoint)
136             throws InvalidBracketRangeException {
137         if (minEvalPoint >= maxEvalPoint) {
138             throw new InvalidBracketRangeException();
139         }
140 
141         this.minEvalPoint = minEvalPoint;
142         this.maxEvalPoint = maxEvalPoint;
143         bracketAvailable = true;
144     }
145 
146     /**
147      * Returns boolean indicating whether bracket has been set or not.
148      *
149      * @return True if bracket has been set, false otherwise.
150      */
151     public boolean isBracketAvailable() {
152         return bracketAvailable;
153     }
154 
155     /**
156      * Returns smallest value inside the bracket of values where the root will
157      * be searched.
158      *
159      * @return Smallest value inside the bracket.
160      * @throws NotAvailableException Raised if bracket has not been set.
161      */
162     public double getMinEvaluationPoint() throws NotAvailableException {
163         if (!isBracketAvailable()) {
164             throw new NotAvailableException();
165         }
166         return minEvalPoint;
167     }
168 
169     /**
170      * Returns largest value inside the bracket of values where the root will
171      * be searched.
172      *
173      * @return Largest values inside the bracket.
174      * @throws NotAvailableException Raised if bracket has not been set.
175      */
176     public double getMaxEvaluationPoint() throws NotAvailableException {
177         if (!isBracketAvailable()) {
178             throw new NotAvailableException();
179         }
180         return maxEvalPoint;
181     }
182 
183     /**
184      * Starting from provided minimum and maximum values, this method expands
185      * the range (i.e. bracket of values) until a zero crossing is found where
186      * a root is present or until the bracket becomes unacceptably large, where
187      * an exception will be raised.
188      * Notice that this method searches for zero crossings, hence, functions
189      * such as Math.pow(x - root, 2.0), will raise a RootEstimationException
190      * because the only root present in them does not produce a zero crossing.
191      *
192      * @param minEvalPoint Smallest initial value to estimate a new larger
193      *                     bracket.
194      * @param maxEvalPoint Largest initial value to estimate a new larger
195      *                     bracket.
196      * @throws LockedException              Raised if this instance is locked while doing
197      *                                      some computations.
198      * @throws NotReadyException            Raised if this instance is not ready (e.g. a
199      *                                      listener has not yet been provided, etc.)
200      * @throws InvalidBracketRangeException Raised if minEvalPoint <
201      *                                      maxEvalPoint
202      * @throws RootEstimationException      Raised if a bracket couldn't be found
203      *                                      inside the provided limits because no zero crossing was present or
204      *                                      convergence was not achieved.
205      */
206     public void computeBracket(final double minEvalPoint, final double maxEvalPoint)
207             throws LockedException, NotReadyException, InvalidBracketRangeException, RootEstimationException {
208 
209         if (isLocked()) {
210             throw new LockedException();
211         }
212         if (!isReady()) {
213             throw new NotReadyException();
214         }
215         if (minEvalPoint >= maxEvalPoint) {
216             throw new InvalidBracketRangeException();
217         }
218 
219         locked = true;
220 
221         // expand initial bracket until function contains a sign change
222         var x1 = minEvalPoint;
223         var x2 = maxEvalPoint;
224         double f1;
225         double f2;
226         try {
227             f1 = listener.evaluate(x1);
228             f2 = listener.evaluate(x2);
229 
230             var found = false;
231             for (var j = 0; j < NTRY; j++) {
232                 if (f1 * f2 < 0.0) {
233                     found = true;
234                     break;
235                 }
236                 if (Math.abs(f1) < Math.abs(f2)) {
237                     x1 += FACTOR * (x1 - x2);
238                     f1 = listener.evaluate(x1);
239                 } else {
240                     x2 += FACTOR * (x2 - x1);
241                     f2 = listener.evaluate(x2);
242                 }
243             }
244 
245             if (!found) {
246                 throw new RootEstimationException();
247             }
248 
249             this.minEvalPoint = x1;
250             this.maxEvalPoint = x2;
251             bracketAvailable = true;
252 
253         } catch (final EvaluationException e) {
254             throw new RootEstimationException(e);
255         } finally {
256             locked = false;
257         }
258     }
259 
260     /**
261      * Starting from provided point, this method expands the range (i.e.
262      * bracket of values) until a zero crossing is found where a root is present
263      * or until the bracket becomes unacceptably large, where an exception will
264      * be raised.
265      * Notice that this method searches for zero crossings, hence, functions
266      * such as Math.pow(x - root, 2.0), will raise a RootEstimationException
267      * because the only root present in them does not produce a zero crossing.
268      *
269      * @param point Initial value to start the bracket computation. Bracket
270      *              range is expanded starting at the point that was provided.
271      * @throws LockedException              Raised if this instance is locked while doing
272      *                                      some computations.
273      * @throws NotReadyException            Raised if this instance is not ready (e.g. a
274      *                                      listener has not yet been provided, etc.).
275      * @throws InvalidBracketRangeException Raised if point is close to
276      *                                      Double.MAX_VALUE.
277      * @throws RootEstimationException      Raised if a bracket couldn't be found
278      *                                      inside the provided limits because no zero crossing was present or
279      *                                      convergence was not achieved.
280      */
281     public void computeBracket(final double point) throws LockedException, NotReadyException,
282             InvalidBracketRangeException, RootEstimationException {
283         computeBracket(point, FACTOR * point + BRACKET_EPS);
284     }
285 
286     /**
287      * Starting at zero, this method expands the range (i.e. bracket of values)
288      * until a zero crossing is found where a root is present or until the
289      * bracket becomes unacceptably large, where an exception will be raised.
290      * Notice that this method searches for zero crossings, hence, functions
291      * such as Math.pow(x - root, 2.0), will raise a RootEstimationException
292      * because the only root present in them does not produce a zero crossing.
293      *
294      * @throws LockedException         Raised if this instance is locked while doing
295      *                                 some computations.
296      * @throws NotReadyException       Raised if this instance is not ready (e.g. a
297      *                                 listener has not yet been provided, etc.).
298      * @throws RootEstimationException Raised if a bracket couldn't be found
299      *                                 inside the provided limits because no zero crossing was present or
300      *                                 convergence was not achieved.
301      */
302     public void computeBracket() throws LockedException, NotReadyException, RootEstimationException {
303         try {
304             computeBracket(0.0, BRACKET_EPS);
305         } catch (final InvalidBracketRangeException ignore) {
306             // never happens
307         }
308     }
309 
310     /**
311      * Internal method to swap two values. Value inside a[0] will be swapped
312      * with value provided in b[0].
313      *
314      * @param a Value to be swapped.
315      * @param b Value to be swapped.
316      */
317     protected void swap(final double[] a, final double[] b) {
318         final var tmp = a[0];
319         a[0] = b[0];
320         b[0] = tmp;
321     }
322 
323     /**
324      * Internal method to determine whether a and b have the same sign.
325      *
326      * @param a Value to be compared.
327      * @param b Value to be compared.
328      * @return Returns "a" if "a" and "b" have the same sign or "-a" otherwise.
329      */
330     protected double sign(final double a, final double b) {
331         if (b >= 0.0) {
332             return a >= 0.0 ? a : -a;
333         } else {
334             return a >= 0.0 ? -a : a;
335         }
336     }
337 }