/** * @param pp PiecewisePolynomialResult * @param xKey * @return Values of piecewise polynomial functions at xKey When _dim in PiecewisePolynomialResult * is greater than 1, i.e., the struct contains multiple splines, an element in the return * values corresponds to each spline */ public DoubleMatrix1D evaluate(final PiecewisePolynomialResult pp, final double xKey) { ArgumentChecker.notNull(pp, "pp"); ArgumentChecker.isFalse(Double.isNaN(xKey), "xKey containing NaN"); ArgumentChecker.isFalse(Double.isInfinite(xKey), "xKey containing Infinity"); final double[] knots = pp.getKnots().getData(); final int nKnots = knots.length; final DoubleMatrix2D coefMatrix = pp.getCoefMatrix(); final int dim = pp.getDimensions(); double[] res = new double[dim]; int indicator = FunctionUtils.getLowerBoundIndex(knots, xKey); if (indicator == nKnots - 1) { indicator--; // there is 1 less interval that knots } for (int j = 0; j < dim; ++j) { final double[] coefs = coefMatrix.getRowVector(dim * indicator + j, false).getData(); res[j] = getValue(coefs, xKey, knots[indicator]); ArgumentChecker.isFalse(Double.isInfinite(res[j]), "Too large input"); ArgumentChecker.isFalse(Double.isNaN(res[j]), "Too large input"); } return new DoubleMatrix1D(res, false); }
/** * Calculate the instrument sensitivity from the yield sensitivity, the jacobian matrix and the * coupon sensitivity. * * @param curveSensitivities The sensitivity to points of the yield curve. * @param curves The curve bundle. * @param couponSensitivity The sensitivity * @param jacobian The present value coupon sensitivity. * @return The instrument quote/rate sensitivity. */ public DoubleMatrix1D calculateFromPresentValue( final Map<String, List<DoublesPair>> curveSensitivities, final YieldCurveBundle curves, final DoubleMatrix1D couponSensitivity, final DoubleMatrix2D jacobian) { final DoubleArrayList resultList = new DoubleArrayList(); for (final String curveName : curves.getAllNames()) { final DoubleMatrix1D nodeSensitivity = new DoubleMatrix1D( (_parameterSensitivityCalculator.pointToParameterSensitivity( curveSensitivities.get(curveName), curves.getCurve(curveName))) .toArray(new Double[0])); final int n = nodeSensitivity.getNumberOfElements(); final DoubleMatrix2D inverseJacobian = MATRIX_ALGEBRA.getInverse(jacobian); for (int i = 0; i < n; i++) { double sum = 0; for (int j = 0; j < n; j++) { sum += -couponSensitivity.getEntry(i) * inverseJacobian.getEntry(j, i) * nodeSensitivity.getEntry(j); } resultList.add(sum); } } return new DoubleMatrix1D(resultList.toDoubleArray()); }
/** Tests solve AX = B from A and B. */ public void solveMatrix() { final CholeskyDecompositionResult result = CDOG.evaluate(A5); double[][] b = new double[][] {{1.0, 2.0}, {2.0, 3.0}, {3.0, 4.0}, {4.0, -2.0}, {-1.0, -1.0}}; DoubleMatrix2D x = result.solve(new DoubleMatrix2D(b)); DoubleMatrix2D ax = (DoubleMatrix2D) ALGEBRA.multiply(A5, x); ArrayAsserts.assertArrayEquals( "Cholesky decomposition OpenGamma - solve", b[0], ax.getData()[0], 1.0E-10); ArrayAsserts.assertArrayEquals( "Cholesky decomposition OpenGamma - solve", b[1], ax.getData()[1], 1.0E-10); }
@Test public void test() { final DoubleMatrix2D matrix = CALCULATOR.evaluate(TS1, TS2); assertEquals(matrix.getNumberOfRows(), 2); assertEquals(matrix.getNumberOfColumns(), 2); assertEquals(matrix.getEntry(0, 0), 4. / 3, EPS); assertEquals(matrix.getEntry(1, 0), -4. / 3, EPS); assertEquals(matrix.getEntry(0, 1), -4. / 3, EPS); assertEquals(matrix.getEntry(1, 1), 4. / 3, EPS); }
/** * @param pp PiecewisePolynomialResult * @param xKeys * @return Values of piecewise polynomial functions at xKeys When _dim in * PiecewisePolynomialResult is greater than 1, i.e., the struct contains multiple piecewise * polynomials, one element of return vector of DoubleMatrix2D corresponds to each piecewise * polynomial */ public DoubleMatrix2D[] evaluate(final PiecewisePolynomialResult pp, final double[][] xKeys) { ArgumentChecker.notNull(pp, "pp"); ArgumentChecker.notNull(xKeys, "xKeys"); final int keyLength = xKeys[0].length; final int keyDim = xKeys.length; for (int j = 0; j < keyDim; ++j) { for (int i = 0; i < keyLength; ++i) { ArgumentChecker.isFalse(Double.isNaN(xKeys[j][i]), "xKeys containing NaN"); ArgumentChecker.isFalse(Double.isInfinite(xKeys[j][i]), "xKeys containing Infinity"); } } final double[] knots = pp.getKnots().getData(); final int nKnots = knots.length; final DoubleMatrix2D coefMatrix = pp.getCoefMatrix(); final int dim = pp.getDimensions(); double[][][] res = new double[dim][keyDim][keyLength]; for (int k = 0; k < dim; ++k) { for (int l = 0; l < keyDim; ++l) { for (int j = 0; j < keyLength; ++j) { int indicator = 0; if (xKeys[l][j] < knots[1]) { indicator = 0; } else { for (int i = 1; i < nKnots - 1; ++i) { if (knots[i] <= xKeys[l][j]) { indicator = i; } } } final double[] coefs = coefMatrix.getRowVector(dim * indicator + k, false).getData(); res[k][l][j] = getValue(coefs, xKeys[l][j], knots[indicator]); ArgumentChecker.isFalse(Double.isInfinite(res[k][l][j]), "Too large input"); ArgumentChecker.isFalse(Double.isNaN(res[k][l][j]), "Too large input"); } } } DoubleMatrix2D[] resMat = new DoubleMatrix2D[dim]; for (int i = 0; i < dim; ++i) { resMat[i] = DoubleMatrix2D.noCopy(res[i]); } return resMat; }
@Override protected void buildMessage( final FudgeSerializer serializer, final MutableFudgeMsg message, final DoubleMatrix2D object) { serializer.addToMessage(message, DATA_FIELD_NAME, null, object.getData()); }
/** * @param pp * @param xKeys * @return Second derivatives of piecewise polynomial functions at xKeys When _dim in * PiecewisePolynomialResult is greater than 1, i.e., the struct contains multiple piecewise * polynomials, a row vector of return value corresponds to each piecewise polynomial */ public DoubleMatrix2D differentiateTwice( final PiecewisePolynomialResult pp, final double[] xKeys) { ArgumentChecker.notNull(pp, "pp"); ArgumentChecker.isFalse(pp.getOrder() < 3, "polynomial degree < 2"); final double[][] coefs = pp.getCoefMatrix().getData(); final double[] knots = pp.getKnots().getData(); final int nKnots = pp.getNumberOfIntervals() + 1; final int nCoefs = pp.getOrder(); final int dim = pp.getDimensions(); double[][] res = new double[dim * (nKnots - 1)][nCoefs - 2]; for (int i = 0; i < dim * (nKnots - 1); ++i) { Arrays.fill(res[i], 0.); } for (int i = 0; i < dim * (nKnots - 1); ++i) { for (int j = 0; j < nCoefs - 2; ++j) { res[i][j] = coefs[i][j] * (nCoefs - j - 1) * (nCoefs - j - 2); } } PiecewisePolynomialResult ppDiff = new PiecewisePolynomialResult( new DoubleMatrix1D(knots), DoubleMatrix2D.noCopy(res), nCoefs - 1, pp.getDimensions()); return evaluate(ppDiff, xKeys); }
/** * @param pp PiecewisePolynomialResult * @param initialKey * @param xKeys * @return Integral of piecewise polynomial between initialKey and xKeys */ public DoubleMatrix1D integrate( final PiecewisePolynomialResult pp, final double initialKey, final double[] xKeys) { ArgumentChecker.notNull(pp, "pp"); ArgumentChecker.notNull(xKeys, "xKeys"); ArgumentChecker.isFalse(Double.isNaN(initialKey), "initialKey containing NaN"); ArgumentChecker.isFalse(Double.isInfinite(initialKey), "initialKey containing Infinity"); ArgumentChecker.isTrue(pp.getDimensions() == 1, "Dimension should be 1"); final double[] knots = pp.getKnots().getData(); final int nCoefs = pp.getOrder(); final int nKnots = pp.getNumberOfIntervals() + 1; final double[][] coefMatrix = pp.getCoefMatrix().getData(); double[][] res = new double[nKnots - 1][nCoefs + 1]; for (int i = 0; i < nKnots - 1; ++i) { Arrays.fill(res[i], 0.); } for (int i = 0; i < nKnots - 1; ++i) { for (int j = 0; j < nCoefs; ++j) { res[i][j] = coefMatrix[i][j] / (nCoefs - j); } } double[] constTerms = new double[nKnots - 1]; Arrays.fill(constTerms, 0.); int indicator = 0; if (initialKey <= knots[1]) { indicator = 0; } else { for (int i = 1; i < nKnots - 1; ++i) { if (knots[i] < initialKey) { indicator = i; } } } double sum = getValue(res[indicator], initialKey, knots[indicator]); for (int i = indicator; i < nKnots - 2; ++i) { constTerms[i + 1] = constTerms[i] + getValue(res[i], knots[i + 1], knots[i]) - sum; sum = 0.; } constTerms[indicator] = -getValue(res[indicator], initialKey, knots[indicator]); for (int i = indicator - 1; i > -1; --i) { constTerms[i] = constTerms[i + 1] - getValue(res[i], knots[i + 1], knots[i]); } for (int i = 0; i < nKnots - 1; ++i) { res[i][nCoefs] = constTerms[i]; } final PiecewisePolynomialResult ppInt = new PiecewisePolynomialResult( new DoubleMatrix1D(knots), DoubleMatrix2D.noCopy(res), nCoefs + 1, 1); return new DoubleMatrix1D(evaluate(ppInt, xKeys).getData()[0]); }
private void checkEquals(final DoubleMatrix2D x, final DoubleMatrix2D y) { final int n = x.getNumberOfRows(); final int m = x.getNumberOfColumns(); assertEquals(n, y.getNumberOfRows()); assertEquals(m, y.getNumberOfColumns()); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { assertEquals(x.getEntry(i, j), y.getEntry(i, j), EPS); } } }
/** * Construct from DoubleMatrix2D type * * @param aMatrix is a DoubleMatrix2D */ public SparseCoordinateFormatMatrix(final DoubleMatrix2D aMatrix) { Validate.notNull(aMatrix); // get number of elements _els = aMatrix.getNumberOfElements(); // tmp arrays, in case we get in a fully populated matrix, intelligent design upstream should // ensure that this is overkill! double[] valuesTmp = new double[_els]; int[] xTmp = new int[_els]; int[] yTmp = new int[_els]; // we need unwind the array aMatrix into coordinate form int localmaxEntriesInARow; _maxEntriesInARow = -1; // set max entries in a column negative, so that maximiser will work int ptr = 0; for (int i = 0; i < aMatrix.getNumberOfRows(); i++) { localmaxEntriesInARow = 0; for (int j = 0; j < aMatrix.getNumberOfColumns(); j++) { if (Double.doubleToLongBits(aMatrix.getEntry(i, j)) != 0L) { xTmp[ptr] = j; yTmp[ptr] = i; valuesTmp[ptr] = aMatrix.getEntry(i, j); ptr++; localmaxEntriesInARow++; } } if (localmaxEntriesInARow > _maxEntriesInARow) { _maxEntriesInARow = localmaxEntriesInARow; } } _values = Arrays.copyOfRange(valuesTmp, 0, ptr); _x = Arrays.copyOfRange(xTmp, 0, ptr); _y = Arrays.copyOfRange(yTmp, 0, ptr); _rows = aMatrix.getNumberOfRows(); _cols = aMatrix.getNumberOfColumns(); }
/** * The method calibrates a LMM on a set of vanilla swaption priced with SABR. The set of vanilla * swaptions is given by the CalibrationType. The curve and SABR sensitivities of the original * swaption are calculated with LMM re-calibration. Used mainly for performance test purposes as * the output is hybrid list. * * @param swaption The swaption. * @param curves The curves and SABR data. * @return The results (returned as a list of objects) [0] the present value, [1] the present * curve sensitivity, [2] the present value SABR sensitivity. */ public List<Object> presentValueCurveSABRSensitivity( final SwaptionPhysicalFixedIbor swaption, final SABRInterestRateDataBundle curves) { ArgumentChecker.notNull(swaption, "swaption"); ArgumentChecker.notNull(curves, "curves"); // TODO: Create a way to chose the LMM base parameters (displacement, mean reversion, // volatility). final LiborMarketModelDisplacedDiffusionParameters lmmParameters = LiborMarketModelDisplacedDiffusionParameters.from( swaption, DEFAULT_DISPLACEMENT, DEFAULT_MEAN_REVERSION, new VolatilityLMMAngle(DEFAULT_ANGLE, DEFAULT_DISPLACEMENT)); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective objective = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective(lmmParameters); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine calibrationEngine = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine(objective); final SwaptionPhysicalFixedIbor[] swaptionCalibration = METHOD_BASKET.calibrationBasketFixedLegPeriod(swaption); calibrationEngine.addInstrument(swaptionCalibration, METHOD_SWAPTION_SABR); calibrationEngine.calibrate(curves); final LiborMarketModelDisplacedDiffusionDataBundle lmmBundle = new LiborMarketModelDisplacedDiffusionDataBundle(lmmParameters, curves); // Risks final int nbCal = swaptionCalibration.length; final int nbFact = lmmParameters.getNbFactor(); final List<Integer> instrumentIndex = calibrationEngine.getInstrumentIndex(); final double[] dPvAmdLambda = new double[nbCal]; final double[][][] dPvCaldGamma = new double[nbCal][][]; final double[][] dPvCaldLambda = new double[nbCal][nbCal]; final PresentValueSABRSensitivityDataBundle[] dPvCaldSABR = new PresentValueSABRSensitivityDataBundle[nbCal]; InterestRateCurveSensitivity pvcsCal = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaption, lmmBundle); pvcsCal = pvcsCal.cleaned(); final double[][] dPvAmdGamma = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaption, lmmBundle); for (int loopcal = 0; loopcal < nbCal; loopcal++) { dPvCaldGamma[loopcal] = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaptionCalibration[loopcal], lmmBundle); } // Multiplicative-factor sensitivity for (int loopcal = 0; loopcal < nbCal; loopcal++) { for (int loopperiod = instrumentIndex.get(loopcal); loopperiod < instrumentIndex.get(loopcal + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvAmdLambda[loopcal] += dPvAmdGamma[loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { for (int loopcal2 = 0; loopcal2 < nbCal; loopcal2++) { for (int loopperiod = instrumentIndex.get(loopcal2); loopperiod < instrumentIndex.get(loopcal2 + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvCaldLambda[loopcal1][loopcal2] += dPvCaldGamma[loopcal1][loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } } final InterestRateCurveSensitivity[] pvcsCalBase = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalCal = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalDiff = new InterestRateCurveSensitivity[nbCal]; for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcsCalBase[loopcal] = METHOD_SWAPTION_SABR.presentValueCurveSensitivity(swaptionCalibration[loopcal], curves); pvcsCalBase[loopcal] = pvcsCalBase[loopcal].cleaned(); pvcsCalCal[loopcal] = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaptionCalibration[loopcal], lmmBundle); pvcsCalCal[loopcal] = pvcsCalCal[loopcal].cleaned(); pvcsCalDiff[loopcal] = pvcsCalBase[loopcal].plus(pvcsCalCal[loopcal].multipliedBy(-1)); pvcsCalDiff[loopcal] = pvcsCalDiff[loopcal].cleaned(); } final CommonsMatrixAlgebra matrix = new CommonsMatrixAlgebra(); final DoubleMatrix2D dPvCaldLambdaMatrix = new DoubleMatrix2D(dPvCaldLambda); final DoubleMatrix2D dPvCaldLambdaMatrixInverse = matrix.getInverse(dPvCaldLambdaMatrix); // SABR sensitivity final double[][] dPvCaldAlpha = new double[nbCal][nbCal]; final double[][] dPvCaldRho = new double[nbCal][nbCal]; final double[][] dPvCaldNu = new double[nbCal][nbCal]; for (int loopcal = 0; loopcal < nbCal; loopcal++) { dPvCaldSABR[loopcal] = METHOD_SWAPTION_SABR.presentValueSABRSensitivity(swaptionCalibration[loopcal], curves); final Set<DoublesPair> keySet = dPvCaldSABR[loopcal].getAlpha().getMap().keySet(); final DoublesPair[] keys = keySet.toArray(new DoublesPair[keySet.size()]); dPvCaldAlpha[loopcal][loopcal] = dPvCaldSABR[loopcal].getAlpha().getMap().get(keys[0]); dPvCaldRho[loopcal][loopcal] = dPvCaldSABR[loopcal].getRho().getMap().get(keys[0]); dPvCaldNu[loopcal][loopcal] = dPvCaldSABR[loopcal].getNu().getMap().get(keys[0]); } final DoubleMatrix1D dPvAmdLambdaMatrix = new DoubleMatrix1D(dPvAmdLambda); final DoubleMatrix2D dPvCaldAlphaMatrix = new DoubleMatrix2D(dPvCaldAlpha); final DoubleMatrix2D dLambdadAlphaMatrix = (DoubleMatrix2D) matrix.multiply(dPvCaldLambdaMatrixInverse, dPvCaldAlphaMatrix); final DoubleMatrix2D dPvAmdAlphaMatrix = (DoubleMatrix2D) matrix.multiply(matrix.getTranspose(dLambdadAlphaMatrix), dPvAmdLambdaMatrix); final DoubleMatrix2D dPvCaldRhoMatrix = new DoubleMatrix2D(dPvCaldRho); final DoubleMatrix2D dLambdadRhoMatrix = (DoubleMatrix2D) matrix.multiply(dPvCaldLambdaMatrixInverse, dPvCaldRhoMatrix); final DoubleMatrix2D dPvAmdRhoMatrix = (DoubleMatrix2D) matrix.multiply(matrix.getTranspose(dLambdadRhoMatrix), dPvAmdLambdaMatrix); final DoubleMatrix2D dPvCaldNuMatrix = new DoubleMatrix2D(dPvCaldNu); final DoubleMatrix2D dLambdadNuMatrix = (DoubleMatrix2D) matrix.multiply(dPvCaldLambdaMatrixInverse, dPvCaldNuMatrix); final DoubleMatrix2D dPvAmdNuMatrix = (DoubleMatrix2D) matrix.multiply(matrix.getTranspose(dLambdadNuMatrix), dPvAmdLambdaMatrix); final double[] dPvAmdAlpha = matrix.getTranspose(dPvAmdAlphaMatrix).getData()[0]; final double[] dPvAmdRho = matrix.getTranspose(dPvAmdRhoMatrix).getData()[0]; final double[] dPvAmdNu = matrix.getTranspose(dPvAmdNuMatrix).getData()[0]; // Storage in PresentValueSABRSensitivityDataBundle final PresentValueSABRSensitivityDataBundle pvss = new PresentValueSABRSensitivityDataBundle(); for (int loopcal = 0; loopcal < nbCal; loopcal++) { final DoublesPair expiryMaturity = DoublesPair.of( swaptionCalibration[loopcal].getTimeToExpiry(), swaptionCalibration[loopcal].getMaturityTime()); pvss.addAlpha(expiryMaturity, dPvAmdAlpha[loopcal]); pvss.addRho(expiryMaturity, dPvAmdRho[loopcal]); pvss.addNu(expiryMaturity, dPvAmdNu[loopcal]); } // Curve sensitivity final InterestRateCurveSensitivity[] dLambdadC = new InterestRateCurveSensitivity[nbCal]; for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { dLambdadC[loopcal1] = new InterestRateCurveSensitivity(); for (int loopcal2 = 0; loopcal2 <= loopcal1; loopcal2++) { dLambdadC[loopcal1] = dLambdadC[loopcal1].plus( pvcsCalDiff[loopcal2].multipliedBy( dPvCaldLambdaMatrixInverse.getEntry(loopcal1, loopcal2))); } } InterestRateCurveSensitivity pvcs = new InterestRateCurveSensitivity(); for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcs = pvcs.plus(dLambdadC[loopcal].multipliedBy(dPvAmdLambda[loopcal])); } pvcs = pvcs.plus(pvcsCal); pvcs = pvcs.cleaned(); final List<Object> results = new ArrayList<>(); results.add( CurrencyAmount.of( swaption.getCurrency(), METHOD_SWAPTION_LMM.presentValue(swaption, lmmBundle).getAmount())); results.add(pvcs); results.add(pvss); return results; }
/** * The method calibrates a LMM on a set of vanilla swaption priced with SABR. The set of vanilla * swaptions is given by the CalibrationType. The curve sensitivities of the original swaption are * calculated with LMM re-calibration. * * @param swaption The swaption. * @param curves The curves and SABR data. * @return The present value curve sensitivities. */ public InterestRateCurveSensitivity presentValueCurveSensitivity( final SwaptionPhysicalFixedIbor swaption, final SABRInterestRateDataBundle curves) { ArgumentChecker.notNull(swaption, "swaption"); ArgumentChecker.notNull(curves, "curves"); // TODO: Create a way to chose the LMM base parameters (displacement, mean reversion, // volatility). final LiborMarketModelDisplacedDiffusionParameters lmmParameters = LiborMarketModelDisplacedDiffusionParameters.from( swaption, DEFAULT_DISPLACEMENT, DEFAULT_MEAN_REVERSION, new VolatilityLMMAngle(DEFAULT_ANGLE, DEFAULT_DISPLACEMENT)); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective objective = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective(lmmParameters); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine calibrationEngine = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine(objective); final SwaptionPhysicalFixedIbor[] swaptionCalibration = METHOD_BASKET.calibrationBasketFixedLegPeriod(swaption); calibrationEngine.addInstrument(swaptionCalibration, METHOD_SWAPTION_SABR); calibrationEngine.calibrate(curves); final LiborMarketModelDisplacedDiffusionDataBundle lmmBundle = new LiborMarketModelDisplacedDiffusionDataBundle(lmmParameters, curves); // Risks final int nbCal = swaptionCalibration.length; final int nbFact = lmmParameters.getNbFactor(); final List<Integer> instrumentIndex = calibrationEngine.getInstrumentIndex(); final double[] dPvAmdLambda = new double[nbCal]; final double[][][] dPvCaldGamma = new double[nbCal][][]; final double[][] dPvCaldLambda = new double[nbCal][nbCal]; InterestRateCurveSensitivity pvcsCal = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaption, lmmBundle); pvcsCal = pvcsCal.cleaned(); final double[][] dPvAmdGamma = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaption, lmmBundle); for (int loopcal = 0; loopcal < nbCal; loopcal++) { dPvCaldGamma[loopcal] = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaptionCalibration[loopcal], lmmBundle); } // Multiplicative-factor sensitivity for (int loopcal = 0; loopcal < nbCal; loopcal++) { for (int loopperiod = instrumentIndex.get(loopcal); loopperiod < instrumentIndex.get(loopcal + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvAmdLambda[loopcal] += dPvAmdGamma[loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { for (int loopcal2 = 0; loopcal2 < nbCal; loopcal2++) { for (int loopperiod = instrumentIndex.get(loopcal2); loopperiod < instrumentIndex.get(loopcal2 + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvCaldLambda[loopcal1][loopcal2] += dPvCaldGamma[loopcal1][loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } } final InterestRateCurveSensitivity[] pvcsCalBase = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalCal = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalDiff = new InterestRateCurveSensitivity[nbCal]; for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcsCalBase[loopcal] = METHOD_SWAPTION_SABR.presentValueCurveSensitivity(swaptionCalibration[loopcal], curves); pvcsCalBase[loopcal] = pvcsCalBase[loopcal].cleaned(); pvcsCalCal[loopcal] = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaptionCalibration[loopcal], lmmBundle); pvcsCalCal[loopcal] = pvcsCalCal[loopcal].cleaned(); pvcsCalDiff[loopcal] = pvcsCalBase[loopcal].plus(pvcsCalCal[loopcal].multipliedBy(-1)); pvcsCalDiff[loopcal] = pvcsCalDiff[loopcal].cleaned(); } final CommonsMatrixAlgebra matrix = new CommonsMatrixAlgebra(); final DoubleMatrix2D dPvCaldLambdaMatrix = new DoubleMatrix2D(dPvCaldLambda); final DoubleMatrix2D dPvCaldLambdaMatrixInverse = matrix.getInverse(dPvCaldLambdaMatrix); // Curve sensitivity final InterestRateCurveSensitivity[] dLambdadC = new InterestRateCurveSensitivity[nbCal]; for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { dLambdadC[loopcal1] = new InterestRateCurveSensitivity(); for (int loopcal2 = 0; loopcal2 <= loopcal1; loopcal2++) { dLambdadC[loopcal1] = dLambdadC[loopcal1].plus( pvcsCalDiff[loopcal2].multipliedBy( dPvCaldLambdaMatrixInverse.getEntry(loopcal1, loopcal2))); } } InterestRateCurveSensitivity pvcsAdjust = new InterestRateCurveSensitivity(); for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcsAdjust = pvcsAdjust.plus(dLambdadC[loopcal].multipliedBy(dPvAmdLambda[loopcal])); } pvcsAdjust = pvcsAdjust.cleaned(); InterestRateCurveSensitivity pvcsTot = pvcsCal.plus(pvcsAdjust); pvcsTot = pvcsTot.cleaned(); return pvcsTot; }
/** * @param x An OG 2D matrix of doubles, not null * @return A Colt 2D matrix */ public static cern.colt.matrix.DoubleMatrix2D wrap(final DoubleMatrix2D x) { Validate.notNull(x, "x"); return cern.colt.matrix.DoubleFactory2D.dense.make(x.getData()); }
@Override public PiecewisePolynomialResultsWithSensitivity interpolateWithSensitivity( final double[] xValues, final double[] yValues) { ArgumentChecker.notNull(xValues, "xValues"); ArgumentChecker.notNull(yValues, "yValues"); ArgumentChecker.isTrue( xValues.length == yValues.length | xValues.length + 2 == yValues.length, "(xValues length = yValues length) or (xValues length + 2 = yValues length)"); ArgumentChecker.isTrue(xValues.length > 2, "Data points should be more than 2"); final int nDataPts = xValues.length; final int yValuesLen = yValues.length; for (int i = 0; i < nDataPts; ++i) { ArgumentChecker.isFalse(Double.isNaN(xValues[i]), "xValues containing NaN"); ArgumentChecker.isFalse(Double.isInfinite(xValues[i]), "xValues containing Infinity"); } for (int i = 0; i < yValuesLen; ++i) { ArgumentChecker.isFalse(Double.isNaN(yValues[i]), "yValues containing NaN"); ArgumentChecker.isFalse(Double.isInfinite(yValues[i]), "yValues containing Infinity"); } for (int i = 0; i < nDataPts - 1; ++i) { for (int j = i + 1; j < nDataPts; ++j) { ArgumentChecker.isFalse(xValues[i] == xValues[j], "xValues should be distinct"); } } double[] yValuesSrt = new double[nDataPts]; if (nDataPts == yValuesLen) { yValuesSrt = Arrays.copyOf(yValues, nDataPts); } else { yValuesSrt = Arrays.copyOfRange(yValues, 1, nDataPts + 1); } final double[] intervals = _solver.intervalsCalculator(xValues); final double[] slopes = _solver.slopesCalculator(yValuesSrt, intervals); double[][] slopesSensitivity = _solver.slopeSensitivityCalculator(intervals); final DoubleMatrix1D[] firstWithSensitivity = new DoubleMatrix1D[nDataPts + 1]; final DoubleMatrix1D[] secondWithSensitivity = new DoubleMatrix1D[nDataPts + 1]; final PiecewisePolynomialResult result = _method.interpolate(xValues, yValues); ArgumentChecker.isTrue(result.getOrder() >= 3, "Primary interpolant should be degree >= 2"); final double[] initialFirst = _function.differentiate(result, xValues).getData()[0]; final double[] initialSecond = _function.differentiateTwice(result, xValues).getData()[0]; double[] first = firstDerivativeCalculator(yValuesSrt, intervals, slopes, initialFirst); boolean modFirst = false; int k; double[] aValues = aValuesCalculator(slopes, first); double[] bValues = bValuesCalculator(slopes, first); double[][] intervalsA = getIntervalsA(intervals, slopes, first, bValues); double[][] intervalsB = getIntervalsB(intervals, slopes, first, aValues); while (modFirst == false) { k = 0; for (int i = 0; i < nDataPts - 2; ++i) { if (first[i + 1] > 0.) { if (intervalsA[i + 1][1] + Math.abs(intervalsA[i + 1][1]) * ERROR < intervalsB[i][0] - Math.abs(intervalsB[i][0]) * ERROR | intervalsA[i + 1][0] - Math.abs(intervalsA[i + 1][0]) * ERROR > intervalsB[i][1] + Math.abs(intervalsB[i][1]) * ERROR) { ++k; first[i + 1] = firstDerivativesRecalculator(intervals, slopes, aValues, bValues, i + 1); } } } if (k == 0) { modFirst = true; } aValues = aValuesCalculator(slopes, first); bValues = bValuesCalculator(slopes, first); intervalsA = getIntervalsA(intervals, slopes, first, bValues); intervalsB = getIntervalsB(intervals, slopes, first, aValues); } final double[] second = secondDerivativeCalculator(initialSecond, intervalsA, intervalsB); firstWithSensitivity[0] = new DoubleMatrix1D(first); secondWithSensitivity[0] = new DoubleMatrix1D(second); /* * Centered finite difference method is used for computing node sensitivity */ int nExtra = (nDataPts == yValuesLen) ? 0 : 1; final double[] yValuesUp = Arrays.copyOf(yValues, nDataPts + 2 * nExtra); final double[] yValuesDw = Arrays.copyOf(yValues, nDataPts + 2 * nExtra); final double[][] tmpFirst = new double[nDataPts][nDataPts]; final double[][] tmpSecond = new double[nDataPts][nDataPts]; for (int l = nExtra; l < nDataPts + nExtra; ++l) { final double den = Math.abs(yValues[l]) < SMALL ? EPS : yValues[l] * EPS; yValuesUp[l] = Math.abs(yValues[l]) < SMALL ? EPS : yValues[l] * (1. + EPS); yValuesDw[l] = Math.abs(yValues[l]) < SMALL ? -EPS : yValues[l] * (1. - EPS); final double[] yValuesSrtUp = Arrays.copyOfRange(yValuesUp, nExtra, nDataPts + nExtra); final double[] yValuesSrtDw = Arrays.copyOfRange(yValuesDw, nExtra, nDataPts + nExtra); final DoubleMatrix1D[] yValuesUpDw = new DoubleMatrix1D[] {new DoubleMatrix1D(yValuesUp), new DoubleMatrix1D(yValuesDw)}; final DoubleMatrix1D[] yValuesSrtUpDw = new DoubleMatrix1D[] {new DoubleMatrix1D(yValuesSrtUp), new DoubleMatrix1D(yValuesSrtDw)}; final DoubleMatrix1D[] firstSecondUpDw = new DoubleMatrix1D[4]; for (int ii = 0; ii < 2; ++ii) { final double[] slopesUpDw = _solver.slopesCalculator(yValuesSrtUpDw[ii].getData(), intervals); final PiecewisePolynomialResult resultUpDw = _method.interpolate(xValues, yValuesUpDw[ii].getData()); final double[] initialFirstUpDw = _function.differentiate(resultUpDw, xValues).getData()[0]; final double[] initialSecondUpDw = _function.differentiateTwice(resultUpDw, xValues).getData()[0]; double[] firstUpDw = firstDerivativeCalculator( yValuesSrtUpDw[ii].getData(), intervals, slopesUpDw, initialFirstUpDw); boolean modFirstUpDw = false; double[] aValuesUpDw = aValuesCalculator(slopesUpDw, firstUpDw); double[] bValuesUpDw = bValuesCalculator(slopesUpDw, firstUpDw); double[][] intervalsAUpDw = getIntervalsA(intervals, slopesUpDw, firstUpDw, bValuesUpDw); double[][] intervalsBUpDw = getIntervalsB(intervals, slopesUpDw, firstUpDw, aValuesUpDw); while (modFirstUpDw == false) { k = 0; for (int i = 0; i < nDataPts - 2; ++i) { if (firstUpDw[i + 1] > 0.) { if (intervalsAUpDw[i + 1][1] + Math.abs(intervalsAUpDw[i + 1][1]) * ERROR < intervalsBUpDw[i][0] - Math.abs(intervalsBUpDw[i][0]) * ERROR | intervalsAUpDw[i + 1][0] - Math.abs(intervalsAUpDw[i + 1][0]) * ERROR > intervalsBUpDw[i][1] + Math.abs(intervalsBUpDw[i][1]) * ERROR) { ++k; firstUpDw[i + 1] = firstDerivativesRecalculator( intervals, slopesUpDw, aValuesUpDw, bValuesUpDw, i + 1); } } } if (k == 0) { modFirstUpDw = true; } aValuesUpDw = aValuesCalculator(slopesUpDw, firstUpDw); bValuesUpDw = bValuesCalculator(slopesUpDw, firstUpDw); intervalsAUpDw = getIntervalsA(intervals, slopesUpDw, firstUpDw, bValuesUpDw); intervalsBUpDw = getIntervalsB(intervals, slopesUpDw, firstUpDw, aValuesUpDw); } final double[] secondUpDw = secondDerivativeCalculator(initialSecondUpDw, intervalsAUpDw, intervalsBUpDw); firstSecondUpDw[ii] = new DoubleMatrix1D(firstUpDw); firstSecondUpDw[2 + ii] = new DoubleMatrix1D(secondUpDw); } for (int j = 0; j < nDataPts; ++j) { tmpFirst[j][l - nExtra] = 0.5 * (firstSecondUpDw[0].getData()[j] - firstSecondUpDw[1].getData()[j]) / den; tmpSecond[j][l - nExtra] = 0.5 * (firstSecondUpDw[2].getData()[j] - firstSecondUpDw[3].getData()[j]) / den; } yValuesUp[l] = yValues[l]; yValuesDw[l] = yValues[l]; } for (int i = 0; i < nDataPts; ++i) { firstWithSensitivity[i + 1] = new DoubleMatrix1D(tmpFirst[i]); secondWithSensitivity[i + 1] = new DoubleMatrix1D(tmpSecond[i]); } final DoubleMatrix2D[] resMatrix = _solver.solveWithSensitivity( yValuesSrt, intervals, slopes, slopesSensitivity, firstWithSensitivity, secondWithSensitivity); for (int l = 0; l < nDataPts; ++l) { DoubleMatrix2D m = resMatrix[l]; final int rows = m.getNumberOfRows(); final int cols = m.getNumberOfColumns(); for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { ArgumentChecker.isTrue( Doubles.isFinite(m.getEntry(i, j)), "Matrix contains a NaN or infinite"); } } } final DoubleMatrix2D coefMatrix = resMatrix[0]; final DoubleMatrix2D[] coefSenseMatrix = new DoubleMatrix2D[nDataPts - 1]; System.arraycopy(resMatrix, 1, coefSenseMatrix, 0, nDataPts - 1); final int nCoefs = coefMatrix.getNumberOfColumns(); return new PiecewisePolynomialResultsWithSensitivity( new DoubleMatrix1D(xValues), coefMatrix, nCoefs, 1, coefSenseMatrix); }
@Override public PiecewisePolynomialResult interpolate( final double[] xValues, final double[][] yValuesMatrix) { ArgumentChecker.notNull(xValues, "xValues"); ArgumentChecker.notNull(yValuesMatrix, "yValuesMatrix"); ArgumentChecker.isTrue( xValues.length == yValuesMatrix[0].length | xValues.length + 2 == yValuesMatrix[0].length, "(xValues length = yValuesMatrix's row vector length) or (xValues length + 2 = yValuesMatrix's row vector length)"); ArgumentChecker.isTrue(xValues.length > 2, "Data points should be more than 2"); final int nDataPts = xValues.length; final int yValuesLen = yValuesMatrix[0].length; final int dim = yValuesMatrix.length; for (int i = 0; i < nDataPts; ++i) { ArgumentChecker.isFalse(Double.isNaN(xValues[i]), "xValues containing NaN"); ArgumentChecker.isFalse(Double.isInfinite(xValues[i]), "xValues containing Infinity"); } for (int i = 0; i < yValuesLen; ++i) { for (int j = 0; j < dim; ++j) { ArgumentChecker.isFalse(Double.isNaN(yValuesMatrix[j][i]), "yValuesMatrix containing NaN"); ArgumentChecker.isFalse( Double.isInfinite(yValuesMatrix[j][i]), "yValuesMatrix containing Infinity"); } } for (int i = 0; i < nDataPts; ++i) { for (int j = i + 1; j < nDataPts; ++j) { ArgumentChecker.isFalse(xValues[i] == xValues[j], "xValues should be distinct"); } } double[] xValuesSrt = new double[nDataPts]; DoubleMatrix2D[] coefMatrix = new DoubleMatrix2D[dim]; for (int i = 0; i < dim; ++i) { xValuesSrt = Arrays.copyOf(xValues, nDataPts); double[] yValuesSrt = new double[nDataPts]; if (nDataPts == yValuesLen) { yValuesSrt = Arrays.copyOf(yValuesMatrix[i], nDataPts); } else { yValuesSrt = Arrays.copyOfRange(yValuesMatrix[i], 1, nDataPts + 1); } ParallelArrayBinarySort.parallelBinarySort(xValuesSrt, yValuesSrt); final double[] intervals = _solver.intervalsCalculator(xValuesSrt); final double[] slopes = _solver.slopesCalculator(yValuesSrt, intervals); final PiecewisePolynomialResult result = _method.interpolate(xValues, yValuesMatrix[i]); ArgumentChecker.isTrue(result.getOrder() >= 3, "Primary interpolant should be degree >= 2"); final double[] initialFirst = _function.differentiate(result, xValuesSrt).getData()[0]; final double[] initialSecond = _function.differentiateTwice(result, xValuesSrt).getData()[0]; final double[] first = firstDerivativeCalculator(yValuesSrt, intervals, slopes, initialFirst); boolean modFirst = false; int k; double[] aValues = aValuesCalculator(slopes, first); double[] bValues = bValuesCalculator(slopes, first); double[][] intervalsA = getIntervalsA(intervals, slopes, first, bValues); double[][] intervalsB = getIntervalsB(intervals, slopes, first, aValues); while (modFirst == false) { k = 0; for (int j = 0; j < nDataPts - 2; ++j) { if (first[j + 1] > 0.) { if (intervalsA[j + 1][1] + Math.abs(intervalsA[j + 1][1]) * ERROR < intervalsB[j][0] - Math.abs(intervalsB[j][0]) * ERROR | intervalsA[j + 1][0] - Math.abs(intervalsA[j + 1][0]) * ERROR > intervalsB[j][1] + Math.abs(intervalsB[j][1]) * ERROR) { ++k; first[j + 1] = firstDerivativesRecalculator(intervals, slopes, aValues, bValues, j + 1); } } } if (k == 0) { modFirst = true; } aValues = aValuesCalculator(slopes, first); bValues = bValuesCalculator(slopes, first); intervalsA = getIntervalsA(intervals, slopes, first, bValues); intervalsB = getIntervalsB(intervals, slopes, first, aValues); } final double[] second = secondDerivativeCalculator(initialSecond, intervalsA, intervalsB); coefMatrix[i] = new DoubleMatrix2D(_solver.solve(yValuesSrt, intervals, slopes, first, second)); } final int nIntervals = coefMatrix[0].getNumberOfRows(); final int nCoefs = coefMatrix[0].getNumberOfColumns(); double[][] resMatrix = new double[dim * nIntervals][nCoefs]; for (int i = 0; i < nIntervals; ++i) { for (int j = 0; j < dim; ++j) { resMatrix[dim * i + j] = coefMatrix[j].getRowVector(i, false).getData(); } } for (int i = 0; i < (nIntervals * dim); ++i) { for (int j = 0; j < nCoefs; ++j) { ArgumentChecker.isFalse(Double.isNaN(resMatrix[i][j]), "Too large input"); ArgumentChecker.isFalse(Double.isInfinite(resMatrix[i][j]), "Too large input"); } } return new PiecewisePolynomialResult( new DoubleMatrix1D(xValuesSrt, false), DoubleMatrix2D.noCopy(resMatrix), nCoefs, dim); }