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    *      http://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  package org.apache.commons.math.analysis;
18  
19  import org.apache.commons.math.MathException;
20  
21  import junit.framework.Test;
22  import junit.framework.TestCase;
23  import junit.framework.TestSuite;
24  
25  /**
26   * Testcase for UnivariateRealSolver.
27   * Because Brent-Dekker is guaranteed to converge in less than the default
28   * maximum iteration count due to bisection fallback, it is quite hard to
29   * debug. I include measured iteration counts plus one in order to detect
30   * regressions. On average Brent-Dekker should use 4..5 iterations for the
31   * default absolute accuracy of 10E-8 for sinus and the quintic function around
32   * zero, and 5..10 iterations for the other zeros.
33   * 
34   * @version $Revision: 536283 $ $Date: 2007-05-08 11:16:52 -0700 (Tue, 08 May 2007) $ 
35   */
36  public final class BrentSolverTest extends TestCase {
37  
38      public BrentSolverTest(String name) {
39          super(name);
40      }
41  
42      public static Test suite() {
43          TestSuite suite = new TestSuite(BrentSolverTest.class);
44          suite.setName("UnivariateRealSolver Tests");
45          return suite;
46      }
47  
48      public void testSinZero() throws MathException {
49          // The sinus function is behaved well around the root at #pi. The second
50          // order derivative is zero, which means linar approximating methods will
51          // still converge quadratically. 
52          UnivariateRealFunction f = new SinFunction();
53          double result;
54          UnivariateRealSolver solver = new BrentSolver(f);
55          // Somewhat benign interval. The function is monotone.
56          result = solver.solve(3, 4);
57          //System.out.println(
58          //    "Root: " + result + " Iterations: " + solver.getIterationCount());
59          assertEquals(result, Math.PI, solver.getAbsoluteAccuracy());
60          // 4 iterations on i586 JDK 1.4.1.
61          assertTrue(solver.getIterationCount() <= 5);
62          // Larger and somewhat less benign interval. The function is grows first.
63          result = solver.solve(1, 4);
64          //System.out.println(
65          //    "Root: " + result + " Iterations: " + solver.getIterationCount());
66          assertEquals(result, Math.PI, solver.getAbsoluteAccuracy());
67          // 5 iterations on i586 JDK 1.4.1.
68          assertTrue(solver.getIterationCount() <= 6);
69          solver = new SecantSolver(f);
70          result = solver.solve(3, 4);
71          //System.out.println(
72          //    "Root: " + result + " Iterations: " + solver.getIterationCount());
73          assertEquals(result, Math.PI, solver.getAbsoluteAccuracy());
74          // 4 iterations on i586 JDK 1.4.1.
75          assertTrue(solver.getIterationCount() <= 5);
76          result = solver.solve(1, 4);
77          //System.out.println(
78          //    "Root: " + result + " Iterations: " + solver.getIterationCount());
79          assertEquals(result, Math.PI, solver.getAbsoluteAccuracy());
80          // 5 iterations on i586 JDK 1.4.1.
81          assertTrue(solver.getIterationCount() <= 6);
82          assertEquals(result, solver.getResult(), 0);
83      }
84  
85      public void testQuinticZero() throws MathException {
86          // The quintic function has zeros at 0, +-0.5 and +-1.
87          // Around the root of 0 the function is well behaved, with a second derivative
88          // of zero a 0.
89          // The other roots are less well to find, in particular the root at 1, because
90          // the function grows fast for x>1.
91          // The function has extrema (first derivative is zero) at 0.27195613 and 0.82221643,
92          // intervals containing these values are harder for the solvers.
93          UnivariateRealFunction f = new QuinticFunction();
94          double result;
95          // Brent-Dekker solver.
96          UnivariateRealSolver solver = new BrentSolver(f);
97          // Symmetric bracket around 0. Test whether solvers can handle hitting
98          // the root in the first iteration.
99          result = solver.solve(-0.2, 0.2);
100         //System.out.println(
101         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
102         assertEquals(result, 0, solver.getAbsoluteAccuracy());
103         assertTrue(solver.getIterationCount() <= 2);
104         // 1 iterations on i586 JDK 1.4.1.
105         // Asymmetric bracket around 0, just for fun. Contains extremum.
106         result = solver.solve(-0.1, 0.3);
107         //System.out.println(
108         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
109         assertEquals(result, 0, solver.getAbsoluteAccuracy());
110         // 5 iterations on i586 JDK 1.4.1.
111         assertTrue(solver.getIterationCount() <= 6);
112         // Large bracket around 0. Contains two extrema.
113         result = solver.solve(-0.3, 0.45);
114         //System.out.println(
115         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
116         assertEquals(result, 0, solver.getAbsoluteAccuracy());
117         // 6 iterations on i586 JDK 1.4.1.
118         assertTrue(solver.getIterationCount() <= 7);
119         // Benign bracket around 0.5, function is monotonous.
120         result = solver.solve(0.3, 0.7);
121         //System.out.println(
122         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
123         assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
124         // 6 iterations on i586 JDK 1.4.1.
125         assertTrue(solver.getIterationCount() <= 7);
126         // Less benign bracket around 0.5, contains one extremum.
127         result = solver.solve(0.2, 0.6);
128         //System.out.println(
129         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
130         assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
131         // 6 iterations on i586 JDK 1.4.1.
132         assertTrue(solver.getIterationCount() <= 7);
133         // Large, less benign bracket around 0.5, contains both extrema.
134         result = solver.solve(0.05, 0.95);
135         //System.out.println(
136         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
137         assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
138         // 8 iterations on i586 JDK 1.4.1.
139         assertTrue(solver.getIterationCount() <= 9);
140         // Relatively benign bracket around 1, function is monotonous. Fast growth for x>1
141         // is still a problem.
142         result = solver.solve(0.85, 1.25);
143         //System.out.println(
144         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
145         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
146         // 8 iterations on i586 JDK 1.4.1.
147         assertTrue(solver.getIterationCount() <= 9);
148         // Less benign bracket around 1 with extremum.
149         result = solver.solve(0.8, 1.2);
150         //System.out.println(
151         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
152         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
153         // 8 iterations on i586 JDK 1.4.1.
154         assertTrue(solver.getIterationCount() <= 9);
155         // Large bracket around 1. Monotonous.
156         result = solver.solve(0.85, 1.75);
157         //System.out.println(
158         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
159         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
160         // 10 iterations on i586 JDK 1.4.1.
161         assertTrue(solver.getIterationCount() <= 11);
162         // Large bracket around 1. Interval contains extremum.
163         result = solver.solve(0.55, 1.45);
164         //System.out.println(
165         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
166         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
167         // 7 iterations on i586 JDK 1.4.1.
168         assertTrue(solver.getIterationCount() <= 8);
169         // Very large bracket around 1 for testing fast growth behaviour.
170         result = solver.solve(0.85, 5);
171         //System.out.println(
172        //     "Root: " + result + " Iterations: " + solver.getIterationCount());
173         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
174         // 12 iterations on i586 JDK 1.4.1.
175         assertTrue(solver.getIterationCount() <= 13);
176         // Secant solver.
177         solver = new SecantSolver(f);
178         result = solver.solve(-0.2, 0.2);
179         //System.out.println(
180         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
181         assertEquals(result, 0, solver.getAbsoluteAccuracy());
182         // 1 iterations on i586 JDK 1.4.1.
183         assertTrue(solver.getIterationCount() <= 2);
184         result = solver.solve(-0.1, 0.3);
185         //System.out.println(
186         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
187         assertEquals(result, 0, solver.getAbsoluteAccuracy());
188         // 5 iterations on i586 JDK 1.4.1.
189         assertTrue(solver.getIterationCount() <= 6);
190         result = solver.solve(-0.3, 0.45);
191         //System.out.println(
192         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
193         assertEquals(result, 0, solver.getAbsoluteAccuracy());
194         // 6 iterations on i586 JDK 1.4.1.
195         assertTrue(solver.getIterationCount() <= 7);
196         result = solver.solve(0.3, 0.7);
197         //System.out.println(
198         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
199         assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
200         // 7 iterations on i586 JDK 1.4.1.
201         assertTrue(solver.getIterationCount() <= 8);
202         result = solver.solve(0.2, 0.6);
203         //System.out.println(
204         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
205         assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
206         // 6 iterations on i586 JDK 1.4.1.
207         assertTrue(solver.getIterationCount() <= 7);
208         result = solver.solve(0.05, 0.95);
209         //System.out.println(
210         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
211         assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
212         // 8 iterations on i586 JDK 1.4.1.
213         assertTrue(solver.getIterationCount() <= 9);
214         result = solver.solve(0.85, 1.25);
215         //System.out.println(
216         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
217         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
218         // 10 iterations on i586 JDK 1.4.1.
219         assertTrue(solver.getIterationCount() <= 11);
220         result = solver.solve(0.8, 1.2);
221         //System.out.println(
222         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
223         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
224         // 8 iterations on i586 JDK 1.4.1.
225         assertTrue(solver.getIterationCount() <= 9);
226         result = solver.solve(0.85, 1.75);
227         //System.out.println(
228         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
229         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
230         // 14 iterations on i586 JDK 1.4.1.
231         assertTrue(solver.getIterationCount() <= 15);
232         // The followig is especially slow because the solver first has to reduce
233         // the bracket to exclude the extremum. After that, convergence is rapide.
234         result = solver.solve(0.55, 1.45);
235         //System.out.println(
236         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
237         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
238         // 7 iterations on i586 JDK 1.4.1.
239         assertTrue(solver.getIterationCount() <= 8);
240         result = solver.solve(0.85, 5);
241         //System.out.println(
242         //    "Root: " + result + " Iterations: " + solver.getIterationCount());
243         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
244         // 14 iterations on i586 JDK 1.4.1.
245         assertTrue(solver.getIterationCount() <= 15);
246         // Static solve method
247         result = UnivariateRealSolverUtils.solve(f, -0.2, 0.2);
248         assertEquals(result, 0, solver.getAbsoluteAccuracy());
249         result = UnivariateRealSolverUtils.solve(f, -0.1, 0.3);
250         assertEquals(result, 0, 1E-8);
251         result = UnivariateRealSolverUtils.solve(f, -0.3, 0.45);
252         assertEquals(result, 0, 1E-6);
253         result = UnivariateRealSolverUtils.solve(f, 0.3, 0.7);
254         assertEquals(result, 0.5, 1E-6);
255         result = UnivariateRealSolverUtils.solve(f, 0.2, 0.6);
256         assertEquals(result, 0.5, 1E-6);
257         result = UnivariateRealSolverUtils.solve(f, 0.05, 0.95);
258         assertEquals(result, 0.5, 1E-6);
259         result = UnivariateRealSolverUtils.solve(f, 0.85, 1.25);
260         assertEquals(result, 1.0, 1E-6);
261         result = UnivariateRealSolverUtils.solve(f, 0.8, 1.2);
262         assertEquals(result, 1.0, 1E-6);
263         result = UnivariateRealSolverUtils.solve(f, 0.85, 1.75);
264         assertEquals(result, 1.0, 1E-6);
265         result = UnivariateRealSolverUtils.solve(f, 0.55, 1.45);
266         assertEquals(result, 1.0, 1E-6);
267         result = UnivariateRealSolverUtils.solve(f, 0.85, 5);
268         assertEquals(result, 1.0, 1E-6);
269     }
270     
271     public void testBadEndpoints() throws Exception {
272         UnivariateRealFunction f = new SinFunction();
273         UnivariateRealSolver solver = new BrentSolver(f);
274         try {  // bad interval
275             solver.solve(1, -1);
276             fail("Expecting IllegalArgumentException - bad interval");
277         } catch (IllegalArgumentException ex) {
278             // expected
279         }
280         try {  // no bracket
281             solver.solve(1, 1.5);
282             fail("Expecting IllegalArgumentException - non-bracketing");
283         } catch (IllegalArgumentException ex) {
284             // expected
285         }
286     }
287 
288     public void testInitialGuess() throws MathException {
289 
290         MonitoredFunction f = new MonitoredFunction(new QuinticFunction());
291         UnivariateRealSolver solver = new BrentSolver(f);
292         double result;
293 
294         // no guess
295         result = solver.solve(0.6, 7.0);
296         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
297         int referenceCallsCount = f.getCallsCount();
298         assertTrue(referenceCallsCount >= 13);
299  
300         // invalid guess (it *is* a root, but outside of the range)
301         try {
302           result = solver.solve(0.6, 7.0, 0.0);
303           fail("an IllegalArgumentException was expected");
304         } catch (IllegalArgumentException iae) {
305             // expected behaviour
306         } catch (Exception e) {
307             fail("wrong exception caught: " + e.getMessage());
308         }
309  
310         // bad guess
311         f.setCallsCount(0);
312         result = solver.solve(0.6, 7.0, 0.61);
313         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
314         assertTrue(f.getCallsCount() > referenceCallsCount);
315  
316         // good guess
317         f.setCallsCount(0);
318         result = solver.solve(0.6, 7.0, 0.999999);
319         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
320         assertTrue(f.getCallsCount() < referenceCallsCount);
321 
322         // perfect guess
323         f.setCallsCount(0);
324         result = solver.solve(0.6, 7.0, 1.0);
325         assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
326         assertEquals(0, solver.getIterationCount());
327         assertEquals(1, f.getCallsCount());
328  
329     }
330     
331 }