com.opengamma.strata.math.impl.interpolation.CubicSplineSolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of strata-math Show documentation
Show all versions of strata-math Show documentation
Mathematic support for Strata
/*
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.math.impl.interpolation;
import static com.opengamma.strata.math.impl.matrix.MatrixAlgebraFactory.OG_ALGEBRA;
import java.util.Arrays;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.math.impl.linearalgebra.LUDecompositionCommons;
import com.opengamma.strata.math.impl.linearalgebra.LUDecompositionResult;
import com.opengamma.strata.math.linearalgebra.Decomposition;
/**
* Abstract class of solving cubic spline problem
* Implementation depends on endpoint conditions
* "solve" for 1-dimensional problem and "solveMultiDim" for multi-dimensional problem should be implemented in inherited classes
* "getKnotsMat1D" is overridden in certain cases
*/
abstract class CubicSplineSolver {
private final Decomposition _luObj = new LUDecompositionCommons();
/**
* One-dimensional cubic spline
* If (xValues length) = (yValues length), Not-A-Knot endpoint conditions are used
* If (xValues length) + 2 = (yValues length), Clamped endpoint conditions are used
* @param xValues X values of data
* @param yValues Y values of data
* @return Coefficient matrix whose i-th row vector is (a_0,a_1,...) for
* i-th intervals, where a_0,a_1,... are coefficients of f(x) = a_0 + a_1 x^1 + ....
* Note that the degree of polynomial is NOT necessarily 3
*/
public abstract DoubleMatrix solve(double[] xValues, double[] yValues);
/**
* One-dimensional cubic spline
* If (xValues length) = (yValues length), Not-A-Knot endpoint conditions are used
* If (xValues length) + 2 = (yValues length), Clamped endpoint conditions are used
* @param xValues X values of data
* @param yValues Y values of data
* @return Array of matrices: the 0-th element is Coefficient Matrix (same as the solve method above),
* the i-th element is \frac{\partial a^{i-1}_j}{\partial yValues_k}
* where a_0^i,a_1^i,... are coefficients of f^i(x) = a_0^i + a_1^i (x - xValues_{i}) + .... with x \in [xValues_{i}, xValues_{i+1}]
*/
public abstract DoubleMatrix[] solveWithSensitivity(double[] xValues, double[] yValues);
/**
* Multi-dimensional cubic spline
* If (xValues length) = (yValuesMatrix NumberOfColumn), Not-A-Knot endpoint conditions are used
* If (xValues length) + 2 = (yValuesMatrix NumberOfColumn), Clamped endpoint conditions are used
* @param xValues X values of data
* @param yValuesMatrix Y values of data, where NumberOfRow defines dimension of the spline
* @return A set of coefficient matrices whose i-th row vector is (a_0,a_1,...) for the i-th interval,
* where a_0,a_1,... are coefficients of f(x) = a_0 + a_1 x^1 + ....
* Each matrix corresponds to an interpolation (xValues, yValuesMatrix RowVector)
* Note that the degree of polynomial is NOT necessarily 3
*/
public abstract DoubleMatrix[] solveMultiDim(double[] xValues, DoubleMatrix yValuesMatrix);
/**
* @param xValues X values of data
* @return X values of knots (Note that these are NOT necessarily xValues if nDataPts=2,3)
*/
public DoubleArray getKnotsMat1D(double[] xValues) {
return DoubleArray.copyOf(xValues);
}
/**
* @param xValues X values of Data
* @return {xValues[1]-xValues[0], xValues[2]-xValues[1],...}
* xValues (and corresponding yValues) should be sorted before calling this method
*/
protected double[] getDiffs(double[] xValues) {
int nDataPts = xValues.length;
double[] res = new double[nDataPts - 1];
for (int i = 0; i < nDataPts - 1; ++i) {
res[i] = xValues[i + 1] - xValues[i];
}
return res;
}
/**
* @param xValues X values of Data
* @param yValues Y values of Data
* @param intervals {xValues[1]-xValues[0], xValues[2]-xValues[1],...}
* @param solnVector Values of second derivative at knots
* @return Coefficient matrix whose i-th row vector is {a_0,a_1,...} for i-th intervals,
* where a_0,a_1,... are coefficients of f(x) = a_0 + a_1 x^1 + ....
*/
protected DoubleMatrix getCommonSplineCoeffs(double[] xValues, double[] yValues, double[] intervals, double[] solnVector) {
int nDataPts = xValues.length;
double[][] res = new double[nDataPts - 1][4];
for (int i = 0; i < nDataPts - 1; ++i) {
res[i][0] = solnVector[i + 1] / 6. / intervals[i] - solnVector[i] / 6. / intervals[i];
res[i][1] = 0.5 * solnVector[i];
res[i][2] = yValues[i + 1] / intervals[i] - yValues[i] / intervals[i] - intervals[i] * solnVector[i] /
2. - intervals[i] * solnVector[i + 1] / 6. + intervals[i] * solnVector[i] / 6.;
res[i][3] = yValues[i];
}
return DoubleMatrix.copyOf(res);
}
/**
* @param intervals {xValues[1]-xValues[0], xValues[2]-xValues[1],...}
* @param solnMatrix Sensitivity of second derivatives (x 0.5)
* @return Array of i coefficient matrices \frac{\partial a^i_j}{\partial y_k}
*/
protected DoubleMatrix[] getCommonSensitivityCoeffs(double[] intervals, double[][] solnMatrix) {
int nDataPts = intervals.length + 1;
double[][][] res = new double[nDataPts - 1][4][nDataPts];
for (int i = 0; i < nDataPts - 1; ++i) {
res[i][3][i] = 1.;
res[i][2][i + 1] = 1. / intervals[i];
res[i][2][i] = -1. / intervals[i];
for (int k = 0; k < nDataPts; ++k) {
res[i][0][k] = solnMatrix[i + 1][k] / 6. / intervals[i] - solnMatrix[i][k] / 6. / intervals[i];
res[i][1][k] = 0.5 * solnMatrix[i][k];
res[i][2][k] += -intervals[i] * solnMatrix[i][k] / 2. - intervals[i] * solnMatrix[i + 1][k] /
6. + intervals[i] * solnMatrix[i][k] / 6.;
}
}
DoubleMatrix[] resMat = new DoubleMatrix[nDataPts - 1];
for (int i = 0; i < nDataPts - 1; ++i) {
resMat[i] = DoubleMatrix.copyOf(res[i]);
}
return resMat;
}
/**
* Cubic spline and its node sensitivity are respectively obtained by solving a linear problem Ax=b
* where A is a square matrix and x,b are vector and AN=L where N,L are matrices
* @param xValues X values of data
* @param yValues Y values of data
* @param intervals {xValues[1]-xValues[0], xValues[2]-xValues[1],...}
* @param toBeInv The matrix A
* @param commonVector The vector b
* @param commonVecSensitivity The matrix L
* @return Coefficient matrices of interpolant (x) and its node sensitivity (N)
*/
protected DoubleMatrix[] getCommonCoefficientWithSensitivity(double[] xValues, double[] yValues, double[] intervals,
double[][] toBeInv, double[] commonVector,
double[][] commonVecSensitivity) {
int nDataPts = xValues.length;
DoubleArray[] soln = this.combinedMatrixEqnSolver(toBeInv, commonVector, commonVecSensitivity);
DoubleMatrix[] res = new DoubleMatrix[nDataPts];
res[0] = getCommonSplineCoeffs(xValues, yValues, intervals, soln[0].toArray());
double[][] solnMatrix = new double[nDataPts][nDataPts];
for (int i = 0; i < nDataPts; ++i) {
for (int j = 0; j < nDataPts; ++j) {
solnMatrix[i][j] = soln[j + 1].get(i);
}
}
DoubleMatrix[] tmp = getCommonSensitivityCoeffs(intervals, solnMatrix);
System.arraycopy(tmp, 0, res, 1, nDataPts - 1);
return res;
}
/**
* Cubic spline is obtained by solving a linear problem Ax=b where A is a square matrix and x,b are vector
* @param intervals the intervals
* @return Endpoint-independent part of the matrix A
*/
protected double[][] getCommonMatrixElements(double[] intervals) {
int nDataPts = intervals.length + 1;
double[][] res = new double[nDataPts][nDataPts];
for (int i = 0; i < nDataPts; ++i) {
Arrays.fill(res[i], 0.);
}
for (int i = 1; i < nDataPts - 1; ++i) {
res[i][i - 1] = intervals[i - 1];
res[i][i] = 2. * (intervals[i - 1] + intervals[i]);
res[i][i + 1] = intervals[i];
}
return res;
}
/**
* Cubic spline is obtained by solving a linear problem Ax=b where A is a square matrix and x,b are vector
* @param yValues Y values of Data
* @param intervals {xValues[1]-xValues[0], xValues[2]-xValues[1],...}
* @return Endpoint-independent part of vector b
*/
protected double[] getCommonVectorElements(double[] yValues, double[] intervals) {
int nDataPts = yValues.length;
double[] res = new double[nDataPts];
Arrays.fill(res, 0.);
for (int i = 1; i < nDataPts - 1; ++i) {
res[i] = 6. * yValues[i + 1] / intervals[i] - 6. * yValues[i] / intervals[i] - 6. *
yValues[i] / intervals[i - 1] + 6. * yValues[i - 1] / intervals[i - 1];
}
return res;
}
/**
* Node sensitivity is obtained by solving a linear problem AN = L where A,N,L are matrices
* @param intervals {xValues[1]-xValues[0], xValues[2]-xValues[1],...}
* @return The matrix L
*/
protected double[][] getCommonVectorSensitivity(double[] intervals) {
int nDataPts = intervals.length + 1;
double[][] res = new double[nDataPts][nDataPts];
for (int i = 0; i < nDataPts; ++i) {
Arrays.fill(res[i], 0.);
}
for (int i = 1; i < nDataPts - 1; ++i) {
res[i][i - 1] = 6. / intervals[i - 1];
res[i][i] = -6. / intervals[i] - 6. / intervals[i - 1];
res[i][i + 1] = 6. / intervals[i];
}
return res;
}
/**
* Cubic spline is obtained by solving a linear problem Ax=b where A is a square matrix and x,b are vector
* This can be done by LU decomposition
* @param doubMat Matrix A
* @param doubVec Vector B
* @return Solution to the linear equation, x
*/
protected double[] matrixEqnSolver(double[][] doubMat, double[] doubVec) {
LUDecompositionResult result = _luObj.apply(DoubleMatrix.copyOf(doubMat));
double[][] lMat = result.getL().toArray();
double[][] uMat = result.getU().toArray();
DoubleArray doubVecMod = ((DoubleArray) OG_ALGEBRA.multiply(result.getP(), DoubleArray.copyOf(doubVec)));
return backSubstitution(uMat, forwardSubstitution(lMat, doubVecMod));
}
/**
* Cubic spline and its node sensitivity are respectively obtained by solving a linear problem Ax=b
* where A is a square matrix and x,b are vector and AN=L where N,L are matrices
* @param doubMat1 The matrix A
* @param doubVec The vector b
* @param doubMat2 The matrix L
* @return The solutions to the linear systems, x,N
*/
protected DoubleArray[] combinedMatrixEqnSolver(double[][] doubMat1, double[] doubVec, double[][] doubMat2) {
int nDataPts = doubVec.length;
LUDecompositionResult result = _luObj.apply(DoubleMatrix.copyOf(doubMat1));
double[][] lMat = result.getL().toArray();
double[][] uMat = result.getU().toArray();
DoubleMatrix pMat = result.getP();
DoubleArray doubVecMod = ((DoubleArray) OG_ALGEBRA.multiply(pMat, DoubleArray.copyOf(doubVec)));
DoubleMatrix doubMat2Matrix = DoubleMatrix.copyOf(doubMat2);
DoubleArray[] res = new DoubleArray[nDataPts + 1];
res[0] = DoubleArray.copyOf(backSubstitution(uMat, forwardSubstitution(lMat, doubVecMod)));
for (int i = 0; i < nDataPts; ++i) {
DoubleArray doubMat2Colum = doubMat2Matrix.column(i);
DoubleArray doubVecMod2 = ((DoubleArray) OG_ALGEBRA.multiply(pMat, doubMat2Colum));
res[i + 1] = DoubleArray.copyOf(backSubstitution(uMat, forwardSubstitution(lMat, doubVecMod2)));
}
return res;
}
/**
* Linear problem Ax=b is solved by forward substitution if A is lower triangular.
*
* @param lMat Lower triangular matrix
* @param doubVec Vector b
* @return Solution to the linear equation, x
*/
private double[] forwardSubstitution(double[][] lMat, DoubleArray doubVec) {
int size = lMat.length;
double[] res = new double[size];
for (int i = 0; i < size; ++i) {
double tmp = doubVec.get(i) / lMat[i][i];
for (int j = 0; j < i; ++j) {
tmp -= lMat[i][j] * res[j] / lMat[i][i];
}
res[i] = tmp;
}
return res;
}
/**
* Linear problem Ax=b is solved by backward substitution if A is upper triangular.
*
* @param uMat Upper triangular matrix
* @param doubVec Vector b
* @return Solution to the linear equation, x
*/
private double[] backSubstitution(double[][] uMat, double[] doubVec) {
int size = uMat.length;
double[] res = new double[size];
for (int i = size - 1; i > -1; --i) {
double tmp = doubVec[i] / uMat[i][i];
for (int j = i + 1; j < size; ++j) {
tmp -= uMat[i][j] * res[j] / uMat[i][i];
}
res[i] = tmp;
}
return res;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy