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.CalculusFieldUnivariateFunction;
26  import org.hipparchus.analysis.QuinticFunction;
27  import org.hipparchus.analysis.UnivariateFunction;
28  import org.hipparchus.analysis.function.Sin;
29  import org.hipparchus.exception.MathIllegalArgumentException;
30  import org.hipparchus.exception.NullArgumentException;
31  import org.hipparchus.util.Binary64;
32  import org.hipparchus.util.FastMath;
33  import org.junit.jupiter.api.Test;
34  
35  import static org.junit.jupiter.api.Assertions.assertEquals;
36  import static org.junit.jupiter.api.Assertions.assertThrows;
37  import static org.junit.jupiter.api.Assertions.assertTrue;
38  
39  /**
40   */
41  class UnivariateSolverUtilsTest {
42  
43      private UnivariateFunction sin = new Sin();
44      private CalculusFieldUnivariateFunction<Binary64> fieldSin = x -> x.sin();
45  
46      @Test
47      void testSolveNull() {
48          assertThrows(NullArgumentException.class, () -> {
49              UnivariateSolverUtils.solve(null, 0.0, 4.0);
50          });
51      }
52  
53      @Test
54      void testSolveBadEndpoints() {
55          assertThrows(MathIllegalArgumentException.class, () -> {
56              double root = UnivariateSolverUtils.solve(sin, 4.0, -0.1, 1e-6);
57              System.out.println("root=" + root);
58          });
59      }
60  
61      @Test
62      void testSolveBadAccuracy() {
63          try { // bad accuracy
64              UnivariateSolverUtils.solve(sin, 0.0, 4.0, 0.0);
65  //             Assertions.fail("Expecting MathIllegalArgumentException"); // TODO needs rework since convergence behaviour was changed
66          } catch (MathIllegalArgumentException ex) {
67              // expected
68          }
69      }
70  
71      @Test
72      void testSolveSin() {
73          double x = UnivariateSolverUtils.solve(sin, 1.0, 4.0);
74          assertEquals(FastMath.PI, x, 1.0e-4);
75      }
76  
77      @Test
78      void testSolveAccuracyNull()  {
79          assertThrows(NullArgumentException.class, () -> {
80              double accuracy = 1.0e-6;
81              UnivariateSolverUtils.solve(null, 0.0, 4.0, accuracy);
82          });
83      }
84  
85      @Test
86      void testSolveAccuracySin() {
87          double accuracy = 1.0e-6;
88          double x = UnivariateSolverUtils.solve(sin, 1.0,
89                  4.0, accuracy);
90          assertEquals(FastMath.PI, x, accuracy);
91      }
92  
93      @Test
94      void testSolveNoRoot() {
95          assertThrows(MathIllegalArgumentException.class, () -> {
96              UnivariateSolverUtils.solve(sin, 1.0, 1.5);
97          });
98      }
99  
100     @Test
101     void testBracketSin() {
102         double[] result = UnivariateSolverUtils.bracket(sin,
103                 0.0, -2.0, 2.0);
104         assertTrue(sin.value(result[0]) < 0);
105         assertTrue(sin.value(result[1]) > 0);
106     }
107 
108     @Test
109     void testBracketCentered() {
110         double initial = 0.1;
111         double[] result = UnivariateSolverUtils.bracket(sin, initial, -2.0, 2.0, 0.2, 1.0, 100);
112         assertTrue(result[0] < initial);
113         assertTrue(result[1] > initial);
114         assertTrue(sin.value(result[0]) < 0);
115         assertTrue(sin.value(result[1]) > 0);
116     }
117 
118     @Test
119     void testBracketLow() {
120         double initial = 0.5;
121         double[] result = UnivariateSolverUtils.bracket(sin, initial, -2.0, 2.0, 0.2, 1.0, 100);
122         assertTrue(result[0] < initial);
123         assertTrue(result[1] < initial);
124         assertTrue(sin.value(result[0]) < 0);
125         assertTrue(sin.value(result[1]) > 0);
126     }
127 
128     @Test
129     void testBracketHigh(){
130         double initial = -0.5;
131         double[] result = UnivariateSolverUtils.bracket(sin, initial, -2.0, 2.0, 0.2, 1.0, 100);
132         assertTrue(result[0] > initial);
133         assertTrue(result[1] > initial);
134         assertTrue(sin.value(result[0]) < 0);
135         assertTrue(sin.value(result[1]) > 0);
136     }
137 
138     @Test
139     void testBracketLinear(){
140         assertThrows(MathIllegalArgumentException.class, () -> {
141             UnivariateSolverUtils.bracket(new UnivariateFunction() {
142                 public double value(double x) {
143                     return 1 - x;
144                 }
145             }, 1000, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 100);
146         });
147     }
148 
149     @Test
150     void testBracketExponential(){
151         double[] result = UnivariateSolverUtils.bracket(new UnivariateFunction() {
152             public double value(double x) {
153                 return 1 - x;
154             }
155         }, 1000, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0, 2.0, 10);
156         assertTrue(result[0] <= 1);
157         assertTrue(result[1] >= 1);
158     }
159 
160     @Test
161     void testBracketEndpointRoot() {
162         double[] result = UnivariateSolverUtils.bracket(sin, 1.5, 0, 2.0, 100);
163         assertEquals(0.0, sin.value(result[0]), 1.0e-15);
164         assertTrue(sin.value(result[1]) > 0);
165     }
166 
167     @Test
168     void testNullFunction() {
169         assertThrows(NullArgumentException.class, () -> {
170             UnivariateSolverUtils.bracket(null, 1.5, 0, 2.0);
171         });
172     }
173 
174     @Test
175     void testBadInitial() {
176         assertThrows(MathIllegalArgumentException.class, () -> {
177             UnivariateSolverUtils.bracket(sin, 2.5, 0, 2.0);
178         });
179     }
180 
181     @Test
182     void testBadAdditive() {
183         assertThrows(MathIllegalArgumentException.class, () -> {
184             UnivariateSolverUtils.bracket(sin, 1.0, -2.0, 3.0, -1.0, 1.0, 100);
185         });
186     }
187 
188     @Test
189     void testIterationExceeded() {
190         assertThrows(MathIllegalArgumentException.class, () -> {
191             UnivariateSolverUtils.bracket(sin, 1.0, -2.0, 3.0, 1.0e-5, 1.0, 100);
192         });
193     }
194 
195     @Test
196     void testBadEndpoints() {
197         assertThrows(MathIllegalArgumentException.class, () -> {
198             // endpoints not valid
199             UnivariateSolverUtils.bracket(sin, 1.5, 2.0, 1.0);
200         });
201     }
202 
203     @Test
204     void testBadMaximumIterations() {
205         assertThrows(MathIllegalArgumentException.class, () -> {
206             // bad maximum iterations
207             UnivariateSolverUtils.bracket(sin, 1.5, 0, 2.0, 0);
208         });
209     }
210 
211     @Test
212     void testFieldBracketSin() {
213         Binary64[] result = UnivariateSolverUtils.bracket(fieldSin,new Binary64(0.0),
214                                                            new Binary64(-2.0),new Binary64(2.0));
215         assertTrue(fieldSin.value(result[0]).getReal() < 0);
216         assertTrue(fieldSin.value(result[1]).getReal() > 0);
217     }
218 
219     @Test
220     void testFieldBracketCentered() {
221         Binary64 initial = new Binary64(0.1);
222         Binary64[] result = UnivariateSolverUtils.bracket(fieldSin, initial,
223                                                            new Binary64(-2.0), new Binary64(2.0),
224                                                            new Binary64(0.2), new Binary64(1.0),
225                                                            100);
226         assertTrue(result[0].getReal() < initial.getReal());
227         assertTrue(result[1].getReal() > initial.getReal());
228         assertTrue(fieldSin.value(result[0]).getReal() < 0);
229         assertTrue(fieldSin.value(result[1]).getReal() > 0);
230     }
231 
232     @Test
233     void testFieldBracketLow() {
234         Binary64 initial = new Binary64(0.5);
235         Binary64[] result = UnivariateSolverUtils.bracket(fieldSin, initial,
236                                                            new Binary64(-2.0), new Binary64(2.0),
237                                                            new Binary64(0.2), new Binary64(1.0),
238                                                            100);
239         assertTrue(result[0].getReal() < initial.getReal());
240         assertTrue(result[1].getReal() < initial.getReal());
241         assertTrue(fieldSin.value(result[0]).getReal() < 0);
242         assertTrue(fieldSin.value(result[1]).getReal() > 0);
243     }
244 
245     @Test
246     void testFieldBracketHigh(){
247         Binary64 initial = new Binary64(-0.5);
248         Binary64[] result = UnivariateSolverUtils.bracket(fieldSin, initial,
249                                                            new Binary64(-2.0), new Binary64(2.0),
250                                                            new Binary64(0.2), new Binary64(1.0),
251                                                            100);
252         assertTrue(result[0].getReal() > initial.getReal());
253         assertTrue(result[1].getReal() > initial.getReal());
254         assertTrue(fieldSin.value(result[0]).getReal() < 0);
255         assertTrue(fieldSin.value(result[1]).getReal() > 0);
256     }
257 
258     @Test
259     void testFieldBracketLinear(){
260         assertThrows(MathIllegalArgumentException.class, () -> {
261             UnivariateSolverUtils.bracket(new CalculusFieldUnivariateFunction<Binary64>() {
262                     public Binary64 value(Binary64 x) {
263                         return x.negate().add(1);
264                     }
265                 },
266                 new Binary64(1000),
267                 new Binary64(Double.NEGATIVE_INFINITY), new Binary64(Double.POSITIVE_INFINITY),
268                 new Binary64(1.0), new Binary64(1.0), 100);
269         });
270     }
271 
272     @Test
273     void testFieldBracketExponential(){
274         Binary64[] result = UnivariateSolverUtils.bracket(new CalculusFieldUnivariateFunction<Binary64>() {
275             public Binary64 value(Binary64 x) {
276                 return x.negate().add(1);
277             }
278         },
279         new Binary64(1000),
280         new Binary64(Double.NEGATIVE_INFINITY), new Binary64(Double.POSITIVE_INFINITY),
281         new Binary64(1.0), new Binary64(2.0), 10);
282         assertTrue(result[0].getReal() <= 1);
283         assertTrue(result[1].getReal() >= 1);
284     }
285 
286     @Test
287     void testFieldBracketEndpointRoot() {
288         Binary64[] result = UnivariateSolverUtils.bracket(fieldSin,
289                                                            new Binary64(1.5), new Binary64(0),
290                                                            new Binary64(2.0), 100);
291         assertEquals(0.0, fieldSin.value(result[0]).getReal(), 1.0e-15);
292         assertTrue(fieldSin.value(result[1]).getReal() > 0);
293     }
294 
295     @Test
296     void testFieldNullFunction() {
297         assertThrows(NullArgumentException.class, () -> {
298             UnivariateSolverUtils.bracket(null, new Binary64(1.5), new Binary64(0), new Binary64(2.0));
299         });
300     }
301 
302     @Test
303     void testFieldBadInitial() {
304         assertThrows(MathIllegalArgumentException.class, () -> {
305             UnivariateSolverUtils.bracket(fieldSin, new Binary64(2.5), new Binary64(0), new Binary64(2.0));
306         });
307     }
308 
309     @Test
310     void testFieldBadAdditive() {
311         assertThrows(MathIllegalArgumentException.class, () -> {
312             UnivariateSolverUtils.bracket(fieldSin, new Binary64(1.0), new Binary64(-2.0), new Binary64(3.0),
313                 new Binary64(-1.0), new Binary64(1.0), 100);
314         });
315     }
316 
317     @Test
318     void testFieldIterationExceeded() {
319         assertThrows(MathIllegalArgumentException.class, () -> {
320             UnivariateSolverUtils.bracket(fieldSin, new Binary64(1.0), new Binary64(-2.0), new Binary64(3.0),
321                 new Binary64(1.0e-5), new Binary64(1.0), 100);
322         });
323     }
324 
325     @Test
326     void testFieldBadEndpoints() {
327         assertThrows(MathIllegalArgumentException.class, () -> {
328             // endpoints not valid
329             UnivariateSolverUtils.bracket(fieldSin, new Binary64(1.5), new Binary64(2.0), new Binary64(1.0));
330         });
331     }
332 
333     @Test
334     void testFieldBadMaximumIterations() {
335         assertThrows(MathIllegalArgumentException.class, () -> {
336             // bad maximum iterations
337             UnivariateSolverUtils.bracket(fieldSin, new Binary64(1.5), new Binary64(0), new Binary64(2.0), 0);
338         });
339     }
340 
341     /** check the search continues when a = lowerBound and b &lt; upperBound. */
342     @Test
343     void testBracketLoopConditionForB() {
344         double[] result = UnivariateSolverUtils.bracket(sin, -0.9, -1, 1, 0.1, 1, 100);
345         assertTrue(result[0] <= 0);
346         assertTrue(result[1] >= 0);
347     }
348 
349     @Test
350     void testMisc() {
351         UnivariateFunction f = new QuinticFunction();
352         double result;
353         // Static solve method
354         result = UnivariateSolverUtils.solve(f, -0.2, 0.2);
355         assertEquals(0, result, 1E-8);
356         result = UnivariateSolverUtils.solve(f, -0.1, 0.3);
357         assertEquals(0, result, 1E-8);
358         result = UnivariateSolverUtils.solve(f, -0.3, 0.45);
359         assertEquals(0, result, 1E-6);
360         result = UnivariateSolverUtils.solve(f, 0.3, 0.7);
361         assertEquals(0.5, result, 1E-6);
362         result = UnivariateSolverUtils.solve(f, 0.2, 0.6);
363         assertEquals(0.5, result, 1E-6);
364         result = UnivariateSolverUtils.solve(f, 0.05, 0.95);
365         assertEquals(0.5, result, 1E-6);
366         result = UnivariateSolverUtils.solve(f, 0.85, 1.25);
367         assertEquals(1.0, result, 1E-6);
368         result = UnivariateSolverUtils.solve(f, 0.8, 1.2);
369         assertEquals(1.0, result, 1E-6);
370         result = UnivariateSolverUtils.solve(f, 0.85, 1.75);
371         assertEquals(1.0, result, 1E-6);
372         result = UnivariateSolverUtils.solve(f, 0.55, 1.45);
373         assertEquals(1.0, result, 1E-6);
374         result = UnivariateSolverUtils.solve(f, 0.85, 5);
375         assertEquals(1.0, result, 1E-6);
376     }
377 }