View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.analysis.solvers;
24  
25  import org.hipparchus.analysis.UnivariateFunction;
26  import org.hipparchus.exception.MathIllegalArgumentException;
27  import org.hipparchus.exception.MathIllegalStateException;
28  import org.hipparchus.exception.NullArgumentException;
29  import org.hipparchus.util.Incrementor;
30  import org.hipparchus.util.MathUtils;
31  
32  /**
33   * Provide a default implementation for several functions useful to generic
34   * solvers.
35   * The default values for relative and function tolerances are 1e-14
36   * and 1e-15, respectively. It is however highly recommended to not
37   * rely on the default, but rather carefully consider values that match
38   * user's expectations, as well as the specifics of each implementation.
39   *
40   * @param <F> Type of function to solve.
41   *
42   */
43  public abstract class BaseAbstractUnivariateSolver<F extends UnivariateFunction>
44      implements BaseUnivariateSolver<F> {
45      /** Default relative accuracy. */
46      private static final double DEFAULT_RELATIVE_ACCURACY = 1e-14;
47      /** Default function value accuracy. */
48      private static final double DEFAULT_FUNCTION_VALUE_ACCURACY = 1e-15;
49      /** Function value accuracy. */
50      private final double functionValueAccuracy;
51      /** Absolute accuracy. */
52      private final double absoluteAccuracy;
53      /** Relative accuracy. */
54      private final double relativeAccuracy;
55      /** Evaluations counter. */
56      private Incrementor evaluations;
57      /** Lower end of search interval. */
58      private double searchMin;
59      /** Higher end of search interval. */
60      private double searchMax;
61      /** Initial guess. */
62      private double searchStart;
63      /** Function to solve. */
64      private F function;
65  
66      /**
67       * Construct a solver with given absolute accuracy.
68       *
69       * @param absoluteAccuracy Maximum absolute error.
70       */
71      protected BaseAbstractUnivariateSolver(final double absoluteAccuracy) {
72          this(DEFAULT_RELATIVE_ACCURACY,
73               absoluteAccuracy,
74               DEFAULT_FUNCTION_VALUE_ACCURACY);
75      }
76  
77      /**
78       * Construct a solver with given accuracies.
79       *
80       * @param relativeAccuracy Maximum relative error.
81       * @param absoluteAccuracy Maximum absolute error.
82       */
83      protected BaseAbstractUnivariateSolver(final double relativeAccuracy,
84                                             final double absoluteAccuracy) {
85          this(relativeAccuracy,
86               absoluteAccuracy,
87               DEFAULT_FUNCTION_VALUE_ACCURACY);
88      }
89  
90      /**
91       * Construct a solver with given accuracies.
92       *
93       * @param relativeAccuracy Maximum relative error.
94       * @param absoluteAccuracy Maximum absolute error.
95       * @param functionValueAccuracy Maximum function value error.
96       */
97      protected BaseAbstractUnivariateSolver(final double relativeAccuracy,
98                                             final double absoluteAccuracy,
99                                             final double functionValueAccuracy) {
100         this.absoluteAccuracy      = absoluteAccuracy;
101         this.relativeAccuracy      = relativeAccuracy;
102         this.functionValueAccuracy = functionValueAccuracy;
103         this.evaluations           = new Incrementor();
104     }
105 
106     /** {@inheritDoc} */
107     @Override
108     public int getEvaluations() {
109         return evaluations.getCount();
110     }
111     /** Get lower end of the search interval.
112      * @return the lower end of the search interval
113      */
114     public double getMin() {
115         return searchMin;
116     }
117     /** Get higher end of the search interval.
118      * @return the higher end of the search interval
119      */
120     public double getMax() {
121         return searchMax;
122     }
123     /** Get initial guess.
124      * @return the initial guess
125      */
126     public double getStartValue() {
127         return searchStart;
128     }
129     /**
130      * {@inheritDoc}
131      */
132     @Override
133     public double getAbsoluteAccuracy() {
134         return absoluteAccuracy;
135     }
136     /**
137      * {@inheritDoc}
138      */
139     @Override
140     public double getRelativeAccuracy() {
141         return relativeAccuracy;
142     }
143     /**
144      * {@inheritDoc}
145      */
146     @Override
147     public double getFunctionValueAccuracy() {
148         return functionValueAccuracy;
149     }
150 
151     /**
152      * Compute the objective function value.
153      *
154      * @param point Point at which the objective function must be evaluated.
155      * @return the objective function value at specified point.
156      * @throws MathIllegalStateException if the maximal number of evaluations
157      * is exceeded.
158      */
159     protected double computeObjectiveValue(double point)
160         throws MathIllegalStateException {
161         incrementEvaluationCount();
162         return function.value(point);
163     }
164 
165     /**
166      * Prepare for computation.
167      * Subclasses must call this method if they override any of the
168      * {@code solve} methods.
169      *
170      * @param f Function to solve.
171      * @param min Lower bound for the interval.
172      * @param max Upper bound for the interval.
173      * @param startValue Start value to use.
174      * @param maxEval Maximum number of evaluations.
175      * @exception NullArgumentException if f is null
176      */
177     protected void setup(int maxEval,
178                          F f,
179                          double min, double max,
180                          double startValue)
181         throws NullArgumentException {
182         // Checks.
183         MathUtils.checkNotNull(f);
184 
185         // Reset.
186         searchMin = min;
187         searchMax = max;
188         searchStart = startValue;
189         function = f;
190         evaluations = evaluations.withMaximalCount(maxEval);
191     }
192 
193     /** {@inheritDoc} */
194     @Override
195     public double solve(int maxEval, F f, double min, double max, double startValue)
196         throws MathIllegalArgumentException, MathIllegalStateException {
197         // Initialization.
198         setup(maxEval, f, min, max, startValue);
199 
200         // Perform computation.
201         return doSolve();
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public double solve(int maxEval, F f, double min, double max) {
207         return solve(maxEval, f, min, max, min + 0.5 * (max - min));
208     }
209 
210     /** {@inheritDoc} */
211     @Override
212     public double solve(int maxEval, F f, double startValue)
213         throws MathIllegalArgumentException, MathIllegalStateException {
214         return solve(maxEval, f, Double.NaN, Double.NaN, startValue);
215     }
216 
217     /**
218      * Method for implementing actual optimization algorithms in derived
219      * classes.
220      *
221      * @return the root.
222      * @throws MathIllegalStateException if the maximal number of evaluations
223      * is exceeded.
224      * @throws MathIllegalArgumentException if the initial search interval does not bracket
225      * a root and the solver requires it.
226      */
227     protected abstract double doSolve()
228         throws MathIllegalArgumentException, MathIllegalStateException;
229 
230     /**
231      * Check whether the function takes opposite signs at the endpoints.
232      *
233      * @param lower Lower endpoint.
234      * @param upper Upper endpoint.
235      * @return {@code true} if the function values have opposite signs at the
236      * given points.
237      */
238     protected boolean isBracketing(final double lower,
239                                    final double upper) {
240         return UnivariateSolverUtils.isBracketing(function, lower, upper);
241     }
242 
243     /**
244      * Check whether the arguments form a (strictly) increasing sequence.
245      *
246      * @param start First number.
247      * @param mid Second number.
248      * @param end Third number.
249      * @return {@code true} if the arguments form an increasing sequence.
250      */
251     protected boolean isSequence(final double start,
252                                  final double mid,
253                                  final double end) {
254         return UnivariateSolverUtils.isSequence(start, mid, end);
255     }
256 
257     /**
258      * Check that the endpoints specify an interval.
259      *
260      * @param lower Lower endpoint.
261      * @param upper Upper endpoint.
262      * @throws MathIllegalArgumentException if {@code lower >= upper}.
263      */
264     protected void verifyInterval(final double lower,
265                                   final double upper)
266         throws MathIllegalArgumentException {
267         UnivariateSolverUtils.verifyInterval(lower, upper);
268     }
269 
270     /**
271      * Check that {@code lower < initial < upper}.
272      *
273      * @param lower Lower endpoint.
274      * @param initial Initial value.
275      * @param upper Upper endpoint.
276      * @throws MathIllegalArgumentException if {@code lower >= initial} or
277      * {@code initial >= upper}.
278      */
279     protected void verifySequence(final double lower,
280                                   final double initial,
281                                   final double upper)
282         throws MathIllegalArgumentException {
283         UnivariateSolverUtils.verifySequence(lower, initial, upper);
284     }
285 
286     /**
287      * Check that the endpoints specify an interval and the function takes
288      * opposite signs at the endpoints.
289      *
290      * @param lower Lower endpoint.
291      * @param upper Upper endpoint.
292      * @throws NullArgumentException if the function has not been set.
293      * @throws MathIllegalArgumentException if the function has the same sign at
294      * the endpoints.
295      */
296     protected void verifyBracketing(final double lower,
297                                     final double upper)
298         throws MathIllegalArgumentException, NullArgumentException {
299         UnivariateSolverUtils.verifyBracketing(function, lower, upper);
300     }
301 
302     /**
303      * Increment the evaluation count by one.
304      * Method {@link #computeObjectiveValue(double)} calls this method internally.
305      * It is provided for subclasses that do not exclusively use
306      * {@code computeObjectiveValue} to solve the function.
307      * See e.g. {@link AbstractUnivariateDifferentiableSolver}.
308      *
309      * @throws MathIllegalStateException when the allowed number of function
310      * evaluations has been exhausted.
311      */
312     protected void incrementEvaluationCount()
313         throws MathIllegalStateException {
314         evaluations.increment();
315     }
316 }