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 */
017package org.apache.commons.math4.analysis.integration;
018
019import org.apache.commons.math4.exception.NumberIsTooLargeException;
020import org.apache.commons.math4.util.FastMath;
021
022/**
023 * Implements <a href="http://mathworld.wolfram.com/SimpsonsRule.html">
024 * Simpson's Rule</a> for integration of real univariate functions. For
025 * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X,
026 * chapter 3.
027 * <p>
028 * This implementation employs the basic trapezoid rule to calculate Simpson's
029 * rule.</p>
030 *
031 * @since 1.2
032 */
033public class SimpsonIntegrator extends BaseAbstractUnivariateIntegrator {
034
035    /** Maximal number of iterations for Simpson. */
036    public static final int SIMPSON_MAX_ITERATIONS_COUNT = 63;
037
038    /**
039     * Build a Simpson integrator with given accuracies and iterations counts.
040     * @param relativeAccuracy relative accuracy of the result
041     * @param absoluteAccuracy absolute accuracy of the result
042     * @param minimalIterationCount minimum number of iterations
043     * @param maximalIterationCount maximum number of iterations
044     * (must be less than or equal to {@link #SIMPSON_MAX_ITERATIONS_COUNT})
045     * @exception org.apache.commons.math4.exception.NotStrictlyPositiveException if minimal number of iterations
046     * is not strictly positive
047     * @exception org.apache.commons.math4.exception.NumberIsTooSmallException if maximal number of iterations
048     * is lesser than or equal to the minimal number of iterations
049     * @exception NumberIsTooLargeException if maximal number of iterations
050     * is greater than {@link #SIMPSON_MAX_ITERATIONS_COUNT}
051     */
052    public SimpsonIntegrator(final double relativeAccuracy,
053                             final double absoluteAccuracy,
054                             final int minimalIterationCount,
055                             final int maximalIterationCount) {
056        super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
057        if (maximalIterationCount > SIMPSON_MAX_ITERATIONS_COUNT) {
058            throw new NumberIsTooLargeException(maximalIterationCount,
059                                                SIMPSON_MAX_ITERATIONS_COUNT, false);
060        }
061    }
062
063    /**
064     * Build a Simpson integrator with given iteration counts.
065     * @param minimalIterationCount minimum number of iterations
066     * @param maximalIterationCount maximum number of iterations
067     * (must be less than or equal to {@link #SIMPSON_MAX_ITERATIONS_COUNT})
068     * @exception org.apache.commons.math4.exception.NotStrictlyPositiveException if minimal number of iterations
069     * is not strictly positive
070     * @exception org.apache.commons.math4.exception.NumberIsTooSmallException if maximal number of iterations
071     * is lesser than or equal to the minimal number of iterations
072     * @exception NumberIsTooLargeException if maximal number of iterations
073     * is greater than {@link #SIMPSON_MAX_ITERATIONS_COUNT}
074     */
075    public SimpsonIntegrator(final int minimalIterationCount,
076                             final int maximalIterationCount) {
077        super(minimalIterationCount, maximalIterationCount);
078        if (maximalIterationCount > SIMPSON_MAX_ITERATIONS_COUNT) {
079            throw new NumberIsTooLargeException(maximalIterationCount,
080                                                SIMPSON_MAX_ITERATIONS_COUNT, false);
081        }
082    }
083
084    /**
085     * Construct an integrator with default settings.
086     * (max iteration count set to {@link #SIMPSON_MAX_ITERATIONS_COUNT})
087     */
088    public SimpsonIntegrator() {
089        super(DEFAULT_MIN_ITERATIONS_COUNT, SIMPSON_MAX_ITERATIONS_COUNT);
090    }
091
092    /** {@inheritDoc} */
093    @Override
094    protected double doIntegrate() {
095        // Simpson's rule requires at least two trapezoid stages.
096        // So we set the first sum using two trapezoid stages.
097        final TrapezoidIntegrator qtrap = new TrapezoidIntegrator();
098
099        final double s0 = qtrap.stage(this, 0);
100        double oldt = qtrap.stage(this, 1);
101        double olds = (4 * oldt - s0) / 3.0;
102        while (true) {
103            // The first iteration is the first refinement of the sum.
104            iterations.increment();
105            final int i = getIterations();
106            final double t = qtrap.stage(this, i + 1); // 1-stage ahead of the iteration
107            final double s = (4 * t - oldt) / 3.0;
108            if (i >= getMinimalIterationCount()) {
109                final double delta = FastMath.abs(s - olds);
110                final double rLimit = getRelativeAccuracy() * (FastMath.abs(olds) + FastMath.abs(s)) * 0.5;
111                if (delta <= rLimit ||
112                    delta <= getAbsoluteAccuracy()) {
113                    return s;
114                }
115            }
116            olds = s;
117            oldt = t;
118        }
119    }
120}