All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.math.impl.interpolation.CubicSplineSolver Maven / Gradle / Ivy

There is a newer version: 2.12.46
Show newest version
/*
 * 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