/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
package org.opensourcephysics.numerics;
/**
* Derivative defines various derivative algorithms.
* This class cannot be subclassed or instantiated because all methods are static.
*
* @author Wolfgang Christian
*/
public class Derivative {
private Derivative() {} // prohibit instantiation because all methods are static
/**
* Gets a derivative function using the centered difference approximation.
* @param f Function f(x)
* @param h double change in x
* @return Function
*/
static public Function getFirst(final Function f, final double h) {
return new Function() {
public double evaluate(double x) { // in-line the code for speed
return(f.evaluate(x+h)-f.evaluate(x-h))/h/2.0;
}
};
}
/**
* Gets a second derivative function using a second order finite difference approximation.
* @param f Function f(x)
* @param h double change in x
* @return Function second derivate
*/
static public Function getSecond(final Function f, final double h) {
return new Function() {
public double evaluate(double x) { // in-line the code for speed
return(f.evaluate(x+h)-2*f.evaluate(x)+f.evaluate(x-h))/h/h;
}
};
}
/**
* Calculates the derivative using the Romberg scheme for Richardson extrapolation.
*
* This method runs until all Romberg rows are filled or until
* the step size drops below defaultNumericalPrecision or if the desired
* tolerance is reached.
*
* @param f the function
* @param x0 where derivative is to be calculated
* @param h initial step size
* @param tol desired accuracy
* @return first derivative
*/
static public double romberg(Function f, double x0, double h, double tol) {
int n = 6; // max. number of columns in the Romberg scheme
double[] d = new double[n];
d[0] = (f.evaluate(x0+h)-f.evaluate(x0-h))/h/2.0;
int error_code = 1;
for(int j = 1; j<=n-1; j++) {
d[j] = 0.0;
double d1 = d[0];
double h2 = h;
h *= 0.5;
if(h<Util.defaultNumericalPrecision) {
error_code = 2; /* step size less than defaultNumericalPrecision */
break;
}
d[0] = (f.evaluate(x0+h)-f.evaluate(x0-h))/h2;
for(int m = 4, i = 1; i<=j; i++, m *= 4) {
double d2 = d[i];
d[i] = (m*d[i-1]-d1)/(m-1);
d1 = d2;
}
if(Math.abs(d[j]-d[j-1])<tol) { /* desired accuracy reached */
return d[j];
}
}
throw new NumericMethodException("Derivative did not converge.", error_code, d[0]); //$NON-NLS-1$
}
/**
* Calculates the first derivative of a function at the given point.
*
* The current implementation uses the centered finite difference method but this may change.
*
* @param f the function
* @param x the x value
* @param h
* @return first derivative
*/
static public double first(Function f, double x, double h) {
return(f.evaluate(x+h)-f.evaluate(x-h))/h/2.0;
}
/**
* Calculates the first derivative of a function using the centered finite difference approximation.
* @param f the function
* @param x the x value
* @param h
* @return first derivatve
*/
static public double centered(Function f, double x, double h) {
return(f.evaluate(x+h)-f.evaluate(x-h))/h/2.0;
}
/**
* Calculates the first derivative of a function using the finite difference approximation toward decreasing x.
* @param f the function
* @param x the x value
* @param h
* @return first derivative
*/
static public double backward(Function f, double x, double h) {
return(f.evaluate(x-2*h)-4*f.evaluate(x-h)+3*f.evaluate(x))/h/2.0;
}
/**
* Calculates the first derivative of a function using the finite difference approximation toward increasing x.
* @param f the function
* @param x the x value
* @param h
* @return first derivative
*/
static public double forward(Function f, double x, double h) {
return(-f.evaluate(x+2*h)+4*f.evaluate(x+h)-3*f.evaluate(x))/h/2.0;
}
/**
* Gets the partial derivate of a multivariable function using the centered finite difference approximation.
*
* @param f MultiVarFunction
* @param x double[] variables
* @param n int index
* @param h double change in the varible with index i
* @return double
*/
static public double firstPartial(MultiVarFunction f, double[] x, int n, double h) {
double[] tempPlus = new double[x.length];
System.arraycopy(x, 0, tempPlus, 0, x.length);
tempPlus[n] += h;
double[] tempMinus = new double[x.length];
System.arraycopy(x, 0, tempMinus, 0, x.length);
tempMinus[n] -= h;
return(f.evaluate(tempPlus)-f.evaluate(tempMinus))/2.0/h;
}
/**
* Computes the second derivate using the centered finite difference approximation.
*
* @param f Function
* @param x double
* @param h double
* @return double
*/
static public double second(Function f, double x, double h) {
return(f.evaluate(x+h)-2*f.evaluate(x)+f.evaluate(x-h))/h/h;
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/