/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.finitedifference;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.math.FunctionUtils;
import com.opengamma.util.ArgumentChecker;
/**
*
*/
public class ExponentialMeshing extends MeshingFunction {
private final double[] _fpValues;
private final UniformMeshing _um;
private final double _theta;
private final double _eta;
private final double _lambda;
private final boolean _linear;
private final double _l;
private final double _r;
/**
* creates a non-uniform set of points according to the formula $x_i = \theta + \eta*\exp(\lambda z_i)$, where the points run from
* $x_0$ to $x_{N-1}$ (i.e. there are N points), $\eta = (x_{N-1} - x_0)/(\exp(\lambda) - 1)$ and $\theta = x_0 - \eta$. The points $z_i$ are uniform on (0,1) and
* given by $z_i = i/(N-1)$.
* @param lowerBound The value of $x_0$
* @param upperBound The value of $x_{N-1}$
* @param nPoints Number of Points (equal to N in the above formula)
* @param lambda Bunching parameter. $\lambda = 0$ is uniform, $\lambda > 0$ gives a high density of points near $x_0$ and $\lambda < 0$ gives a high density
* of points near $x_{N-1}$
*/
public ExponentialMeshing(final double lowerBound, final double upperBound, final int nPoints, final double lambda) {
super(nPoints);
Validate.isTrue(upperBound > lowerBound, "need upperBound>lowerBound");
_l = lowerBound;
_r = upperBound;
_lambda = lambda;
if (lambda == 0.0) {
_linear = true;
_theta = lowerBound;
_eta = (upperBound - lowerBound);
} else {
_linear = false;
_eta = (upperBound - lowerBound) / (Math.exp(lambda) - 1);
_theta = lowerBound - _eta;
}
_um = new UniformMeshing(nPoints);
_fpValues = null;
}
/**
* creates a non-uniform set of points according to the formula $x_i = \theta + \eta*\exp(\lambda z_i)$, where the points run from
* $x_0$ to $x_{N-1}$ (i.e. there are N points), $\eta = (x_{N-1} - x_0)/(\exp(\lambda) - 1)$ and $\theta = x_0 - \eta$.
* The points $z_i$ are are close as possible to uniform on (0,1) while allowing the <em>fixedPoints</em> to be in the set of points.
* @param lowerBound The value of $x_0$
* @param upperBound The value of $x_{N-1}$
* @param nPoints Number of Points (equal to N in the above formula). The number of points must exceed the number of fixed points by at least 2.
* @param lambda Bunching parameter. $\lambda = 0$ is uniform, $\lambda > 0$ gives a high density of points near $x_0$ and $\lambda < 0$ gives a high density
* of points near $x_{N-1}$
* @param fixedPoints set of points that must be included. These must be within the lower and upper bound (exclusive)
*/
public ExponentialMeshing(final double lowerBound, final double upperBound, final int nPoints, final double lambda, final double[] fixedPoints) {
super(nPoints);
Validate.isTrue(upperBound > lowerBound, "need upperBound>lowerBound");
ArgumentChecker.notNull(fixedPoints, "null fixedPoints");
_lambda = lambda;
_l = lowerBound;
_r = upperBound;
_fpValues = FunctionUtils.unique(fixedPoints);
int m = _fpValues.length;
final double[] fp = new double[m];
if (lambda == 0.0) {
_linear = true;
_theta = lowerBound;
_eta = (upperBound - lowerBound);
for (int ii = 0; ii < m; ii++) {
fp[ii] = (fixedPoints[ii] - _theta) / _eta;
}
} else {
_linear = false;
_eta = (upperBound - lowerBound) / (Math.exp(lambda) - 1);
_theta = lowerBound - _eta;
for (int ii = 0; ii < m; ii++) {
fp[ii] = Math.log((_fpValues[ii] - _theta) / _eta) / lambda;
}
}
_um = new UniformMeshing(nPoints, fp);
}
@Override
public Double evaluate(final Integer i) {
Validate.isTrue(i >= 0 && i < getNumberOfPoints(), "i out of range");
if (i == 0) {
return _l;
}
if (i == getNumberOfPoints() - 1) {
return _r;
}
//short cut if required point is one of the specified fixed points
if (_fpValues != null) {
int index = _um.getFixedPointIndex(i);
if (index >= 0) {
return _fpValues[index];
}
}
final double z = _um.evaluate(i);
if (_linear) {
return _theta + _eta * z;
}
return _theta + _eta * Math.exp(z * _lambda);
}
}