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