/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.math.interpolation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.ParallelArrayBinarySort;
import com.opengamma.util.tuple.DoublesPair;
import com.opengamma.util.tuple.FirstThenSecondPairComparator;
/**
*
*/
public class GridInterpolator2D extends Interpolator2D {
private static final long serialVersionUID = 1L;
//TODO this is really inefficient - needs to be changed in a similar way to 1D interpolation
/** The x interpolator */
private final Interpolator1D _xInterpolator;
/** The y interpolator */
private final Interpolator1D _yInterpolator;
/** The comparator */
private final FirstThenSecondPairComparator<Double, Double> _comparator;
/**
* @param xInterpolator The x interpolator, not null
* @param yInterpolator The y interpolator, not null
*/
public GridInterpolator2D(final Interpolator1D xInterpolator, final Interpolator1D yInterpolator) {
ArgumentChecker.notNull(xInterpolator, "x interpolator");
ArgumentChecker.notNull(yInterpolator, "y interpolator");
_xInterpolator = xInterpolator;
_yInterpolator = yInterpolator;
_comparator = FirstThenSecondPairComparator.INSTANCE_DOUBLES;
}
/**
* @param xInterpolator The x interpolator, not null
* @param yInterpolator The y interpolator, not null
* @param xExtrapolator The x extrapolator, not null
* @param yExtrapolator The y extrapolator, not null
*/
public GridInterpolator2D(final Interpolator1D xInterpolator, final Interpolator1D yInterpolator, final Interpolator1D xExtrapolator, final Interpolator1D yExtrapolator) {
ArgumentChecker.notNull(xInterpolator, "x interpolator");
ArgumentChecker.notNull(yInterpolator, "y interpolator");
ArgumentChecker.notNull(xExtrapolator, "x extrapolator");
ArgumentChecker.notNull(yExtrapolator, "y extrapolator");
_xInterpolator = new CombinedInterpolatorExtrapolator(xInterpolator, xExtrapolator);
_yInterpolator = new CombinedInterpolatorExtrapolator(yInterpolator, yExtrapolator);
_comparator = FirstThenSecondPairComparator.INSTANCE_DOUBLES;
}
public Map<Double, Interpolator1DDataBundle> getDataBundle(final Map<DoublesPair, Double> data) {
ArgumentChecker.notNull(data, "data");
final TreeMap<DoublesPair, Double> sorted = new TreeMap<>(_comparator);
sorted.putAll(data);
return testData(sorted);
}
public Map<Double, Interpolator1DDataBundle> getDataBundleFromSorted(final SortedMap<DoublesPair, Double> data) {
ArgumentChecker.notNull(data, "data");
return testData(data);
}
public Map<Double, Interpolator1DDataBundle> getDataBundleFromUnsorted(final double[] xInput, final double[] yInput, final double[] zInput, boolean mustCopy) {
ArgumentChecker.notNull(xInput, "xValues");
ArgumentChecker.notNull(yInput, "yValues");
ArgumentChecker.notNull(zInput, "zValues");
double[] xValues = mustCopy ? xInput.clone() : xInput;
double[] yValues = mustCopy ? yInput.clone() : yInput;
double[] zValues = mustCopy ? zInput.clone() : zInput;
Map<Double, Interpolator1DDataBundle> result = new TreeMap<Double, Interpolator1DDataBundle>();
ParallelArrayBinarySort.parallelBinarySort(xValues, yValues, zValues);
Double currX = xValues[0];
long currXBits = Double.doubleToRawLongBits(currX);
int currStartRange = 0;
for (int i = 0; i <= xValues.length; i++) {
if ((i == xValues.length) || (currXBits != Double.doubleToRawLongBits(xValues[i]))) {
//ParallelArrayBinarySort.parallelBinarySort(yValues, zValues, currStartRange, i - 1);
double[] currYValues = Arrays.copyOfRange(yValues, currStartRange, i);
double[] currZValues = Arrays.copyOfRange(zValues, currStartRange, i);
Interpolator1DDataBundle yzBundle = _yInterpolator.getDataBundle(currYValues, currZValues);
result.put(currX, yzBundle);
currStartRange = i;
if (i != xValues.length) {
currX = xValues[i];
currXBits = Double.doubleToRawLongBits(currX);
}
}
}
return result;
}
@Override
public Double interpolate(final Map<Double, Interpolator1DDataBundle> dataBundle, final DoublesPair value) {
ArgumentChecker.notNull(value, "value");
ArgumentChecker.notNull(dataBundle, "data bundle");
final Map<Double, Double> xData = new HashMap<>();
for (final Map.Entry<Double, Interpolator1DDataBundle> entry : dataBundle.entrySet()) {
xData.put(entry.getKey(), _yInterpolator.interpolate(entry.getValue(), value.getSecond()));
}
return _xInterpolator.interpolate(_xInterpolator.getDataBundle(xData), value.getFirst());
}
@Override
public Map<DoublesPair, Double> getNodeSensitivitiesForValue(final Map<Double, Interpolator1DDataBundle> dataBundle, final DoublesPair value) {
ArgumentChecker.notNull(value, "value");
ArgumentChecker.notNull(dataBundle, "data bundle");
final Map<Double, Double> xData = new HashMap<>();
final double[][] temp = new double[dataBundle.size()][];
int i = 0;
for (final Map.Entry<Double, Interpolator1DDataBundle> entry : dataBundle.entrySet()) {
//this is the sensitivity of the point projected onto a column of y-points to those points
temp[i++] = _yInterpolator.getNodeSensitivitiesForValue(entry.getValue(), value.getSecond());
xData.put(entry.getKey(), _yInterpolator.interpolate(entry.getValue(), value.getSecond()));
}
//this is the sensitivity of the point to the points projected onto y columns
final double[] xSense = _xInterpolator.getNodeSensitivitiesForValue(_xInterpolator.getDataBundle(xData), value.getFirst());
ArgumentChecker.isTrue(xSense.length == dataBundle.size(), "Number of x sensitivities {} must be equal to the data bundle size {}", xSense.length, dataBundle.size());
final Map<DoublesPair, Double> res = new HashMap<>();
double sense;
i = 0;
int j = 0;
for (final Map.Entry<Double, Interpolator1DDataBundle> entry : dataBundle.entrySet()) {
final double[] yValues = entry.getValue().getKeys();
for (j = 0; j < yValues.length; j++) {
sense = xSense[i] * temp[i][j];
res.put(DoublesPair.of(entry.getKey().doubleValue(), yValues[j]), sense);
}
i++;
}
return res;
}
private Map<Double, Interpolator1DDataBundle> testData(final Map<DoublesPair, Double> data) {
final Map<Double, Interpolator1DDataBundle> result = new TreeMap<>();
final Iterator<Map.Entry<DoublesPair, Double>> iterator = data.entrySet().iterator();
final Map.Entry<DoublesPair, Double> firstEntry = iterator.next();
double x = firstEntry.getKey().first;
Map<Double, Double> yzValues = new TreeMap<>();
yzValues.put(firstEntry.getKey().second, firstEntry.getValue());
while (iterator.hasNext()) {
final Map.Entry<DoublesPair, Double> nextEntry = iterator.next();
final double newX = nextEntry.getKey().first;
if (Double.doubleToLongBits(newX) != Double.doubleToLongBits(x)) {
final Interpolator1DDataBundle interpolatorData = _yInterpolator.getDataBundle(yzValues);
result.put(x, interpolatorData);
yzValues = new TreeMap<>();
yzValues.put(nextEntry.getKey().second, nextEntry.getValue());
x = newX;
} else {
yzValues.put(nextEntry.getKey().second, nextEntry.getValue());
}
if (!iterator.hasNext()) {
yzValues.put(nextEntry.getKey().second, nextEntry.getValue());
final Interpolator1DDataBundle interpolatorData = _yInterpolator.getDataBundle(yzValues);
result.put(x, interpolatorData);
}
}
return result;
}
public Interpolator1D getXInterpolator() {
return _xInterpolator;
}
public Interpolator1D getYInterpolator() {
return _yInterpolator;
}
@Override
public boolean equals(final Object o) {
if (o == null) {
return false;
}
if (!(o instanceof GridInterpolator2D)) {
return false;
}
final GridInterpolator2D other = (GridInterpolator2D) o;
return getXInterpolator().equals(other.getXInterpolator()) && getYInterpolator().equals(other.getYInterpolator());
}
@Override
public int hashCode() {
int hc = 1;
hc = (hc * 31) + getXInterpolator().hashCode();
hc = (hc * 31) + getYInterpolator().hashCode();
return hc;
}
}