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    *      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.math4.legacy.analysis.integration;
18  
19  import org.apache.commons.math4.legacy.analysis.QuinticFunction;
20  import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
21  import org.apache.commons.math4.legacy.analysis.function.Sin;
22  import org.apache.commons.math4.legacy.exception.NumberIsTooLargeException;
23  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
24  import org.apache.commons.math4.core.jdkmath.JdkMath;
25  import org.junit.Assert;
26  import org.junit.Test;
27  
28  /**
29   * Test case for midpoint integrator.
30   * <p>
31   * Test runs show that for a default relative accuracy of 1E-6, it generally
32   * takes 10 to 15 iterations for the integral to converge.
33   *
34   */
35  public final class MidPointIntegratorTest {
36      private static final int NUM_ITER = 30;
37  
38      /**
39       * The initial iteration contributes 1 evaluation. Each successive iteration
40       * contributes 2 points to each previous slice.
41       *
42       * The total evaluation count == 1 + 2*3^0 + 2*3^1 + ... 2*3^n
43       *
44       * the series 3^0 + 3^1 + ... + 3^n sums to 3^(n-1) / (3-1), so the total
45       * expected evaluations == 1 + 2*(3^(n-1) - 1)/2 == 3^(n-1).
46       *
47       * The n in the series above is offset by 1 from the MidPointIntegrator
48       * iteration count so the actual result == 3^n.
49       *
50       * Without the incremental implementation, the same result would require
51       * (3^(n + 1) - 1) / 2 evaluations; just under 50% more.
52       */
53      private long expectedEvaluations(int iterations) {
54          return (long) JdkMath.pow(3, iterations);
55      }
56  
57      /**
58       * Test of integrator for the sine function.
59       */
60      @Test
61      public void testLowAccuracy() {
62          UnivariateFunction f = new QuinticFunction();
63          UnivariateIntegrator integrator = new MidPointIntegrator(0.01, 1.0e-10, 2, 4);
64  
65          double min = -10;
66          double max =  -9;
67          double expected = -3697001.0 / 48.0;
68          double tolerance = JdkMath.abs(expected * integrator.getRelativeAccuracy());
69          double result = integrator.integrate(Integer.MAX_VALUE, f, min, max);
70          Assert.assertTrue(integrator.getEvaluations() < Integer.MAX_VALUE / 3);
71          Assert.assertTrue(integrator.getIterations() < NUM_ITER);
72          Assert.assertEquals(expectedEvaluations(integrator.getIterations()), integrator.getEvaluations());
73          Assert.assertEquals(expected, result, tolerance);
74      }
75  
76      /**
77       * Test of integrator for the sine function.
78       */
79      @Test
80      public void testSinFunction() {
81          UnivariateFunction f = new Sin();
82          UnivariateIntegrator integrator = new MidPointIntegrator();
83  
84          double min = 0;
85          double max = JdkMath.PI;
86          double expected = 2;
87          double tolerance = JdkMath.abs(expected * integrator.getRelativeAccuracy());
88          double result = integrator.integrate(Integer.MAX_VALUE, f, min, max);
89          Assert.assertTrue(integrator.getEvaluations() < Integer.MAX_VALUE / 3);
90          Assert.assertTrue(integrator.getIterations() < NUM_ITER);
91          Assert.assertEquals(expectedEvaluations(integrator.getIterations()), integrator.getEvaluations());
92          Assert.assertEquals(expected, result, tolerance);
93  
94          min = -JdkMath.PI/3;
95          max = 0;
96          expected = -0.5;
97          tolerance = JdkMath.abs(expected * integrator.getRelativeAccuracy());
98          result = integrator.integrate(Integer.MAX_VALUE, f, min, max);
99          Assert.assertTrue(integrator.getEvaluations() < Integer.MAX_VALUE / 3);
100         Assert.assertTrue(integrator.getIterations() < NUM_ITER);
101         Assert.assertEquals(expectedEvaluations(integrator.getIterations()), integrator.getEvaluations());
102         Assert.assertEquals(expected, result, tolerance);
103     }
104 
105     /**
106      * Test of integrator for the quintic function.
107      */
108     @Test
109     public void testQuinticFunction() {
110         UnivariateFunction f = new QuinticFunction();
111         UnivariateIntegrator integrator = new MidPointIntegrator();
112 
113         double min = 0;
114         double max = 1;
115         double expected = -1.0 / 48;
116         double tolerance = JdkMath.abs(expected * integrator.getRelativeAccuracy());
117         double result = integrator.integrate(Integer.MAX_VALUE, f, min, max);
118         Assert.assertTrue(integrator.getEvaluations() < Integer.MAX_VALUE / 3);
119         Assert.assertTrue(integrator.getIterations() < NUM_ITER);
120         Assert.assertEquals(expectedEvaluations(integrator.getIterations()), integrator.getEvaluations());
121         Assert.assertEquals(expected, result, tolerance);
122 
123         min = 0;
124         max = 0.5;
125         expected = 11.0 / 768;
126         tolerance = JdkMath.abs(expected * integrator.getRelativeAccuracy());
127         result = integrator.integrate(Integer.MAX_VALUE, f, min, max);
128         Assert.assertTrue(integrator.getEvaluations() < Integer.MAX_VALUE / 3);
129         Assert.assertTrue(integrator.getIterations() < NUM_ITER);
130         Assert.assertEquals(expected, result, tolerance);
131 
132         min = -1;
133         max = 4;
134         expected = 2048 / 3.0 - 78 + 1.0 / 48;
135         tolerance = JdkMath.abs(expected * integrator.getRelativeAccuracy());
136         result = integrator.integrate(Integer.MAX_VALUE, f, min, max);
137         Assert.assertTrue(integrator.getEvaluations() < Integer.MAX_VALUE / 3);
138         Assert.assertTrue(integrator.getIterations() < NUM_ITER);
139         Assert.assertEquals(expectedEvaluations(integrator.getIterations()), integrator.getEvaluations());
140         Assert.assertEquals(expected, result, tolerance);
141     }
142 
143     /**
144      * Test of parameters for the integrator.
145      */
146     @Test
147     public void testParameters() {
148         UnivariateFunction f = new Sin();
149 
150         try {
151             // bad interval
152             new MidPointIntegrator().integrate(1000, f, 1, -1);
153             Assert.fail("Expecting NumberIsTooLargeException - bad interval");
154         } catch (NumberIsTooLargeException ex) {
155             // expected
156         }
157         try {
158             // bad iteration limits
159             new MidPointIntegrator(5, 4);
160             Assert.fail("Expecting NumberIsTooSmallException - bad iteration limits");
161         } catch (NumberIsTooSmallException ex) {
162             // expected
163         }
164         try {
165             // bad iteration limits
166             new MidPointIntegrator(10, 99);
167             Assert.fail("Expecting NumberIsTooLargeException - bad iteration limits");
168         } catch (NumberIsTooLargeException ex) {
169             // expected
170         }
171     }
172 }