/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.analysis.solvers;
import org.apache.commons.math.ConvergingAlgorithmImpl;
import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.MathRuntimeException;
import org.apache.commons.math.analysis.UnivariateRealFunction;
/**
* Provide a default implementation for several functions useful to generic
* solvers.
*
* @version $Revision: 811833 $ $Date: 2009-09-06 12:27:50 -0400 (Sun, 06 Sep 2009) $
*/
public abstract class UnivariateRealSolverImpl
extends ConvergingAlgorithmImpl implements UnivariateRealSolver {
/**
* Maximum error of function.
*/
protected double functionValueAccuracy;
/**
* Default maximum error of function.
*/
protected double defaultFunctionValueAccuracy;
/**
* Indicates where a root has been computed.
*/
protected boolean resultComputed = false;
/**
* The last computed root.
*/
protected double result;
/**
* Value of the function at the last computed result.
*/
protected double functionValue;
/**
* The function to solve.
*
* @deprecated as of 2.0 the function to solve is passed as an argument
* to the {@link #solve(UnivariateRealFunction, double, double)} or
* {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
* method.
*/
@Deprecated
protected UnivariateRealFunction f;
/**
* Construct a solver with given iteration count and accuracy.
*
* @param f the function to solve.
* @param defaultAbsoluteAccuracy maximum absolute error
* @param defaultMaximalIterationCount maximum number of iterations
* @throws IllegalArgumentException if f is null or the
* defaultAbsoluteAccuracy is not valid
* @deprecated as of 2.0 the function to solve is passed as an argument
* to the {@link #solve(UnivariateRealFunction, double, double)} or
* {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
* method.
*/
@Deprecated
protected UnivariateRealSolverImpl(final UnivariateRealFunction f,
final int defaultMaximalIterationCount,
final double defaultAbsoluteAccuracy) {
super(defaultMaximalIterationCount, defaultAbsoluteAccuracy);
if (f == null) {
throw MathRuntimeException.createIllegalArgumentException("function to solve cannot be null");
}
this.f = f;
this.defaultFunctionValueAccuracy = 1.0e-15;
this.functionValueAccuracy = defaultFunctionValueAccuracy;
}
/**
* Construct a solver with given iteration count and accuracy.
*
* @param defaultAbsoluteAccuracy maximum absolute error
* @param defaultMaximalIterationCount maximum number of iterations
* @throws IllegalArgumentException if f is null or the
* defaultAbsoluteAccuracy is not valid
*/
protected UnivariateRealSolverImpl(final int defaultMaximalIterationCount,
final double defaultAbsoluteAccuracy) {
super(defaultMaximalIterationCount, defaultAbsoluteAccuracy);
this.defaultFunctionValueAccuracy = 1.0e-15;
this.functionValueAccuracy = defaultFunctionValueAccuracy;
}
/**
* Check if a result has been computed.
*
* @throws IllegalStateException if no result has been computed
*/
protected void checkResultComputed() throws IllegalStateException {
if (!resultComputed) {
throw MathRuntimeException.createIllegalStateException("no result available");
}
}
/**
* {@inheritDoc}
*/
@Override
public double getResult() {
checkResultComputed();
return result;
}
/**
* {@inheritDoc}
*/
@Override
public double getFunctionValue() {
checkResultComputed();
return functionValue;
}
/**
* {@inheritDoc}
*/
@Override
public void setFunctionValueAccuracy(final double accuracy) {
functionValueAccuracy = accuracy;
}
/**
* {@inheritDoc}
*/
@Override
public double getFunctionValueAccuracy() {
return functionValueAccuracy;
}
/**
* {@inheritDoc}
*/
@Override
public void resetFunctionValueAccuracy() {
functionValueAccuracy = defaultFunctionValueAccuracy;
}
/**
* Convenience function for implementations.
*
* @param newResult the result to set
* @param iterationCount the iteration count to set
*/
protected final void setResult(final double newResult, final int iterationCount) {
this.result = newResult;
this.iterationCount = iterationCount;
this.resultComputed = true;
}
/**
* Convenience function for implementations.
*
* @param x the result to set
* @param fx the result to set
* @param iterationCount the iteration count to set
*/
protected final void setResult(final double x, final double fx,
final int iterationCount) {
this.result = x;
this.functionValue = fx;
this.iterationCount = iterationCount;
this.resultComputed = true;
}
/**
* Convenience function for implementations.
*/
protected final void clearResult() {
this.iterationCount = 0;
this.resultComputed = false;
}
/**
* Returns true iff the function takes opposite signs at the endpoints.
*
* @param lower the lower endpoint
* @param upper the upper endpoint
* @param function the function
* @return true if f(lower) * f(upper) < 0
* @throws FunctionEvaluationException if an error occurs evaluating the
* function at the endpoints
*/
protected boolean isBracketing(final double lower, final double upper,
final UnivariateRealFunction function)
throws FunctionEvaluationException {
final double f1 = function.value(lower);
final double f2 = function.value(upper);
return (f1 > 0 && f2 < 0) || (f1 < 0 && f2 > 0);
}
/**
* Returns true if the arguments form a (strictly) increasing sequence
*
* @param start first number
* @param mid second number
* @param end third number
* @return true if the arguments form an increasing sequence
*/
protected boolean isSequence(final double start, final double mid, final double end) {
return (start < mid) && (mid < end);
}
/**
* Verifies that the endpoints specify an interval,
* throws IllegalArgumentException if not
*
* @param lower lower endpoint
* @param upper upper endpoint
* @throws IllegalArgumentException
*/
protected void verifyInterval(final double lower, final double upper) {
if (lower >= upper) {
throw MathRuntimeException.createIllegalArgumentException(
"endpoints do not specify an interval: [{0}, {1}]",
lower, upper);
}
}
/**
* Verifies that <code>lower < initial < upper</code>
* throws IllegalArgumentException if not
*
* @param lower lower endpoint
* @param initial initial value
* @param upper upper endpoint
* @throws IllegalArgumentException
*/
protected void verifySequence(final double lower, final double initial, final double upper) {
if (!isSequence(lower, initial, upper)) {
throw MathRuntimeException.createIllegalArgumentException(
"invalid interval, initial value parameters: lower={0}, initial={1}, upper={2}",
lower, initial, upper);
}
}
/**
* Verifies that the endpoints specify an interval and the function takes
* opposite signs at the enpoints, throws IllegalArgumentException if not
*
* @param lower lower endpoint
* @param upper upper endpoint
* @param function function
* @throws IllegalArgumentException
* @throws FunctionEvaluationException if an error occurs evaluating the
* function at the endpoints
*/
protected void verifyBracketing(final double lower, final double upper,
final UnivariateRealFunction function)
throws FunctionEvaluationException {
verifyInterval(lower, upper);
if (!isBracketing(lower, upper, function)) {
throw MathRuntimeException.createIllegalArgumentException(
"function values at endpoints do not have different signs. " +
"Endpoints: [{0}, {1}], Values: [{2}, {3}]",
lower, upper, function.value(lower), function.value(upper));
}
}
}