001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.math.analysis.solvers;
018    
019    import org.apache.commons.math.MathException;
020    import org.apache.commons.math.analysis.Expm1Function;
021    import org.apache.commons.math.analysis.QuinticFunction;
022    import org.apache.commons.math.analysis.SinFunction;
023    import org.apache.commons.math.analysis.UnivariateRealFunction;
024    
025    import junit.framework.TestCase;
026    
027    /**
028     * Testcase for Muller solver.
029     * <p>
030     * Muller's method converges almost quadratically near roots, but it can
031     * be very slow in regions far away from zeros. Test runs show that for
032     * reasonably good initial values, for a default absolute accuracy of 1E-6,
033     * it generally takes 5 to 10 iterations for the solver to converge.
034     * <p>
035     * Tests for the exponential function illustrate the situations where
036     * Muller solver performs poorly.
037     * 
038     * @version $Revision: 799857 $ $Date: 2009-08-01 09:07:12 -0400 (Sat, 01 Aug 2009) $ 
039     */
040    public final class MullerSolverTest extends TestCase {
041    
042        /**
043         * Test deprecated APIs.
044         */
045        @Deprecated
046        public void testDeprecated() throws MathException {
047            UnivariateRealFunction f = new SinFunction();
048            UnivariateRealSolver solver = new MullerSolver(f);
049            double min, max, expected, result, tolerance;
050    
051            min = 3.0; max = 4.0; expected = Math.PI;
052            tolerance = Math.max(solver.getAbsoluteAccuracy(),
053                        Math.abs(expected * solver.getRelativeAccuracy()));
054            result = solver.solve(min, max);
055            assertEquals(expected, result, tolerance);
056    
057            min = -1.0; max = 1.5; expected = 0.0;
058            tolerance = Math.max(solver.getAbsoluteAccuracy(),
059                        Math.abs(expected * solver.getRelativeAccuracy()));
060            result = solver.solve(min, max);
061            assertEquals(expected, result, tolerance);
062        }
063    
064        /**
065         * Test deprecated APIs.
066         */
067        @Deprecated
068        public void testDeprecated2() throws MathException {
069            UnivariateRealFunction f = new QuinticFunction();
070            MullerSolver solver = new MullerSolver(f);
071            double min, max, expected, result, tolerance;
072    
073            min = -0.4; max = 0.2; expected = 0.0;
074            tolerance = Math.max(solver.getAbsoluteAccuracy(),
075                        Math.abs(expected * solver.getRelativeAccuracy()));
076            result = solver.solve2(min, max);
077            assertEquals(expected, result, tolerance);
078    
079            min = 0.75; max = 1.5; expected = 1.0;
080            tolerance = Math.max(solver.getAbsoluteAccuracy(),
081                        Math.abs(expected * solver.getRelativeAccuracy()));
082            result = solver.solve2(min, max);
083            assertEquals(expected, result, tolerance);
084    
085            min = -0.9; max = -0.2; expected = -0.5;
086            tolerance = Math.max(solver.getAbsoluteAccuracy(),
087                        Math.abs(expected * solver.getRelativeAccuracy()));
088            result = solver.solve2(min, max);
089            assertEquals(expected, result, tolerance);
090        }
091    
092        /**
093         * Test of solver for the sine function.
094         */
095        public void testSinFunction() throws MathException {
096            UnivariateRealFunction f = new SinFunction();
097            UnivariateRealSolver solver = new MullerSolver();
098            double min, max, expected, result, tolerance;
099    
100            min = 3.0; max = 4.0; expected = Math.PI;
101            tolerance = Math.max(solver.getAbsoluteAccuracy(),
102                        Math.abs(expected * solver.getRelativeAccuracy()));
103            result = solver.solve(f, min, max);
104            assertEquals(expected, result, tolerance);
105    
106            min = -1.0; max = 1.5; expected = 0.0;
107            tolerance = Math.max(solver.getAbsoluteAccuracy(),
108                        Math.abs(expected * solver.getRelativeAccuracy()));
109            result = solver.solve(f, min, max);
110            assertEquals(expected, result, tolerance);
111        }
112    
113        /**
114         * Test of solver for the sine function using solve2().
115         */
116        public void testSinFunction2() throws MathException {
117            UnivariateRealFunction f = new SinFunction();
118            MullerSolver solver = new MullerSolver();
119            double min, max, expected, result, tolerance;
120    
121            min = 3.0; max = 4.0; expected = Math.PI;
122            tolerance = Math.max(solver.getAbsoluteAccuracy(),
123                        Math.abs(expected * solver.getRelativeAccuracy()));
124            result = solver.solve2(f, min, max);
125            assertEquals(expected, result, tolerance);
126    
127            min = -1.0; max = 1.5; expected = 0.0;
128            tolerance = Math.max(solver.getAbsoluteAccuracy(),
129                        Math.abs(expected * solver.getRelativeAccuracy()));
130            result = solver.solve2(f, min, max);
131            assertEquals(expected, result, tolerance);
132        }
133    
134        /**
135         * Test of solver for the quintic function.
136         */
137        public void testQuinticFunction() throws MathException {
138            UnivariateRealFunction f = new QuinticFunction();
139            UnivariateRealSolver solver = new MullerSolver();
140            double min, max, expected, result, tolerance;
141    
142            min = -0.4; max = 0.2; expected = 0.0;
143            tolerance = Math.max(solver.getAbsoluteAccuracy(),
144                        Math.abs(expected * solver.getRelativeAccuracy()));
145            result = solver.solve(f, min, max);
146            assertEquals(expected, result, tolerance);
147    
148            min = 0.75; max = 1.5; expected = 1.0;
149            tolerance = Math.max(solver.getAbsoluteAccuracy(),
150                        Math.abs(expected * solver.getRelativeAccuracy()));
151            result = solver.solve(f, min, max);
152            assertEquals(expected, result, tolerance);
153    
154            min = -0.9; max = -0.2; expected = -0.5;
155            tolerance = Math.max(solver.getAbsoluteAccuracy(),
156                        Math.abs(expected * solver.getRelativeAccuracy()));
157            result = solver.solve(f, min, max);
158            assertEquals(expected, result, tolerance);
159        }
160    
161        /**
162         * Test of solver for the quintic function using solve2().
163         */
164        public void testQuinticFunction2() throws MathException {
165            UnivariateRealFunction f = new QuinticFunction();
166            MullerSolver solver = new MullerSolver();
167            double min, max, expected, result, tolerance;
168    
169            min = -0.4; max = 0.2; expected = 0.0;
170            tolerance = Math.max(solver.getAbsoluteAccuracy(),
171                        Math.abs(expected * solver.getRelativeAccuracy()));
172            result = solver.solve2(f, min, max);
173            assertEquals(expected, result, tolerance);
174    
175            min = 0.75; max = 1.5; expected = 1.0;
176            tolerance = Math.max(solver.getAbsoluteAccuracy(),
177                        Math.abs(expected * solver.getRelativeAccuracy()));
178            result = solver.solve2(f, min, max);
179            assertEquals(expected, result, tolerance);
180    
181            min = -0.9; max = -0.2; expected = -0.5;
182            tolerance = Math.max(solver.getAbsoluteAccuracy(),
183                        Math.abs(expected * solver.getRelativeAccuracy()));
184            result = solver.solve2(f, min, max);
185            assertEquals(expected, result, tolerance);
186        }
187    
188        /**
189         * Test of solver for the exponential function.
190         * <p>
191         * It takes 10 to 15 iterations for the last two tests to converge.
192         * In fact, if not for the bisection alternative, the solver would
193         * exceed the default maximal iteration of 100.
194         */
195        public void testExpm1Function() throws MathException {
196            UnivariateRealFunction f = new Expm1Function();
197            UnivariateRealSolver solver = new MullerSolver();
198            double min, max, expected, result, tolerance;
199    
200            min = -1.0; max = 2.0; expected = 0.0;
201            tolerance = Math.max(solver.getAbsoluteAccuracy(),
202                        Math.abs(expected * solver.getRelativeAccuracy()));
203            result = solver.solve(f, min, max);
204            assertEquals(expected, result, tolerance);
205    
206            min = -20.0; max = 10.0; expected = 0.0;
207            tolerance = Math.max(solver.getAbsoluteAccuracy(),
208                        Math.abs(expected * solver.getRelativeAccuracy()));
209            result = solver.solve(f, min, max);
210            assertEquals(expected, result, tolerance);
211    
212            min = -50.0; max = 100.0; expected = 0.0;
213            tolerance = Math.max(solver.getAbsoluteAccuracy(),
214                        Math.abs(expected * solver.getRelativeAccuracy()));
215            result = solver.solve(f, min, max);
216            assertEquals(expected, result, tolerance);
217        }
218    
219        /**
220         * Test of solver for the exponential function using solve2().
221         * <p>
222         * It takes 25 to 50 iterations for the last two tests to converge.
223         */
224        public void testExpm1Function2() throws MathException {
225            UnivariateRealFunction f = new Expm1Function();
226            MullerSolver solver = new MullerSolver();
227            double min, max, expected, result, tolerance;
228    
229            min = -1.0; max = 2.0; expected = 0.0;
230            tolerance = Math.max(solver.getAbsoluteAccuracy(),
231                        Math.abs(expected * solver.getRelativeAccuracy()));
232            result = solver.solve2(f, min, max);
233            assertEquals(expected, result, tolerance);
234    
235            min = -20.0; max = 10.0; expected = 0.0;
236            tolerance = Math.max(solver.getAbsoluteAccuracy(),
237                        Math.abs(expected * solver.getRelativeAccuracy()));
238            result = solver.solve2(f, min, max);
239            assertEquals(expected, result, tolerance);
240    
241            min = -50.0; max = 100.0; expected = 0.0;
242            tolerance = Math.max(solver.getAbsoluteAccuracy(),
243                        Math.abs(expected * solver.getRelativeAccuracy()));
244            result = solver.solve2(f, min, max);
245            assertEquals(expected, result, tolerance);
246        }
247    
248        /**
249         * Test of parameters for the solver.
250         */
251        public void testParameters() throws Exception {
252            UnivariateRealFunction f = new SinFunction();
253            UnivariateRealSolver solver = new MullerSolver();
254    
255            try {
256                // bad interval
257                solver.solve(f, 1, -1);
258                fail("Expecting IllegalArgumentException - bad interval");
259            } catch (IllegalArgumentException ex) {
260                // expected
261            }
262            try {
263                // no bracketing
264                solver.solve(f, 2, 3);
265                fail("Expecting IllegalArgumentException - no bracketing");
266            } catch (IllegalArgumentException ex) {
267                // expected
268            }
269        }
270    }