/**
* Copyright (C) 2012 - 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.NotImplementedException;
import com.opengamma.analytics.math.interpolation.data.ArrayInterpolator1DDataBundle;
import com.opengamma.analytics.math.interpolation.data.InterpolationBoundedValues;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle;
import com.opengamma.util.ArgumentChecker;
/**
* A one-dimensional interpolator.
* The interpolation is linear on x y^2. The interpolator is used for interpolation on integrated variance for options.
* All values of y must be positive.
*/
public class TimeSquareInterpolator1D extends Interpolator1D {
private static final long serialVersionUID = 1L;
/* Level below which the value is consider to be 0. */
private static final double EPS = 1.0E-10;
@Override
public Double interpolate(final Interpolator1DDataBundle data, final Double value) {
ArgumentChecker.notNull(value, "Value to be interpolated must not be null");
ArgumentChecker.isTrue(value > 0, "Value should be stricly positive");
ArgumentChecker.notNull(data, "Data bundle must not be null");
final InterpolationBoundedValues boundedValues = data.getBoundedValues(value);
final double x1 = boundedValues.getLowerBoundKey();
final double y1 = boundedValues.getLowerBoundValue();
if (boundedValues.getLowerBoundIndex() == data.size() - 1) {
return y1;
}
final double x2 = boundedValues.getHigherBoundKey();
final double y2 = boundedValues.getHigherBoundValue();
final double w = (x2 - value) / (x2 - x1);
final double xy21 = x1 * y1 * y1;
final double xy22 = x2 * y2 * y2;
final double xy2 = w * xy21 + (1 - w) * xy22;
return Math.sqrt(xy2 / value);
}
@Override
public double firstDerivative(final Interpolator1DDataBundle data, final Double value) {
ArgumentChecker.notNull(value, "Value to be interpolated must not be null");
ArgumentChecker.isTrue(value > 0, "Value should be stricly positive");
ArgumentChecker.notNull(data, "Data bundle must not be null");
int lowerIndex = data.getLowerBoundIndex(value);
int index;
if (lowerIndex == data.size() - 1) {
index = data.size() - 2;
} else {
index = lowerIndex;
}
double x1 = data.getKeys()[index];
double y1 = data.getValues()[index];
double x2 = data.getKeys()[index + 1];
double y2 = data.getValues()[index + 1];
if ((y1 < EPS) || (y2 < EPS)) {
throw new NotImplementedException("node sensitivity not implemented when one node is 0 value");
}
final double w = (x2 - value) / (x2 - x1);
final double xy21 = x1 * y1 * y1;
final double xy22 = x2 * y2 * y2;
final double xy2 = w * xy21 + (1 - w) * xy22;
return 0.5 * (-Math.sqrt(xy2 / value) + (-xy21 + xy22) / (x2 - x1) / Math.sqrt(xy2 / value)) / value;
}
@Override
public double[] getNodeSensitivitiesForValue(final Interpolator1DDataBundle data, final Double value) {
ArgumentChecker.notNull(value, "Value to be interpolated must not be null");
ArgumentChecker.notNull(data, "Data bundle must not be null");
final int n = data.size();
final double[] resultSensitivity = new double[n];
final InterpolationBoundedValues boundedValues = data.getBoundedValues(value);
final double x1 = boundedValues.getLowerBoundKey();
final double y1 = boundedValues.getLowerBoundValue();
final int index = boundedValues.getLowerBoundIndex();
if (index == n - 1) {
resultSensitivity[n - 1] = 1.0;
return resultSensitivity;
}
final double x2 = boundedValues.getHigherBoundKey();
final double y2 = boundedValues.getHigherBoundValue();
if ((y1 < EPS) || (y2 < EPS)) {
throw new NotImplementedException("node sensitivity not implemented when one node is 0 value");
}
final double w = (x2 - value) / (x2 - x1);
final double xy21 = x1 * y1 * y1;
final double xy22 = x2 * y2 * y2;
final double xy2 = w * xy21 + (1 - w) * xy22;
final double resultValue = Math.sqrt(xy2 / value);
final double resultValueBar = 1.0;
final double xy2Bar = 0.5 / resultValue / value * resultValueBar;
final double xy21Bar = w * xy2Bar;
final double xy22Bar = (1 - w) * xy2Bar;
final double y2Bar = 2 * x2 * y2 * xy22Bar;
final double y1Bar = 2 * x1 * y1 * xy21Bar;
resultSensitivity[index] = y1Bar;
resultSensitivity[index + 1] = y2Bar;
return resultSensitivity;
}
@Override
public Interpolator1DDataBundle getDataBundle(final double[] x, final double[] y) {
ArgumentChecker.notNull(y, "y");
int nY = y.length;
for (int i = 0; i < nY; ++i) {
ArgumentChecker.isTrue(y[i] >= 0.0, "All values in y must be positive");
}
return new ArrayInterpolator1DDataBundle(x, y);
}
@Override
public Interpolator1DDataBundle getDataBundleFromSortedArrays(final double[] x, final double[] y) {
ArgumentChecker.notNull(y, "y");
int nY = y.length;
for (int i = 0; i < nY; ++i) {
ArgumentChecker.isTrue(y[i] >= 0.0, "All values in y must be positive");
}
return new ArrayInterpolator1DDataBundle(x, y, true);
}
}