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.integration;
018    
019    import org.apache.commons.math.exception.MathIllegalArgumentException;
020    import org.apache.commons.math.exception.MaxCountExceededException;
021    import org.apache.commons.math.exception.NotStrictlyPositiveException;
022    import org.apache.commons.math.exception.NumberIsTooSmallException;
023    import org.apache.commons.math.exception.TooManyEvaluationsException;
024    import org.apache.commons.math.exception.util.LocalizedFormats;
025    import org.apache.commons.math.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 1179926 2011-10-07 03:18:05Z psteitz $
051     * @since 1.2
052     */
053    
054    public class LegendreGaussIntegrator extends UnivariateRealIntegratorImpl {
055    
056        /** Abscissas for the 2 points method. */
057        private static final double[] ABSCISSAS_2 = {
058            -1.0 / FastMath.sqrt(3.0),
059             1.0 / FastMath.sqrt(3.0)
060        };
061    
062        /** Weights for the 2 points method. */
063        private static final double[] WEIGHTS_2 = {
064            1.0,
065            1.0
066        };
067    
068        /** Abscissas for the 3 points method. */
069        private static final double[] ABSCISSAS_3 = {
070            -FastMath.sqrt(0.6),
071             0.0,
072             FastMath.sqrt(0.6)
073        };
074    
075        /** Weights for the 3 points method. */
076        private static final double[] WEIGHTS_3 = {
077            5.0 / 9.0,
078            8.0 / 9.0,
079            5.0 / 9.0
080        };
081    
082        /** Abscissas for the 4 points method. */
083        private static final double[] ABSCISSAS_4 = {
084            -FastMath.sqrt((15.0 + 2.0 * FastMath.sqrt(30.0)) / 35.0),
085            -FastMath.sqrt((15.0 - 2.0 * FastMath.sqrt(30.0)) / 35.0),
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        };
089    
090        /** Weights for the 4 points method. */
091        private static final double[] WEIGHTS_4 = {
092            (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0,
093            (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0,
094            (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0,
095            (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0
096        };
097    
098        /** Abscissas for the 5 points method. */
099        private static final double[] ABSCISSAS_5 = {
100            -FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0),
101            -FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0),
102             0.0,
103             FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0),
104             FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0)
105        };
106    
107        /** Weights for the 5 points method. */
108        private static final double[] WEIGHTS_5 = {
109            (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0,
110            (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0,
111            128.0 / 225.0,
112            (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0,
113            (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0
114        };
115    
116        /** Abscissas for the current method. */
117        private final double[] abscissas;
118    
119        /** Weights for the current method. */
120        private final double[] weights;
121    
122        /**
123         * Build a Legendre-Gauss integrator with given accuracies and iterations counts.
124         * @param n number of points desired (must be between 2 and 5 inclusive)
125         * @param relativeAccuracy relative accuracy of the result
126         * @param absoluteAccuracy absolute accuracy of the result
127         * @param minimalIterationCount minimum number of iterations
128         * @param maximalIterationCount maximum number of iterations
129         * @exception NotStrictlyPositiveException if minimal number of iterations
130         * is not strictly positive
131         * @exception NumberIsTooSmallException if maximal number of iterations
132         * is lesser than or equal to the minimal number of iterations
133         */
134        public LegendreGaussIntegrator(final int n,
135                                       final double relativeAccuracy,
136                                       final double absoluteAccuracy,
137                                       final int minimalIterationCount,
138                                       final int maximalIterationCount)
139            throws NotStrictlyPositiveException, NumberIsTooSmallException {
140            super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
141            switch(n) {
142            case 2 :
143                abscissas = ABSCISSAS_2;
144                weights   = WEIGHTS_2;
145                break;
146            case 3 :
147                abscissas = ABSCISSAS_3;
148                weights   = WEIGHTS_3;
149                break;
150            case 4 :
151                abscissas = ABSCISSAS_4;
152                weights   = WEIGHTS_4;
153                break;
154            case 5 :
155                abscissas = ABSCISSAS_5;
156                weights   = WEIGHTS_5;
157                break;
158            default :
159                throw new MathIllegalArgumentException(
160                        LocalizedFormats.N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED,
161                        n, 2, 5);
162            }
163    
164        }
165    
166        /**
167         * Build a Legendre-Gauss integrator with given accuracies.
168         * @param n number of points desired (must be between 2 and 5 inclusive)
169         * @param relativeAccuracy relative accuracy of the result
170         * @param absoluteAccuracy absolute accuracy of the result
171         */
172        public LegendreGaussIntegrator(final int n,
173                                       final double relativeAccuracy,
174                                       final double absoluteAccuracy) {
175            this(n, relativeAccuracy, absoluteAccuracy,
176                 DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
177        }
178    
179        /**
180         * Build a Legendre-Gauss integrator with given iteration counts.
181         * @param n number of points desired (must be between 2 and 5 inclusive)
182         * @param minimalIterationCount minimum number of iterations
183         * @param maximalIterationCount maximum number of iterations
184         * @exception NotStrictlyPositiveException if minimal number of iterations
185         * is not strictly positive
186         * @exception NumberIsTooSmallException if maximal number of iterations
187         * is lesser than or equal to the minimal number of iterations
188         */
189        public LegendreGaussIntegrator(final int n,
190                                       final int minimalIterationCount,
191                                       final int maximalIterationCount) {
192            this(n, DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
193                 minimalIterationCount, maximalIterationCount);
194        }
195    
196        /** {@inheritDoc} */
197        protected double doIntegrate()
198            throws TooManyEvaluationsException, MaxCountExceededException {
199    
200            // compute first estimate with a single step
201            double oldt = stage(1);
202    
203            int n = 2;
204            while (true) {
205    
206                // improve integral with a larger number of steps
207                final double t = stage(n);
208    
209                // estimate error
210                final double delta = FastMath.abs(t - oldt);
211                final double limit =
212                    FastMath.max(absoluteAccuracy,
213                             relativeAccuracy * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5);
214    
215                // check convergence
216                if ((iterations.getCount() + 1 >= minimalIterationCount) && (delta <= limit)) {
217                    return t;
218                }
219    
220                // prepare next iteration
221                double ratio = FastMath.min(4, FastMath.pow(delta / limit, 0.5 / abscissas.length));
222                n = FastMath.max((int) (ratio * n), n + 1);
223                oldt = t;
224                iterations.incrementCount();
225    
226            }
227    
228        }
229    
230        /**
231         * Compute the n-th stage integral.
232         * @param n number of steps
233         * @return the value of n-th stage integral
234         * @throws TooManyEvaluationsException if the maximum number of evaluations
235         * is exceeded.
236         */
237        private double stage(final int n)
238            throws TooManyEvaluationsException {
239    
240            // set up the step for the current stage
241            final double step     = (max - min) / n;
242            final double halfStep = step / 2.0;
243    
244            // integrate over all elementary steps
245            double midPoint = min + halfStep;
246            double sum = 0.0;
247            for (int i = 0; i < n; ++i) {
248                for (int j = 0; j < abscissas.length; ++j) {
249                    sum += weights[j] * computeObjectiveValue(midPoint + halfStep * abscissas[j]);
250                }
251                midPoint += step;
252            }
253    
254            return halfStep * sum;
255    
256        }
257    
258    }