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.legacy.analysis.integration;
018
019import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
020import org.apache.commons.math4.legacy.analysis.solvers.UnivariateSolverUtils;
021import org.apache.commons.math4.legacy.exception.NullArgumentException;
022import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
023import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
024import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
025import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
026import org.apache.commons.math4.legacy.core.IntegerSequence;
027
028/**
029 * Provide a default implementation for several generic functions.
030 *
031 * @since 1.2
032 */
033public abstract class BaseAbstractUnivariateIntegrator implements UnivariateIntegrator {
034
035    /** Default absolute accuracy. */
036    public static final double DEFAULT_ABSOLUTE_ACCURACY = 1.0e-15;
037
038    /** Default relative accuracy. */
039    public static final double DEFAULT_RELATIVE_ACCURACY = 1.0e-6;
040
041    /** Default minimal iteration count. */
042    public static final int DEFAULT_MIN_ITERATIONS_COUNT = 3;
043
044    /** Default maximal iteration count. */
045    public static final int DEFAULT_MAX_ITERATIONS_COUNT = Integer.MAX_VALUE;
046
047    /** The iteration count. */
048    protected IntegerSequence.Incrementor iterations;
049
050    /** Maximum absolute error. */
051    private final double absoluteAccuracy;
052
053    /** Maximum relative error. */
054    private final double relativeAccuracy;
055
056    /** minimum number of iterations. */
057    private final int minimalIterationCount;
058    /** maximum number of iterations. */
059    private final int maximalIterationCount;
060
061    /** The functions evaluation count. */
062    private IntegerSequence.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.numbers.core.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     *
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        // accuracy settings
113        this.relativeAccuracy      = relativeAccuracy;
114        this.absoluteAccuracy      = absoluteAccuracy;
115
116        // iterations count settings
117        if (minimalIterationCount <= 0) {
118            throw new NotStrictlyPositiveException(minimalIterationCount);
119        }
120        if (maximalIterationCount <= minimalIterationCount) {
121            throw new NumberIsTooSmallException(maximalIterationCount, minimalIterationCount, false);
122        }
123        this.minimalIterationCount = minimalIterationCount;
124        this.maximalIterationCount = maximalIterationCount;
125    }
126
127    /**
128     * Construct an integrator with given accuracies.
129     * @param relativeAccuracy relative accuracy of the result
130     * @param absoluteAccuracy absolute accuracy of the result
131     */
132    protected BaseAbstractUnivariateIntegrator(final double relativeAccuracy,
133                                               final double absoluteAccuracy) {
134        this(relativeAccuracy, absoluteAccuracy,
135             DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
136    }
137
138    /**
139     * Construct an integrator with given iteration counts.
140     * @param minimalIterationCount minimum number of iterations
141     * @param maximalIterationCount maximum number of iterations
142     * @exception NotStrictlyPositiveException if minimal number of iterations
143     * is not strictly positive
144     * @exception NumberIsTooSmallException if maximal number of iterations
145     * is lesser than or equal to the minimal number of iterations
146     */
147    protected BaseAbstractUnivariateIntegrator(final int minimalIterationCount,
148                                               final int maximalIterationCount) {
149        this(DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
150             minimalIterationCount, maximalIterationCount);
151    }
152
153    /** {@inheritDoc} */
154    @Override
155    public double getRelativeAccuracy() {
156        return relativeAccuracy;
157    }
158
159    /** {@inheritDoc} */
160    @Override
161    public double getAbsoluteAccuracy() {
162        return absoluteAccuracy;
163    }
164
165    /** {@inheritDoc} */
166    @Override
167    public int getMinimalIterationCount() {
168        return minimalIterationCount;
169    }
170
171    /** {@inheritDoc} */
172    @Override
173    public int getMaximalIterationCount() {
174        return iterations.getMaximalCount();
175    }
176
177    /** {@inheritDoc} */
178    @Override
179    public int getEvaluations() {
180        return evaluations.getCount();
181    }
182
183    /** {@inheritDoc} */
184    @Override
185    public int getIterations() {
186        return iterations.getCount();
187    }
188
189    /**
190     * @return the lower bound.
191     */
192    protected double getMin() {
193        return min;
194    }
195    /**
196     * @return the upper bound.
197     */
198    protected double getMax() {
199        return max;
200    }
201
202    /**
203     * Compute the objective function value.
204     *
205     * @param point Point at which the objective function must be evaluated.
206     * @return the objective function value at specified point.
207     * @throws TooManyEvaluationsException if the maximal number of function
208     * evaluations is exceeded.
209     */
210    protected double computeObjectiveValue(final double point) {
211        try {
212            evaluations.increment();
213        } catch (MaxCountExceededException e) {
214            throw new TooManyEvaluationsException(e.getMax());
215        }
216        return function.value(point);
217    }
218
219    /**
220     * Prepare for computation.
221     * Subclasses must call this method if they the {@code integrate} method.
222     *
223     * @param maxEval Maximum number of evaluations.
224     * @param f the integrand function
225     * @param lower the min bound for the interval
226     * @param upper the upper bound for the interval
227     * @throws NullArgumentException if {@code f} is {@code null}.
228     * @throws org.apache.commons.math4.legacy.exception.MathIllegalArgumentException
229     * if {@code min >= max}.
230     */
231    protected void setup(final int maxEval,
232                         final UnivariateFunction f,
233                         final double lower, final double upper) {
234
235        // Checks.
236        NullArgumentException.check(f);
237        UnivariateSolverUtils.verifyInterval(lower, upper);
238
239        // Reset.
240        min = lower;
241        max = upper;
242        function = f;
243        iterations = IntegerSequence.Incrementor.create()
244            .withMaximalCount(maximalIterationCount);
245        evaluations = IntegerSequence.Incrementor.create()
246            .withMaximalCount(maxEval);
247    }
248
249    /** {@inheritDoc} */
250    @Override
251    public double integrate(final int maxEval, final UnivariateFunction f,
252                            final double lower, final double upper) {
253
254        // Initialization.
255        setup(maxEval, f, lower, upper);
256
257        // Perform computation.
258        return doIntegrate();
259    }
260
261    /**
262     * Method for implementing actual integration algorithms in derived
263     * classes.
264     *
265     * @return the root.
266     * @throws TooManyEvaluationsException if the maximal number of evaluations
267     * is exceeded.
268     * @throws MaxCountExceededException if the maximum iteration count is exceeded
269     * or the integrator detects convergence problems otherwise
270     */
271    protected abstract double doIntegrate() ;
272}