/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.finitedifference; import java.util.Arrays; import com.opengamma.analytics.math.FunctionUtils; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.NaturalCubicSplineInterpolator1D; import com.opengamma.analytics.math.interpolation.data.Interpolator1DCubicSplineDataBundle; import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle; import com.opengamma.util.ArgumentChecker; /** * */ public class UniformMeshing extends MeshingFunction { private static final Interpolator1D INTERPOLATOR = new NaturalCubicSplineInterpolator1D(); private final Interpolator1DDataBundle _db; private final int[] _fpIndicies; private final double[] _fpValues; /** * Crates a set of points (mesh) equally spaced between 0.0 and 1.0 inclusive * @param nPoints Number of points in the mesh */ public UniformMeshing(int nPoints) { super(nPoints); _db = null; _fpIndicies = null; _fpValues = null; } /** * Crates a set of points (mesh) approximately equally spaced between 0.0 and 1.0 inclusive, such that the specified <em>fixedPoints</em> are included as points * @param nPoints Number of points in the mesh * @param fixedPoints Set of points that must be in the mesh. <b>Note:</b> the mesh can be far from uniform if fixed-points are close together and/or a small * number of points are used */ public UniformMeshing(int nPoints, final double[] fixedPoints) { super(nPoints); ArgumentChecker.notNull(fixedPoints, "null fixed Points"); // sort and remove duplicates, preserving order _fpValues = FunctionUtils.unique(fixedPoints); // remove any fixed points on the boundary // int nn = temp.length; // if (nn > 0 && temp[0] == 0.0) { // temp = Arrays.copyOfRange(temp, 1, nn - 1); // } // nn = temp.length; // if (nn > 0 && temp[nn - 1] == 1.0) { // temp = Arrays.copyOfRange(temp, 0, nn - 2); // } // _fpValues = temp; final int m = _fpValues.length; if (m == 0) { _db = null; _fpIndicies = null; } else { if (_fpValues[0] <= 0.0 || _fpValues[m - 1] >= 1.0) { throw new IllegalArgumentException("fixedPoints must be between 0.0 and 1.0 exclusive"); } if (super.getNumberOfPoints() - m < 2) { throw new IllegalArgumentException("insufficient points to form grid"); } _fpIndicies = new int[m]; for (int ii = 0; ii < m; ii++) { _fpIndicies[ii] = (int) Math.round((nPoints - 1) * _fpValues[ii]); } // prevent points sharing index if (m != FunctionUtils.unique(_fpIndicies).length) { for (int ii = 1; ii < m; ii++) { int step = _fpIndicies[ii] - _fpIndicies[ii - 1]; if (step < 1) { _fpIndicies[ii] += 1 - step; } } } if (_fpIndicies[0] == 0) { _fpIndicies[0] = 1; for (int ii = 1; ii < m; ii++) { if (_fpIndicies[ii - 1] == _fpIndicies[ii]) { _fpIndicies[ii] = _fpIndicies[ii] + 1; } else { break; // no more work to do } } } if (_fpIndicies[m - 1] >= nPoints - 1) { _fpIndicies[m - 1] = nPoints - 2; for (int ii = 1; ii < m - 1; ii++) { int step = _fpIndicies[m - ii] - _fpIndicies[m - ii - 1]; if (step < 1) { _fpIndicies[m - ii - 1] += step - 1; } else { break; } } } final double[] x = new double[m + 2]; final double[] y = new double[m + 2]; x[0] = 0.0; for (int ii = 0; ii < m; ii++) { x[ii + 1] = _fpIndicies[ii]; } x[m + 1] = nPoints - 1; y[0] = 0.0; System.arraycopy(_fpValues, 0, y, 1, m); y[m + 1] = 1.0; Interpolator1DDataBundle data = INTERPOLATOR.getDataBundleFromSortedArrays(x, y); final double grad = 1.0 / (nPoints - 1); _db = new Interpolator1DCubicSplineDataBundle(data, grad, grad); } } protected int getFixedPointIndex(int i) { return Arrays.binarySearch(_fpIndicies, i); } @Override public Double evaluate(Integer x) { if (x < 0 || x >= getNumberOfPoints()) { throw new IllegalArgumentException("index out of range"); } if (x == 0) { return 0.0; } if (x == getNumberOfPoints() - 1) { return 1.0; } if (_db == null) { return ((double) x) / (getNumberOfPoints() - 1); } // avoid interpolator lookup if requested value is a fixed value final int index = getFixedPointIndex(x); if (index >= 0) { return _fpValues[index]; } return INTERPOLATOR.interpolate(_db, (double) x); } }