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