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 }