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];
MultipleCurrencyMulticurveSensitivity pvcsCal = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaption, lmm);
pvcsCal = pvcsCal.cleaned();
final double[][] dPvAmdGamma = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaption, lmm);
for (int loopcal = 0; loopcal < nbCal; loopcal++) {
dPvCaldGamma[loopcal] = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaptionCalibration[loopcal], lmm);
}
// 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 MultipleCurrencyMulticurveSensitivity[] pvcsCalBase = new MultipleCurrencyMulticurveSensitivity[nbCal];
final MultipleCurrencyMulticurveSensitivity[] pvcsCalCal = new MultipleCurrencyMulticurveSensitivity[nbCal];
final MultipleCurrencyMulticurveSensitivity[] pvcsCalDiff = new MultipleCurrencyMulticurveSensitivity[nbCal];
for (int loopcal = 0; loopcal < nbCal; loopcal++) {
pvcsCalBase[loopcal] = swaptionCalibration[loopcal].accept(PVCSSSC, sabrData);
pvcsCalBase[loopcal] = pvcsCalBase[loopcal].cleaned();
pvcsCalCal[loopcal] = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaptionCalibration[loopcal], lmm);
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] = swaptionCalibration[loopcal].accept(PVSSSSC, sabrData);
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 = new DoublesPair(swaptionCalibration[loopcal].getTimeToExpiry(), swaptionCalibration[loopcal].getMaturityTime());
pvss.addAlpha(expiryMaturity, dPvAmdAlpha[loopcal]);
pvss.addRho(expiryMaturity, dPvAmdRho[loopcal]);
pvss.addNu(expiryMaturity, dPvAmdNu[loopcal]);
}
// Curve sensitivity
final MultipleCurrencyMulticurveSensitivity[] dLambdadC = new MultipleCurrencyMulticurveSensitivity[nbCal];
for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) {
dLambdadC[loopcal1] = new MultipleCurrencyMulticurveSensitivity();
for (int loopcal2 = 0; loopcal2 <= loopcal1; loopcal2++) {
dLambdadC[loopcal1] = dLambdadC[loopcal1].plus(pvcsCalDiff[loopcal2].multipliedBy(dPvCaldLambdaMatrixInverse.getEntry(loopcal1, loopcal2)));
}
}
MultipleCurrencyMulticurveSensitivity pvcs = new MultipleCurrencyMulticurveSensitivity();
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, lmm).getAmount(ccy)));
results.add(pvcs);
results.add(pvss);
return results;