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.QuinticFunction;
26  import org.hipparchus.analysis.UnivariateFunction;
27  import org.hipparchus.analysis.differentiation.DSFactory;
28  import org.hipparchus.analysis.differentiation.Derivative;
29  import org.hipparchus.analysis.differentiation.UnivariateDifferentiableFunction;
30  import org.hipparchus.exception.MathIllegalArgumentException;
31  import org.hipparchus.exception.MathIllegalStateException;
32  import org.hipparchus.util.FastMath;
33  import org.junit.jupiter.api.Test;
34  
35  import java.util.ArrayList;
36  import java.util.List;
37  
38  import static org.junit.jupiter.api.Assertions.assertEquals;
39  import static org.junit.jupiter.api.Assertions.assertThrows;
40  import static org.junit.jupiter.api.Assertions.assertTrue;
41  
42  /**
43   * Test case for
44   * {@link BracketingNthOrderBrentSolver bracketing n<sup>th</sup> order Brent}
45   * solver.
46   */
47  final class BracketingNthOrderBrentSolverTest extends BaseSecantSolverAbstractTest {
48  
49      /** {@inheritDoc} */
50      @Override
51      protected UnivariateSolver getSolver() {
52          return new BracketingNthOrderBrentSolver();
53      }
54  
55      /** {@inheritDoc} */
56      @Override
57      protected int[] getQuinticEvalCounts() {
58          return new int[] { 1, 3, 8, 1, 9, 4, 8, 1, 12, 1, 16 };
59      }
60  
61      @Test
62      void testInsufficientOrder1() {
63          assertThrows(MathIllegalArgumentException.class, () -> {
64  
65              new BracketingNthOrderBrentSolver(1.0e-10, 1);
66          });
67      }
68  
69      @Test
70      void testInsufficientOrder2() {
71          assertThrows(MathIllegalArgumentException.class, () -> {
72              new BracketingNthOrderBrentSolver(1.0e-10, 1.0e-10, 1);
73          });
74      }
75  
76      @Test
77      void testInsufficientOrder3() {
78          assertThrows(MathIllegalArgumentException.class, () -> {
79              new BracketingNthOrderBrentSolver(1.0e-10, 1.0e-10, 1.0e-10, 1);
80          });
81      }
82  
83      @Test
84      void testConstructorsOK() {
85          assertEquals(2, new BracketingNthOrderBrentSolver(1.0e-10,
86                                                                       2).getMaximalOrder());
87          assertEquals(2, new BracketingNthOrderBrentSolver(1.0e-10,
88                                                                       1.0e-10,
89                                                                       2).getMaximalOrder());
90          assertEquals(2, new BracketingNthOrderBrentSolver(1.0e-10,
91                                                                       1.0e-10,
92                                                                       1.0e-10,
93                                                                       2).getMaximalOrder());
94      }
95  
96      @Test
97      void testConvergenceOnFunctionAccuracy() {
98          BracketingNthOrderBrentSolver solver = new BracketingNthOrderBrentSolver(
99                          1.0e-12, 1.0e-10, 0.001, 3);
100         QuinticFunction f = new QuinticFunction();
101         double result = solver.solve(20, f, 0.2, 0.9, 0.4,
102                                      AllowedSolution.BELOW_SIDE);
103         assertEquals(0, f.value(result),
104                                 solver.getFunctionValueAccuracy());
105         assertTrue(f.value(result) <= 0);
106         assertTrue(result - 0.5 > solver.getAbsoluteAccuracy());
107         result = solver.solve(20, f, -0.9, -0.2, -0.4,
108                               AllowedSolution.ABOVE_SIDE);
109         assertEquals(0, f.value(result),
110                                 solver.getFunctionValueAccuracy());
111         assertTrue(f.value(result) >= 0);
112         assertTrue(result + 0.5 < -solver.getAbsoluteAccuracy());
113     }
114 
115     @Test
116     void testIssue716() {
117         BracketingNthOrderBrentSolver solver = new BracketingNthOrderBrentSolver(
118                         1.0e-12, 1.0e-10, 1.0e-22, 5);
119         UnivariateFunction sharpTurn = new UnivariateFunction() {
120 
121             public double value(double x) {
122                 return (2 * x + 1) / (1.0e9 * (x + 1));
123             }
124         };
125         double result = solver.solve(100, sharpTurn, -0.9999999, 30, 15,
126                                      AllowedSolution.RIGHT_SIDE);
127         assertEquals(0, sharpTurn.value(result),
128                                 solver.getFunctionValueAccuracy());
129         assertTrue(sharpTurn.value(result) >= 0);
130         assertEquals(-0.5, result, 1.0e-10);
131     }
132 
133     @Test
134     void testToleranceLessThanUlp() {
135         // function that is never zero
136         UnivariateFunction f = (x) -> x < 2.1 ? -1 : 1;
137         // tolerance less than 1 ulp(x)
138         UnivariateSolver solver = new BracketingNthOrderBrentSolver(0, 1e-18, 0,
139                                                                     5);
140 
141         // make sure it doesn't throw a maxIterations exception
142         double result = solver.solve(100, f, 0.0, 5.0);
143         assertEquals(2.1, result, 0.0);
144     }
145 
146     @Test
147     void testFasterThanNewton() {
148         // the following test functions come from Beny Neta's paper:
149         // "Several New Methods for solving Equations"
150         // intern J. Computer Math Vol 23 pp 265-282
151         // available here: http://www.math.nps.navy.mil/~bneta/SeveralNewMethods.PDF
152         // the reference roots have been computed by the Dfp solver to more than
153         // 80 digits and checked with emacs (only the first 20 digits are reproduced here)
154         compare(new TestFunction(0.0, -2, 2) {
155 
156             @Override
157             public <T extends Derivative<T>> T value(T x) {
158                 return x.sin().subtract(x.multiply(0.5));
159             }
160         });
161         compare(new TestFunction(6.3087771299726890947, -5, 10) {
162 
163             @Override
164             public <T extends Derivative<T>> T value(T x) {
165                 return x.pow(5).add(x).subtract(10000);
166             }
167         });
168         compare(new TestFunction(9.6335955628326951924, 0.001, 10) {
169 
170             @Override
171             public <T extends Derivative<T>> T value(T x) {
172                 return x.sqrt().subtract(x.reciprocal()).subtract(3);
173             }
174         });
175         compare(new TestFunction(2.8424389537844470678, -5, 5) {
176 
177             @Override
178             public <T extends Derivative<T>> T value(T x) {
179                 return x.exp().add(x).subtract(20);
180             }
181         });
182         compare(new TestFunction(8.3094326942315717953, 0.001, 10) {
183 
184             @Override
185             public <T extends Derivative<T>> T value(T x) {
186                 return x.log().add(x.sqrt()).subtract(5);
187             }
188         });
189         compare(new TestFunction(1.4655712318767680266, -0.5, 1.5) {
190 
191             @Override
192             public <T extends Derivative<T>> T value(T x) {
193                 return x.subtract(1).multiply(x).multiply(x).subtract(1);
194             }
195         });
196 
197     }
198 
199     @Test
200     void testSolverStopIteratingOnceSolutionIsFound() {
201         final double absoluteAccuracy = 0.1;
202         final double valueAccuracy = 1.;
203         BracketingNthOrderBrentSolver solver = new BracketingNthOrderBrentSolver(
204                         1e-14, absoluteAccuracy, valueAccuracy, 5);
205         FunctionHipparcus function = new FunctionHipparcus();
206         solver.solve(100, function, -100, 100);
207         assertEquals(1, function.values.stream()
208                         .filter(value -> FastMath.abs(value) < valueAccuracy)
209                         .count());
210         assertEquals(7, function.values.size());
211     }
212 
213     private static class FunctionHipparcus implements UnivariateFunction {
214 
215         List<Double> values = new ArrayList<>();
216 
217         @Override
218         public double value(double x) {
219             double value = -0.01 * x * x + 20;
220             values.add(value);
221             return value;
222         }
223     }
224 
225     private void compare(TestFunction f) {
226         compare(f, f.getRoot(), f.getMin(), f.getMax());
227     }
228 
229     private void compare(final UnivariateDifferentiableFunction f, double root,
230                          double min, double max) {
231         NewtonRaphsonSolver newton = new NewtonRaphsonSolver(1.0e-12);
232         BracketingNthOrderBrentSolver bracketing = new BracketingNthOrderBrentSolver(
233                         1.0e-12, 1.0e-12, 1.0e-18, 5);
234         double resultN;
235         try {
236             resultN = newton.solve(100, f, min, max);
237         } catch (MathIllegalStateException tmee) {
238             resultN = Double.NaN;
239         }
240         double resultB;
241         try {
242             resultB = bracketing.solve(100, f, min, max);
243         } catch (MathIllegalStateException tmee) {
244             resultB = Double.NaN;
245         }
246         assertEquals(root, resultN, newton.getAbsoluteAccuracy());
247         assertEquals(root, resultB,
248                                 bracketing.getAbsoluteAccuracy());
249 
250         // bracketing solver evaluates only function value, we set the weight to 1
251         final int weightedBracketingEvaluations = bracketing.getEvaluations();
252 
253         // Newton-Raphson solver evaluates both function value and derivative, we set the weight to 2
254         final int weightedNewtonEvaluations = 2 * newton.getEvaluations();
255 
256         assertTrue(
257                         weightedBracketingEvaluations < weightedNewtonEvaluations);
258 
259     }
260 
261     private static abstract class TestFunction implements UnivariateDifferentiableFunction {
262 
263         private final double root;
264 
265         private final double min;
266 
267         private final double max;
268 
269         protected TestFunction(final double root, final double min,
270                                final double max) {
271             this.root = root;
272             this.min = min;
273             this.max = max;
274         }
275 
276         public double getRoot() {
277             return root;
278         }
279 
280         public double getMin() {
281             return min;
282         }
283 
284         public double getMax() {
285             return max;
286         }
287 
288         public double value(final double x) {
289             return value(new DSFactory(0, 0).constant(x)).getValue();
290         }
291 
292         public abstract <T extends Derivative<T>> T value(final T t);
293 
294     }
295 
296 }