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.UnivariateFunction;
20  import org.apache.commons.math4.legacy.analysis.integration.gauss.GaussIntegrator;
21  import org.apache.commons.math4.legacy.analysis.integration.gauss.GaussIntegratorFactory;
22  import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
23  import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
24  import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
25  import org.apache.commons.math4.core.jdkmath.JdkMath;
26  
27  /**
28   * This algorithm divides the integration interval into equally-sized
29   * sub-interval and on each of them performs a
30   * <a href="http://mathworld.wolfram.com/Legendre-GaussQuadrature.html">
31   * Legendre-Gauss</a> quadrature.
32   * Because of its <em>non-adaptive</em> nature, this algorithm can
33   * converge to a wrong value for the integral (for example, if the
34   * function is significantly different from zero toward the ends of the
35   * integration interval).
36   * In particular, a change of variables aimed at estimating integrals
37   * over infinite intervals as proposed
38   * <a href="http://en.wikipedia.org/w/index.php?title=Numerical_integration#Integrals_over_infinite_intervals">
39   *  here</a> should be avoided when using this class.
40   *
41   * @since 3.1
42   */
43  
44  public class IterativeLegendreGaussIntegrator
45      extends BaseAbstractUnivariateIntegrator {
46      /** Factory that computes the points and weights. */
47      private static final GaussIntegratorFactory FACTORY
48          = new GaussIntegratorFactory();
49      /** Number of integration points (per interval). */
50      private final int numberOfPoints;
51  
52      /**
53       * Builds an integrator with given accuracies and iterations counts.
54       *
55       * @param n Number of integration points.
56       * @param relativeAccuracy Relative accuracy of the result.
57       * @param absoluteAccuracy Absolute accuracy of the result.
58       * @param minimalIterationCount Minimum number of iterations.
59       * @param maximalIterationCount Maximum number of iterations.
60       * @throws NotStrictlyPositiveException if minimal number of iterations
61       * or number of points are not strictly positive.
62       * @throws org.apache.commons.math4.legacy.exception.NumberIsTooSmallException
63       * if the maximal number of iterations is smaller than or equal to the
64       * minimal number of iterations.
65       */
66      public IterativeLegendreGaussIntegrator(final int n,
67                                              final double relativeAccuracy,
68                                              final double absoluteAccuracy,
69                                              final int minimalIterationCount,
70                                              final int maximalIterationCount) {
71          super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
72          if (n <= 0) {
73              throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_POINTS, n);
74          }
75         numberOfPoints = n;
76      }
77  
78      /**
79       * Builds an integrator with given accuracies.
80       *
81       * @param n Number of integration points.
82       * @param relativeAccuracy Relative accuracy of the result.
83       * @param absoluteAccuracy Absolute accuracy of the result.
84       * @throws NotStrictlyPositiveException if {@code n < 1}.
85       */
86      public IterativeLegendreGaussIntegrator(final int n,
87                                              final double relativeAccuracy,
88                                              final double absoluteAccuracy) {
89          this(n, relativeAccuracy, absoluteAccuracy,
90               DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
91      }
92  
93      /**
94       * Builds an integrator with given iteration counts.
95       *
96       * @param n Number of integration points.
97       * @param minimalIterationCount Minimum number of iterations.
98       * @param maximalIterationCount Maximum number of iterations.
99       * @throws NotStrictlyPositiveException if minimal number of iterations
100      * is not strictly positive.
101      * @throws org.apache.commons.math4.legacy.exception.NumberIsTooSmallException
102      * if the maximal number of iterations is smaller than or equal to the
103      * minimal number of iterations.
104      * @throws NotStrictlyPositiveException if {@code n < 1}.
105      */
106     public IterativeLegendreGaussIntegrator(final int n,
107                                             final int minimalIterationCount,
108                                             final int maximalIterationCount) {
109         this(n, DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
110              minimalIterationCount, maximalIterationCount);
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     protected double doIntegrate() {
116         // Compute first estimate with a single step.
117         double oldt = stage(1);
118 
119         int n = 2;
120         while (true) {
121             // Improve integral with a larger number of steps.
122             final double t = stage(n);
123 
124             // Estimate the error.
125             final double delta = JdkMath.abs(t - oldt);
126             final double limit =
127                 JdkMath.max(getAbsoluteAccuracy(),
128                              getRelativeAccuracy() * (JdkMath.abs(oldt) + JdkMath.abs(t)) * 0.5);
129 
130             // check convergence
131             if (iterations.getCount() + 1 >= getMinimalIterationCount() &&
132                 delta <= limit) {
133                 return t;
134             }
135 
136             // Prepare next iteration.
137             final double ratio = JdkMath.min(4, JdkMath.pow(delta / limit, 0.5 / numberOfPoints));
138             n = JdkMath.max((int) (ratio * n), n + 1);
139             oldt = t;
140             iterations.increment();
141         }
142     }
143 
144     /**
145      * Compute the n-th stage integral.
146      *
147      * @param n Number of steps.
148      * @return the value of n-th stage integral.
149      * @throws TooManyEvaluationsException if the maximum number of evaluations
150      * is exceeded.
151      */
152     private double stage(final int n)
153         throws TooManyEvaluationsException {
154         // Function to be integrated is stored in the base class.
155         final UnivariateFunction f = new UnivariateFunction() {
156                 /** {@inheritDoc} */
157                 @Override
158                 public double value(double x) {
159                     return computeObjectiveValue(x);
160                 }
161             };
162 
163         final double min = getMin();
164         final double max = getMax();
165         final double step = (max - min) / n;
166 
167         double sum = 0;
168         for (int i = 0; i < n; i++) {
169             // Integrate over each sub-interval [a, b].
170             final double a = min + i * step;
171             final double b = a + step;
172             final GaussIntegrator g = FACTORY.legendreHighPrecision(numberOfPoints, a, b);
173             sum += g.integrate(f);
174         }
175 
176         return sum;
177     }
178 }