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     */
017    
018    package org.apache.commons.math.analysis.solvers;
019    
020    import org.apache.commons.math.ConvergingAlgorithmImpl;
021    import org.apache.commons.math.FunctionEvaluationException;
022    import org.apache.commons.math.MathRuntimeException;
023    import org.apache.commons.math.analysis.UnivariateRealFunction;
024    
025    /**
026     * Provide a default implementation for several functions useful to generic
027     * solvers.
028     *  
029     * @version $Revision: 799857 $ $Date: 2009-08-01 09:07:12 -0400 (Sat, 01 Aug 2009) $
030     */
031    public abstract class UnivariateRealSolverImpl
032        extends ConvergingAlgorithmImpl implements UnivariateRealSolver {
033    
034        /** Maximum error of function. */
035        protected double functionValueAccuracy;
036    
037        /** Default maximum error of function. */
038        protected double defaultFunctionValueAccuracy;
039    
040        /** Indicates where a root has been computed. */
041        protected boolean resultComputed = false;
042    
043        /** The last computed root. */
044        protected double result;
045    
046        /** Value of the function at the last computed result. */
047        protected double functionValue;
048    
049        /** The function to solve.
050         * @deprecated as of 2.0 the function to solve is passed as an argument
051         * to the {@link #solve(UnivariateRealFunction, double, double)} or
052         * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
053         * method. */
054        @Deprecated
055        protected UnivariateRealFunction f;
056    
057        /**
058         * Construct a solver with given iteration count and accuracy.
059         * 
060         * @param f the function to solve.
061         * @param defaultAbsoluteAccuracy maximum absolute error
062         * @param defaultMaximalIterationCount maximum number of iterations
063         * @throws IllegalArgumentException if f is null or the 
064         * defaultAbsoluteAccuracy is not valid
065         * @deprecated as of 2.0 the function to solve is passed as an argument
066         * to the {@link #solve(UnivariateRealFunction, double, double)} or
067         * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
068         * method.
069         */
070        @Deprecated
071        protected UnivariateRealSolverImpl(final UnivariateRealFunction f,
072                                           final int defaultMaximalIterationCount,
073                                           final double defaultAbsoluteAccuracy) {
074            super(defaultMaximalIterationCount, defaultAbsoluteAccuracy);
075            if (f == null) {
076                throw MathRuntimeException.createIllegalArgumentException("function to solve cannot be null");
077            }
078            this.f = f;
079            this.defaultFunctionValueAccuracy = 1.0e-15;
080            this.functionValueAccuracy = defaultFunctionValueAccuracy;
081        }
082    
083        /**
084         * Construct a solver with given iteration count and accuracy.
085         * 
086         * @param defaultAbsoluteAccuracy maximum absolute error
087         * @param defaultMaximalIterationCount maximum number of iterations
088         * @throws IllegalArgumentException if f is null or the 
089         * defaultAbsoluteAccuracy is not valid
090         */
091        protected UnivariateRealSolverImpl(final int defaultMaximalIterationCount,
092                                           final double defaultAbsoluteAccuracy) {
093            super(defaultMaximalIterationCount, defaultAbsoluteAccuracy);
094            this.defaultFunctionValueAccuracy = 1.0e-15;
095            this.functionValueAccuracy = defaultFunctionValueAccuracy;
096        }
097    
098        /** Check if a result has been computed.
099         * @exception IllegalStateException if no result has been computed
100         */
101        protected void checkResultComputed() throws IllegalStateException {
102            if (!resultComputed) {
103                throw MathRuntimeException.createIllegalStateException("no result available");
104            }
105        }
106    
107        /** {@inheritDoc} */
108        public double getResult() {
109            checkResultComputed();
110            return result;
111        }
112    
113        /** {@inheritDoc} */
114        public double getFunctionValue() {
115            checkResultComputed();
116            return functionValue;
117        }
118    
119        /** {@inheritDoc} */
120        public void setFunctionValueAccuracy(final double accuracy) {
121            functionValueAccuracy = accuracy;
122        }
123    
124        /** {@inheritDoc} */
125        public double getFunctionValueAccuracy() {
126            return functionValueAccuracy;
127        }
128    
129        /** {@inheritDoc} */
130        public void resetFunctionValueAccuracy() {
131            functionValueAccuracy = defaultFunctionValueAccuracy;
132        }
133    
134        /**
135         * Convenience function for implementations.
136         * 
137         * @param result the result to set
138         * @param iterationCount the iteration count to set
139         */
140        protected final void setResult(final double result, final int iterationCount) {
141            this.result         = result;
142            this.iterationCount = iterationCount;
143            this.resultComputed = true;
144        }
145    
146        /**
147         * Convenience function for implementations.
148         * 
149         * @param x the result to set
150         * @param fx the result to set
151         * @param iterationCount the iteration count to set
152         */
153        protected final void setResult(final double x, final double fx,
154                                       final int iterationCount) {
155            this.result         = x;
156            this.functionValue  = fx;
157            this.iterationCount = iterationCount;
158            this.resultComputed = true;
159        }
160    
161        /**
162         * Convenience function for implementations.
163         */
164        protected final void clearResult() {
165            this.iterationCount = 0;
166            this.resultComputed = false;
167        }
168    
169        /**
170         * Returns true iff the function takes opposite signs at the endpoints.
171         * 
172         * @param lower  the lower endpoint 
173         * @param upper  the upper endpoint
174         * @param f the function
175         * @return true if f(lower) * f(upper) < 0
176         * @throws FunctionEvaluationException if an error occurs evaluating the 
177         * function at the endpoints
178         */
179        protected boolean isBracketing(final double lower, final double upper, 
180                                       final UnivariateRealFunction f)
181            throws FunctionEvaluationException {
182            final double f1 = f.value(lower);
183            final double f2 = f.value(upper);
184            return ((f1 > 0 && f2 < 0) || (f1 < 0 && f2 > 0));
185        }
186        
187        /**
188         * Returns true if the arguments form a (strictly) increasing sequence
189         * 
190         * @param start  first number
191         * @param mid   second number
192         * @param end  third number
193         * @return true if the arguments form an increasing sequence
194         */
195        protected boolean isSequence(final double start, final double mid, final double end) {
196            return (start < mid) && (mid < end);
197        }
198        
199        /**
200         * Verifies that the endpoints specify an interval, 
201         * throws IllegalArgumentException if not
202         * 
203         * @param lower  lower endpoint
204         * @param upper upper endpoint
205         * @throws IllegalArgumentException
206         */
207        protected void verifyInterval(final double lower, final double upper) {
208            if (lower >= upper) {
209                throw MathRuntimeException.createIllegalArgumentException(
210                        "endpoints do not specify an interval: [{0}, {1}]",
211                        lower, upper);
212            }       
213        }
214        
215        /**
216         * Verifies that <code>lower < initial < upper</code>
217         * throws IllegalArgumentException if not
218         * 
219         * @param lower  lower endpoint
220         * @param initial initial value
221         * @param upper upper endpoint
222         * @throws IllegalArgumentException
223         */
224        protected void verifySequence(final double lower, final double initial, final double upper) {
225            if (!isSequence(lower, initial, upper)) {
226                throw MathRuntimeException.createIllegalArgumentException(
227                        "invalid interval, initial value parameters:  lower={0}, initial={1}, upper={2}",
228                        lower, initial, upper);
229            }       
230        }
231        
232        /**
233         * Verifies that the endpoints specify an interval and the function takes
234         * opposite signs at the enpoints, throws IllegalArgumentException if not
235         * 
236         * @param lower  lower endpoint
237         * @param upper upper endpoint
238         * @param f function
239         * @throws IllegalArgumentException
240         * @throws FunctionEvaluationException if an error occurs evaluating the 
241         * function at the endpoints
242         */
243        protected void verifyBracketing(final double lower, final double upper, 
244                                        final UnivariateRealFunction f)
245            throws FunctionEvaluationException {
246            
247            verifyInterval(lower, upper);
248            if (!isBracketing(lower, upper, f)) {
249                throw MathRuntimeException.createIllegalArgumentException(
250                        "function values at endpoints do not have different signs.  " +
251                        "Endpoints: [{0}, {1}], Values: [{2}, {3}]",
252                        lower, upper, f.value(lower), f.value(upper));       
253            }
254        }
255    }