/*
 Copyright (C) 2008 Richard Gomes

 This source code is release under the BSD License.

 This file is part of JQuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://jquantlib.org/

 JQuantLib is free software: you can redistribute it and/or modify it
 under the terms of the JQuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 . The license is also available online at
 .

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.

 JQuantLib is based on QuantLib. http://quantlib.org/
 When applicable, the original copyright notice follows this notice.
 */

package org.jquantlib.math;

import org.jquantlib.QL;




/**
 * @author 
 */
// TODO: code review :: license, class comments, comments for access modifiers, comments for @Override
//FIXME: refactor package "solvers1d"
abstract public class AbstractSolver1D {

    //
    // Constants
    //

    private static final int MAX_FUNCTION_EVALUATIONS = 100;


    //
    // private fields
    //

    private int maxEvaluations;
    private boolean lowerBoundEnforced;
    private boolean upperBoundEnforced;


    //
    // protected fields
    //

    protected double root, xMin, xMax, fxMin, fxMax;
    protected int evaluationNumber;
    protected double lowerBound, upperBound;


    //
    // public constructors
    //

    public AbstractSolver1D() {
        this.maxEvaluations = MAX_FUNCTION_EVALUATIONS;
        this.lowerBoundEnforced = false;
        this.upperBoundEnforced = false;
    }

    public AbstractSolver1D(final int maxEvalautions, final boolean lowerBoundEnforced, final boolean upperBoundenforeced) {
        setMaxEvaluations(maxEvalautions);
        this.lowerBoundEnforced = lowerBoundEnforced;
        this.upperBoundEnforced = upperBoundenforeced;
    }


    //
    // protected abstract methods
    //

    abstract protected double solveImpl(final F f, double accuracy);


    //
    // public methods
    //

    /**
     * This method returns the zero of the function f,
     * determined with the given accuracy epsilon;
     * depending on the particular solver, this might mean that
     * the returned x is such that |f(x)| < epsilon f,
     * or that f |x-\xi| < \epsilon \f$ where \f$ \xi \f$ 
     * is the real zero.
     * 

* This method contains a bracketing routine to which an * initial guess must be supplied as well as a step used to * scan the range of the possible bracketing values. */ public double solve(final F f, double accuracy, final double guess, final double step) { QL.require(accuracy > 0.0 , "accuracy must be positive"); // TODO: message // check whether we really want to use epsilon accuracy = Math.max(accuracy, Constants.QL_EPSILON); final double growthFactor = 1.6; int flipflop = -1; root = guess; fxMax = f.op(root); // monotonically crescent bias, as in optionValue(volatility) if (fxMax == 0.0) return root; else if (fxMax > 0.0) { xMin = enforceBounds(root - step); fxMin = f.op(xMin); xMax = root; } else { xMin = root; fxMin = fxMax; xMax = enforceBounds(root + step); fxMax = f.op(xMax); } evaluationNumber = 2; while (evaluationNumber <= maxEvaluations) { if (fxMin * fxMax <= 0.0) {//FIXME avoid product to check signs if (fxMin == 0.0) return xMin; if (fxMax == 0.0) return xMax; root = (xMax + xMin) / 2.0; return solveImpl(f, accuracy); } if (Math.abs(fxMin) < Math.abs(fxMax)) { xMin = enforceBounds(xMin + growthFactor * (xMin - xMax)); fxMin = f.op(xMin); } else if (Math.abs(fxMin) > Math.abs(fxMax)) { xMax = enforceBounds(xMax + growthFactor * (xMax - xMin)); fxMax = f.op(xMax); } else if (flipflop == -1) { xMin = enforceBounds(xMin + growthFactor * (xMin - xMax)); fxMin = f.op(xMin); evaluationNumber++; flipflop = 1; } else if (flipflop == 1) { xMax = enforceBounds(xMax + growthFactor * (xMax - xMin)); fxMax = f.op(xMax); flipflop = -1; } evaluationNumber++; } //FIXME is it so exceptional, should we return s success/fail flag? // TODO: code review :: please verify against QL/C++ code throw new ArithmeticException("unable to bracket root after function evaluation"); // TODO: message } /** * This method returns the zero of the function {@latex$ f }, determined with the given accuracy {@latex$ \epsilon }; * depending on the particular solver, this might mean that the returned {@latex$ x } is such that {@latex$ |f(x)| < \epsilon }, * or that {@latex$ |x-\xi| < \epsilon } where {@latex$ \xi } is the real zero. *

* An initial guess must be supplied, as well as two values {@latex$ x_\mathrm{min} } and {@latex$ x_\mathrm{max} } which must * bracket the zero (i.e., either {@latex$ f(x_\mathrm{min}) \leq 0 \leq f(x_\mathrm{max}) }, or * {@latex$ f(x_\mathrm{max}) \leq 0 \leq f(x_\mathrm{min}) } must be true). * * @param f is the function we wish to find a root * @param accuracy is the desired accuracy * @param guess * @param xMin * @param xMax * @return a zero of a function */ public double solve(final F f, double accuracy, final double guess, final double xMin, final double xMax) { // TODO: Design by Contract? http://bugs.jquantlib.org/view.php?id=291 QL.require(accuracy > 0.0 , "accuracy must be positive"); // TODO: message QL.require(xMin < xMax , "invalid range: xMin >= xMax"); // TODO: message QL.require(!lowerBoundEnforced || xMin >= lowerBound , "xMin < enforced low bound"); // TODO: message QL.require(!upperBoundEnforced || xMax <= upperBound , "xMax > enforced hi bound"); // TODO: message // check whether we really want to use epsilon accuracy = Math.max(accuracy, Constants.QL_EPSILON); this.xMin = xMin; this.xMax = xMax; fxMin = f.op(this.xMin); if (fxMin == 0.0) return this.xMin; fxMax = f.op(this.xMax); if (fxMax == 0.0) return this.xMax; evaluationNumber = 2; // TODO: code review :: please verify against QL/C++ code QL.require(fxMin * fxMax < 0.0 , "root not bracketed"); // TODO: message QL.require(guess > this.xMin , "guess must be greather than xMin"); // TODO: message QL.require(guess < this.xMax , "guess must be lesser than xMax"); // TODO: message root = guess; return solveImpl(f, accuracy); } public void setMaxEvaluations(final int evaluations) { this.maxEvaluations = Math.max(1, evaluations); } public int getMaxEvaluations() { return this.maxEvaluations; } public void setLowerBound(final double lowerBound) { this.lowerBound = lowerBound; this.lowerBoundEnforced = true; } public void setUpperBound(final double upperBound) { this.upperBound = upperBound; this.upperBoundEnforced = true; } public int getNumEvaluations() { return evaluationNumber; } // // private methods // private double enforceBounds(final double x) { if (lowerBoundEnforced && x < lowerBound) return lowerBound; if (upperBoundEnforced && x > upperBound) return upperBound; return x; } }

Ads help maintain this website.