/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.math.interpolation;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle;
import com.opengamma.analytics.math.minimization.ParameterLimitsTransform;
/**
* This allows one to fit an interpolated curve, where the y-coordinates of the curve must lie in a certain range (e.g. nowhere must the
* curve be below 0 or above 1 for any value of x), using any base interpolator and a function (and its inverse) that maps the constrained range
* (e.g. 0 to 1 inclusive) to -infinity to +infinity. <p>
* Let y(x) be an interpolated value and y_i the set of node values. An interpolated value can
* be written as y(x) = I(x, (x_i,y_i)) - for a given interpolator, it is a function of x, and the node coordinates (x_i,y_i). However even
* if all the node values (y_i) are constrained to the required range, there is no guarantee that y(x) will be in the range for all x (it depends
* on the interpolator).<p>
* Now let y*(x) and y*_i be the corresponding transformed values (i.e. the range is the entire real line), so y* = f(y). We may wish to
* work directly with the transformed values y*_i (since this allows unconstrained optimisation to find the values, where the interpolated curve
* is part of some larger calibration). To this end we have y(x) = f^-1[I(x,(x_i,y*_i))], which is not strictly an interpolator since the curve
* y(x) does not go through the points y*_i. We could of course write y(x) = f^-1[I(x,(x_i,f(y_i)))], which is a true interpolator
* (and could be write as I*(x,(x_i,y_i)) ). In both these cases y(x) is guaranteed to be in the range regardless of the base interpolator used.
*/
public class TransformedInterpolator1D extends Interpolator1D {
private static final long serialVersionUID = 1L;
private final ParameterLimitsTransform _transform;
private final Interpolator1D _base;
/**
*
* @param baseInterpolator The interpolator used for interpolating in the transformed space
* @param transform a two way mapping between a limited range and the real line
*/
public TransformedInterpolator1D(final Interpolator1D baseInterpolator, final ParameterLimitsTransform transform) {
Validate.notNull(baseInterpolator, "null baseInterpolator");
Validate.notNull(transform, "null transform");
_base = baseInterpolator;
_transform = transform;
}
@Override
public Double interpolate(final Interpolator1DDataBundle data, final Double value) {
return _transform.inverseTransform(_base.interpolate(data, value));
}
@Override
public double firstDerivative(final Interpolator1DDataBundle data, final Double value) {
return _transform.inverseTransformGradient(_base.interpolate(data, value)) * _base.firstDerivative(data, value);
}
@Override
public double[] getNodeSensitivitiesForValue(final Interpolator1DDataBundle data, final Double value) {
final double yStar = _base.interpolate(data, value);
final double grad = _transform.inverseTransformGradient(yStar);
final double[] temp = _base.getNodeSensitivitiesForValue(data, value);
final int n = temp.length;
for (int i = 0; i < n; i++) {
temp[i] *= grad;
}
return temp;
}
/**
* The node values must be in the transformed space
* @param x The positions of the nodes (not necessarily in order)
* @param y The values of the nodes - these must be in the transformed space
* @return a data bundle
*/
@Override
public Interpolator1DDataBundle getDataBundle(final double[] x, final double[] y) {
return _base.getDataBundle(x, y);
}
/**
* The node values must be in the transformed space
* @param x The positions of the nodes. <b>These must be in ascending order</b>
* @param y The values of the nodes - these must be in the transformed space
* @return a data bundle
*/
@Override
public Interpolator1DDataBundle getDataBundleFromSortedArrays(final double[] x, final double[] y) {
return _base.getDataBundleFromSortedArrays(x, y);
}
}