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 }