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 }