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.math3.analysis.integration;
018
019import org.apache.commons.math3.analysis.UnivariateFunction;
020import org.apache.commons.math3.analysis.solvers.UnivariateSolverUtils;
021import org.apache.commons.math3.exception.MathIllegalArgumentException;
022import org.apache.commons.math3.exception.MaxCountExceededException;
023import org.apache.commons.math3.exception.NotStrictlyPositiveException;
024import org.apache.commons.math3.exception.NullArgumentException;
025import org.apache.commons.math3.exception.NumberIsTooSmallException;
026import org.apache.commons.math3.exception.TooManyEvaluationsException;
027import org.apache.commons.math3.util.Incrementor;
028import org.apache.commons.math3.util.MathUtils;
029
030/**
031 * Provide a default implementation for several generic functions.
032 *
033 * @since 1.2
034 */
035public abstract class BaseAbstractUnivariateIntegrator implements UnivariateIntegrator {
036
037    /** Default absolute accuracy. */
038    public static final double DEFAULT_ABSOLUTE_ACCURACY = 1.0e-15;
039
040    /** Default relative accuracy. */
041    public static final double DEFAULT_RELATIVE_ACCURACY = 1.0e-6;
042
043    /** Default minimal iteration count. */
044    public static final int DEFAULT_MIN_ITERATIONS_COUNT = 3;
045
046    /** Default maximal iteration count. */
047    public static final int DEFAULT_MAX_ITERATIONS_COUNT = Integer.MAX_VALUE;
048
049    /** The iteration count. */
050    protected final Incrementor iterations;
051
052    /** Maximum absolute error. */
053    private final double absoluteAccuracy;
054
055    /** Maximum relative error. */
056    private final double relativeAccuracy;
057
058    /** minimum number of iterations */
059    private final int minimalIterationCount;
060
061    /** The functions evaluation count. */
062    private final Incrementor evaluations;
063
064    /** Function to integrate. */
065    private UnivariateFunction function;
066
067    /** Lower bound for the interval. */
068    private double min;
069
070    /** Upper bound for the interval. */
071    private double max;
072
073    /**
074     * Construct an integrator with given accuracies and iteration counts.
075     * <p>
076     * The meanings of the various parameters are:
077     * <ul>
078     *   <li>relative accuracy:
079     *       this is used to stop iterations if the absolute accuracy can't be
080     *       achieved due to large values or short mantissa length. If this
081     *       should be the primary criterion for convergence rather then a
082     *       safety measure, set the absolute accuracy to a ridiculously small value,
083     *       like {@link org.apache.commons.math3.util.Precision#SAFE_MIN Precision.SAFE_MIN}.</li>
084     *   <li>absolute accuracy:
085     *       The default is usually chosen so that results in the interval
086     *       -10..-0.1 and +0.1..+10 can be found with a reasonable accuracy. If the
087     *       expected absolute value of your results is of much smaller magnitude, set
088     *       this to a smaller value.</li>
089     *   <li>minimum number of iterations:
090     *       minimal iteration is needed to avoid false early convergence, e.g.
091     *       the sample points happen to be zeroes of the function. Users can
092     *       use the default value or choose one that they see as appropriate.</li>
093     *   <li>maximum number of iterations:
094     *       usually a high iteration count indicates convergence problems. However,
095     *       the "reasonable value" varies widely for different algorithms. Users are
096     *       advised to use the default value supplied by the algorithm.</li>
097     * </ul>
098     * </p>
099     * @param relativeAccuracy relative accuracy of the result
100     * @param absoluteAccuracy absolute accuracy of the result
101     * @param minimalIterationCount minimum number of iterations
102     * @param maximalIterationCount maximum number of iterations
103     * @exception NotStrictlyPositiveException if minimal number of iterations
104     * is not strictly positive
105     * @exception NumberIsTooSmallException if maximal number of iterations
106     * is lesser than or equal to the minimal number of iterations
107     */
108    protected BaseAbstractUnivariateIntegrator(final double relativeAccuracy,
109                                               final double absoluteAccuracy,
110                                               final int minimalIterationCount,
111                                               final int maximalIterationCount)
112        throws NotStrictlyPositiveException, NumberIsTooSmallException {
113
114        // accuracy settings
115        this.relativeAccuracy      = relativeAccuracy;
116        this.absoluteAccuracy      = absoluteAccuracy;
117
118        // iterations count settings
119        if (minimalIterationCount <= 0) {
120            throw new NotStrictlyPositiveException(minimalIterationCount);
121        }
122        if (maximalIterationCount <= minimalIterationCount) {
123            throw new NumberIsTooSmallException(maximalIterationCount, minimalIterationCount, false);
124        }
125        this.minimalIterationCount = minimalIterationCount;
126        this.iterations            = new Incrementor();
127        iterations.setMaximalCount(maximalIterationCount);
128
129        // prepare evaluations counter, but do not set it yet
130        evaluations = new Incrementor();
131
132    }
133
134    /**
135     * Construct an integrator with given accuracies.
136     * @param relativeAccuracy relative accuracy of the result
137     * @param absoluteAccuracy absolute accuracy of the result
138     */
139    protected BaseAbstractUnivariateIntegrator(final double relativeAccuracy,
140                                           final double absoluteAccuracy) {
141        this(relativeAccuracy, absoluteAccuracy,
142             DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
143    }
144
145    /**
146     * Construct an integrator with given iteration counts.
147     * @param minimalIterationCount minimum number of iterations
148     * @param maximalIterationCount maximum number of iterations
149     * @exception NotStrictlyPositiveException if minimal number of iterations
150     * is not strictly positive
151     * @exception NumberIsTooSmallException if maximal number of iterations
152     * is lesser than or equal to the minimal number of iterations
153     */
154    protected BaseAbstractUnivariateIntegrator(final int minimalIterationCount,
155                                           final int maximalIterationCount)
156        throws NotStrictlyPositiveException, NumberIsTooSmallException {
157        this(DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
158             minimalIterationCount, maximalIterationCount);
159    }
160
161    /** {@inheritDoc} */
162    public double getRelativeAccuracy() {
163        return relativeAccuracy;
164    }
165
166    /** {@inheritDoc} */
167    public double getAbsoluteAccuracy() {
168        return absoluteAccuracy;
169    }
170
171    /** {@inheritDoc} */
172    public int getMinimalIterationCount() {
173        return minimalIterationCount;
174    }
175
176    /** {@inheritDoc} */
177    public int getMaximalIterationCount() {
178        return iterations.getMaximalCount();
179    }
180
181    /** {@inheritDoc} */
182    public int getEvaluations() {
183        return evaluations.getCount();
184    }
185
186    /** {@inheritDoc} */
187    public int getIterations() {
188        return iterations.getCount();
189    }
190
191    /**
192     * @return the lower bound.
193     */
194    protected double getMin() {
195        return min;
196    }
197    /**
198     * @return the upper bound.
199     */
200    protected double getMax() {
201        return max;
202    }
203
204    /**
205     * Compute the objective function value.
206     *
207     * @param point Point at which the objective function must be evaluated.
208     * @return the objective function value at specified point.
209     * @throws TooManyEvaluationsException if the maximal number of function
210     * evaluations is exceeded.
211     */
212    protected double computeObjectiveValue(final double point)
213        throws TooManyEvaluationsException {
214        try {
215            evaluations.incrementCount();
216        } catch (MaxCountExceededException e) {
217            throw new TooManyEvaluationsException(e.getMax());
218        }
219        return function.value(point);
220    }
221
222    /**
223     * Prepare for computation.
224     * Subclasses must call this method if they override any of the
225     * {@code solve} methods.
226     *
227     * @param maxEval Maximum number of evaluations.
228     * @param f the integrand function
229     * @param lower the min bound for the interval
230     * @param upper the upper bound for the interval
231     * @throws NullArgumentException if {@code f} is {@code null}.
232     * @throws MathIllegalArgumentException if {@code min >= max}.
233     */
234    protected void setup(final int maxEval,
235                         final UnivariateFunction f,
236                         final double lower, final double upper)
237        throws NullArgumentException, MathIllegalArgumentException {
238
239        // Checks.
240        MathUtils.checkNotNull(f);
241        UnivariateSolverUtils.verifyInterval(lower, upper);
242
243        // Reset.
244        min = lower;
245        max = upper;
246        function = f;
247        evaluations.setMaximalCount(maxEval);
248        evaluations.resetCount();
249        iterations.resetCount();
250
251    }
252
253    /** {@inheritDoc} */
254    public double integrate(final int maxEval, final UnivariateFunction f,
255                            final double lower, final double upper)
256        throws TooManyEvaluationsException, MaxCountExceededException,
257               MathIllegalArgumentException, NullArgumentException {
258
259        // Initialization.
260        setup(maxEval, f, lower, upper);
261
262        // Perform computation.
263        return doIntegrate();
264
265    }
266
267    /**
268     * Method for implementing actual integration algorithms in derived
269     * classes.
270     *
271     * @return the root.
272     * @throws TooManyEvaluationsException if the maximal number of evaluations
273     * is exceeded.
274     * @throws MaxCountExceededException if the maximum iteration count is exceeded
275     * or the integrator detects convergence problems otherwise
276     */
277    protected abstract double doIntegrate()
278        throws TooManyEvaluationsException, MaxCountExceededException;
279
280}