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.MaxCountExceededException;
020    import org.apache.commons.math.exception.NotStrictlyPositiveException;
021    import org.apache.commons.math.exception.NumberIsTooLargeException;
022    import org.apache.commons.math.exception.NumberIsTooSmallException;
023    import org.apache.commons.math.exception.TooManyEvaluationsException;
024    import org.apache.commons.math.util.FastMath;
025    
026    /**
027     * Implements the <a href="http://mathworld.wolfram.com/TrapezoidalRule.html">
028     * Trapezoid Rule</a> for integration of real univariate functions. For
029     * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X,
030     * chapter 3.
031     * <p>
032     * The function should be integrable.</p>
033     *
034     * @version $Id: TrapezoidIntegrator.java 1179926 2011-10-07 03:18:05Z psteitz $
035     * @since 1.2
036     */
037    public class TrapezoidIntegrator extends UnivariateRealIntegratorImpl {
038    
039        /** Maximum number of iterations for trapezoid. */
040        public static final int TRAPEZOID_MAX_ITERATIONS_COUNT = 64;
041    
042        /** Intermediate result. */
043        private double s;
044    
045        /**
046         * Build a trapezoid integrator with given accuracies and iterations counts.
047         * @param relativeAccuracy relative accuracy of the result
048         * @param absoluteAccuracy absolute accuracy of the result
049         * @param minimalIterationCount minimum number of iterations
050         * @param maximalIterationCount maximum number of iterations
051         * (must be less than or equal to {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
052         * @exception NotStrictlyPositiveException if minimal number of iterations
053         * is not strictly positive
054         * @exception NumberIsTooSmallException if maximal number of iterations
055         * is lesser than or equal to the minimal number of iterations
056         * @exception NumberIsTooLargeException if maximal number of iterations
057         * is greater than {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
058         */
059        public TrapezoidIntegrator(final double relativeAccuracy,
060                                   final double absoluteAccuracy,
061                                   final int minimalIterationCount,
062                                   final int maximalIterationCount)
063            throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
064            super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
065            if (maximalIterationCount > TRAPEZOID_MAX_ITERATIONS_COUNT) {
066                throw new NumberIsTooLargeException(maximalIterationCount,
067                                                    TRAPEZOID_MAX_ITERATIONS_COUNT, false);
068            }
069        }
070    
071        /**
072         * Build a trapezoid integrator with given iteration counts.
073         * @param minimalIterationCount minimum number of iterations
074         * @param maximalIterationCount maximum number of iterations
075         * (must be less than or equal to {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
076         * @exception NotStrictlyPositiveException if minimal number of iterations
077         * is not strictly positive
078         * @exception NumberIsTooSmallException if maximal number of iterations
079         * is lesser than or equal to the minimal number of iterations
080         * @exception NumberIsTooLargeException if maximal number of iterations
081         * is greater than {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
082         */
083        public TrapezoidIntegrator(final int minimalIterationCount,
084                                   final int maximalIterationCount)
085            throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
086            super(minimalIterationCount, maximalIterationCount);
087            if (maximalIterationCount > TRAPEZOID_MAX_ITERATIONS_COUNT) {
088                throw new NumberIsTooLargeException(maximalIterationCount,
089                                                    TRAPEZOID_MAX_ITERATIONS_COUNT, false);
090            }
091        }
092    
093        /**
094         * Construct a trapezoid integrator with default settings.
095         * (max iteration count set to {@link #TRAPEZOID_MAX_ITERATIONS_COUNT})
096         */
097        public TrapezoidIntegrator() {
098            super(DEFAULT_MIN_ITERATIONS_COUNT, TRAPEZOID_MAX_ITERATIONS_COUNT);
099        }
100    
101        /**
102         * Compute the n-th stage integral of trapezoid rule. This function
103         * should only be called by API <code>integrate()</code> in the package.
104         * To save time it does not verify arguments - caller does.
105         * <p>
106         * The interval is divided equally into 2^n sections rather than an
107         * arbitrary m sections because this configuration can best utilize the
108         * already computed values.</p>
109         *
110         * @param baseIntegrator integrator holding integration parameters
111         * @param n the stage of 1/2 refinement, n = 0 is no refinement
112         * @return the value of n-th stage integral
113         * @throws TooManyEvaluationsException if the maximal number of evaluations
114         * is exceeded.
115         */
116        double stage(final UnivariateRealIntegratorImpl baseIntegrator, final int n)
117            throws TooManyEvaluationsException {
118    
119            if (n == 0) {
120                s = 0.5 * (baseIntegrator.max - baseIntegrator.min) *
121                          (baseIntegrator.computeObjectiveValue(baseIntegrator.min) +
122                           baseIntegrator.computeObjectiveValue(baseIntegrator.max));
123                return s;
124            } else {
125                final long np = 1L << (n-1);           // number of new points in this stage
126                double sum = 0;
127                // spacing between adjacent new points
128                final double spacing = (baseIntegrator.max - baseIntegrator.min) / np;
129                double x = baseIntegrator.min + 0.5 * spacing;    // the first new point
130                for (long i = 0; i < np; i++) {
131                    sum += baseIntegrator.computeObjectiveValue(x);
132                    x += spacing;
133                }
134                // add the new sum to previously calculated result
135                s = 0.5 * (s + sum * spacing);
136                return s;
137            }
138        }
139    
140        /** {@inheritDoc} */
141        protected double doIntegrate()
142            throws TooManyEvaluationsException, MaxCountExceededException {
143    
144            double oldt = stage(this, 0);
145            iterations.incrementCount();
146            while (true) {
147                final int i = iterations.getCount();
148                final double t = stage(this, i);
149                if (i >= minimalIterationCount) {
150                    final double delta = FastMath.abs(t - oldt);
151                    final double rLimit =
152                        relativeAccuracy * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5;
153                    if ((delta <= rLimit) || (delta <= absoluteAccuracy)) {
154                        return t;
155                    }
156                }
157                oldt = t;
158                iterations.incrementCount();
159            }
160    
161        }
162    
163    }