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.math3.analysis.integration;
018    
019    import org.apache.commons.math3.exception.MathIllegalArgumentException;
020    import org.apache.commons.math3.exception.MaxCountExceededException;
021    import org.apache.commons.math3.exception.NotStrictlyPositiveException;
022    import org.apache.commons.math3.exception.NumberIsTooSmallException;
023    import org.apache.commons.math3.exception.TooManyEvaluationsException;
024    import org.apache.commons.math3.exception.util.LocalizedFormats;
025    import org.apache.commons.math3.util.FastMath;
026    
027    /**
028     * Implements the <a href="http://mathworld.wolfram.com/Legendre-GaussQuadrature.html">
029     * Legendre-Gauss</a> quadrature formula.
030     * <p>
031     * Legendre-Gauss integrators are efficient integrators that can
032     * accurately integrate functions with few function evaluations. A
033     * Legendre-Gauss integrator using an n-points quadrature formula can
034     * integrate 2n-1 degree polynomials exactly.
035     * </p>
036     * <p>
037     * These integrators evaluate the function on n carefully chosen
038     * abscissas in each step interval (mapped to the canonical [-1,1] interval).
039     * The evaluation abscissas are not evenly spaced and none of them are
040     * at the interval endpoints. This implies the function integrated can be
041     * undefined at integration interval endpoints.
042     * </p>
043     * <p>
044     * The evaluation abscissas x<sub>i</sub> are the roots of the degree n
045     * Legendre polynomial. The weights a<sub>i</sub> of the quadrature formula
046     * integrals from -1 to +1 &int; Li<sup>2</sup> where Li (x) =
047     * &prod; (x-x<sub>k</sub>)/(x<sub>i</sub>-x<sub>k</sub>) for k != i.
048     * </p>
049     * <p>
050     * @version $Id: LegendreGaussIntegrator.java 1455194 2013-03-11 15:45:54Z luc $
051     * @since 1.2
052     * @deprecated As of 3.1 (to be removed in 4.0). Please use
053     * {@link IterativeLegendreGaussIntegrator} instead.
054     */
055    @Deprecated
056    public class LegendreGaussIntegrator extends BaseAbstractUnivariateIntegrator {
057    
058        /** Abscissas for the 2 points method. */
059        private static final double[] ABSCISSAS_2 = {
060            -1.0 / FastMath.sqrt(3.0),
061             1.0 / FastMath.sqrt(3.0)
062        };
063    
064        /** Weights for the 2 points method. */
065        private static final double[] WEIGHTS_2 = {
066            1.0,
067            1.0
068        };
069    
070        /** Abscissas for the 3 points method. */
071        private static final double[] ABSCISSAS_3 = {
072            -FastMath.sqrt(0.6),
073             0.0,
074             FastMath.sqrt(0.6)
075        };
076    
077        /** Weights for the 3 points method. */
078        private static final double[] WEIGHTS_3 = {
079            5.0 / 9.0,
080            8.0 / 9.0,
081            5.0 / 9.0
082        };
083    
084        /** Abscissas for the 4 points method. */
085        private static final double[] ABSCISSAS_4 = {
086            -FastMath.sqrt((15.0 + 2.0 * FastMath.sqrt(30.0)) / 35.0),
087            -FastMath.sqrt((15.0 - 2.0 * FastMath.sqrt(30.0)) / 35.0),
088             FastMath.sqrt((15.0 - 2.0 * FastMath.sqrt(30.0)) / 35.0),
089             FastMath.sqrt((15.0 + 2.0 * FastMath.sqrt(30.0)) / 35.0)
090        };
091    
092        /** Weights for the 4 points method. */
093        private static final double[] WEIGHTS_4 = {
094            (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0,
095            (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0,
096            (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0,
097            (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0
098        };
099    
100        /** Abscissas for the 5 points method. */
101        private static final double[] ABSCISSAS_5 = {
102            -FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0),
103            -FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0),
104             0.0,
105             FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0),
106             FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0)
107        };
108    
109        /** Weights for the 5 points method. */
110        private static final double[] WEIGHTS_5 = {
111            (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0,
112            (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0,
113            128.0 / 225.0,
114            (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0,
115            (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0
116        };
117    
118        /** Abscissas for the current method. */
119        private final double[] abscissas;
120    
121        /** Weights for the current method. */
122        private final double[] weights;
123    
124        /**
125         * Build a Legendre-Gauss integrator with given accuracies and iterations counts.
126         * @param n number of points desired (must be between 2 and 5 inclusive)
127         * @param relativeAccuracy relative accuracy of the result
128         * @param absoluteAccuracy absolute accuracy of the result
129         * @param minimalIterationCount minimum number of iterations
130         * @param maximalIterationCount maximum number of iterations
131         * @exception MathIllegalArgumentException if number of points is out of [2; 5]
132         * @exception NotStrictlyPositiveException if minimal number of iterations
133         * is not strictly positive
134         * @exception NumberIsTooSmallException if maximal number of iterations
135         * is lesser than or equal to the minimal number of iterations
136         */
137        public LegendreGaussIntegrator(final int n,
138                                       final double relativeAccuracy,
139                                       final double absoluteAccuracy,
140                                       final int minimalIterationCount,
141                                       final int maximalIterationCount)
142            throws MathIllegalArgumentException, NotStrictlyPositiveException, NumberIsTooSmallException {
143            super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
144            switch(n) {
145            case 2 :
146                abscissas = ABSCISSAS_2;
147                weights   = WEIGHTS_2;
148                break;
149            case 3 :
150                abscissas = ABSCISSAS_3;
151                weights   = WEIGHTS_3;
152                break;
153            case 4 :
154                abscissas = ABSCISSAS_4;
155                weights   = WEIGHTS_4;
156                break;
157            case 5 :
158                abscissas = ABSCISSAS_5;
159                weights   = WEIGHTS_5;
160                break;
161            default :
162                throw new MathIllegalArgumentException(
163                        LocalizedFormats.N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED,
164                        n, 2, 5);
165            }
166    
167        }
168    
169        /**
170         * Build a Legendre-Gauss integrator with given accuracies.
171         * @param n number of points desired (must be between 2 and 5 inclusive)
172         * @param relativeAccuracy relative accuracy of the result
173         * @param absoluteAccuracy absolute accuracy of the result
174         * @exception MathIllegalArgumentException if number of points is out of [2; 5]
175         */
176        public LegendreGaussIntegrator(final int n,
177                                       final double relativeAccuracy,
178                                       final double absoluteAccuracy)
179            throws MathIllegalArgumentException {
180            this(n, relativeAccuracy, absoluteAccuracy,
181                 DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
182        }
183    
184        /**
185         * Build a Legendre-Gauss integrator with given iteration counts.
186         * @param n number of points desired (must be between 2 and 5 inclusive)
187         * @param minimalIterationCount minimum number of iterations
188         * @param maximalIterationCount maximum number of iterations
189         * @exception MathIllegalArgumentException if number of points is out of [2; 5]
190         * @exception NotStrictlyPositiveException if minimal number of iterations
191         * is not strictly positive
192         * @exception NumberIsTooSmallException if maximal number of iterations
193         * is lesser than or equal to the minimal number of iterations
194         */
195        public LegendreGaussIntegrator(final int n,
196                                       final int minimalIterationCount,
197                                       final int maximalIterationCount)
198            throws MathIllegalArgumentException {
199            this(n, DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
200                 minimalIterationCount, maximalIterationCount);
201        }
202    
203        /** {@inheritDoc} */
204        @Override
205        protected double doIntegrate()
206            throws MathIllegalArgumentException, TooManyEvaluationsException, MaxCountExceededException {
207    
208            // compute first estimate with a single step
209            double oldt = stage(1);
210    
211            int n = 2;
212            while (true) {
213    
214                // improve integral with a larger number of steps
215                final double t = stage(n);
216    
217                // estimate error
218                final double delta = FastMath.abs(t - oldt);
219                final double limit =
220                    FastMath.max(getAbsoluteAccuracy(),
221                                 getRelativeAccuracy() * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5);
222    
223                // check convergence
224                if ((iterations.getCount() + 1 >= getMinimalIterationCount()) && (delta <= limit)) {
225                    return t;
226                }
227    
228                // prepare next iteration
229                double ratio = FastMath.min(4, FastMath.pow(delta / limit, 0.5 / abscissas.length));
230                n = FastMath.max((int) (ratio * n), n + 1);
231                oldt = t;
232                iterations.incrementCount();
233    
234            }
235    
236        }
237    
238        /**
239         * Compute the n-th stage integral.
240         * @param n number of steps
241         * @return the value of n-th stage integral
242         * @throws TooManyEvaluationsException if the maximum number of evaluations
243         * is exceeded.
244         */
245        private double stage(final int n)
246            throws TooManyEvaluationsException {
247    
248            // set up the step for the current stage
249            final double step     = (getMax() - getMin()) / n;
250            final double halfStep = step / 2.0;
251    
252            // integrate over all elementary steps
253            double midPoint = getMin() + halfStep;
254            double sum = 0.0;
255            for (int i = 0; i < n; ++i) {
256                for (int j = 0; j < abscissas.length; ++j) {
257                    sum += weights[j] * computeObjectiveValue(midPoint + halfStep * abscissas[j]);
258                }
259                midPoint += step;
260            }
261    
262            return halfStep * sum;
263    
264        }
265    
266    }