final double[][] dPvCaldTheta = new double[nbCalibrations][3 * nbPeriods];
// Implementation note: Derivative of the calibration swaptions wrt the SABR parameters as a unique array.
for (int loopperiod = 0; loopperiod < nbPeriods; loopperiod++) {
for (int loopstrike = 0; loopstrike < nbStrikes; loopstrike++) {
final PresentValueSABRSensitivityDataBundle dPvCaldSABR = METHOD_SWAPTION_SABR.presentValueSABRSensitivity(swaptionCalibration[loopperiod * nbStrikes + loopstrike], curves);
final Set<DoublesPair> keySet = dPvCaldSABR.getAlpha().getMap().keySet();
final DoublesPair[] keys = keySet.toArray(new DoublesPair[keySet.size()]);
dPvCaldTheta[loopperiod * nbStrikes + loopstrike][loopperiod] += dPvCaldSABR.getAlpha().getMap().get(keys[0]);
dPvCaldTheta[loopperiod * nbStrikes + loopstrike][nbPeriods + loopperiod] = dPvCaldSABR.getRho().getMap().get(keys[0]);
dPvCaldTheta[loopperiod * nbStrikes + loopstrike][2 * nbPeriods + loopperiod] = dPvCaldSABR.getNu().getMap().get(keys[0]);
}
}
final double[][] dfdTheta = new double[2 * nbPeriods][3 * nbPeriods];
// Implementation note: Derivative of f wrt the SABR parameters.
for (int loopp = 0; loopp < 2 * nbPeriods; loopp++) {
for (int loops = 0; loops < 3 * nbPeriods; loops++) {
for (int loopcal = 0; loopcal < nbCalibrations; loopcal++) {
dfdTheta[loopp][loops] += -2 * dPvCaldPhi[loopcal][loopp] * dPvCaldTheta[loopcal][loops];
}
}
}
final double[][] dfdPhi = new double[2 * nbPeriods][2 * nbPeriods];
// Implementation note: Derivative of f wrt the calibration parameters. This is an approximation: the second order derivative part are ignored.
for (int loopp1 = 0; loopp1 < 2 * nbPeriods; loopp1++) {
for (int loopp2 = 0; loopp2 < 2 * nbPeriods; loopp2++) {
for (int loopcal = 0; loopcal < nbCalibrations; loopcal++) {
dfdPhi[loopp1][loopp2] += 2 * dPvCaldPhi[loopcal][loopp1] * dPvCaldPhi[loopcal][loopp2];
}
}
}
final DoubleMatrix2D dfdThetaMat = new DoubleMatrix2D(dfdTheta);
final DoubleMatrix2D dfdPhiMat = new DoubleMatrix2D(dfdPhi);
final DoubleMatrix2D dfdPhiInvMat = ALGEBRA.getInverse(dfdPhiMat);
final DoubleMatrix2D dPhidThetaMat = (DoubleMatrix2D) ALGEBRA.scale(ALGEBRA.multiply(dfdPhiInvMat, dfdThetaMat), -1.0);
final DoubleMatrix1D dPvdPhiMat = new DoubleMatrix1D(dPvdPhi);
final DoubleMatrix2D dPvdThetaMat = ALGEBRA.getTranspose(ALGEBRA.multiply(ALGEBRA.getTranspose(dPhidThetaMat), dPvdPhiMat));
final double[] dPvdTheta = dPvdThetaMat.getData()[0];
// Storage in PresentValueSABRSensitivityDataBundle
final PresentValueSABRSensitivityDataBundle sensiSABR = new PresentValueSABRSensitivityDataBundle();
for (int loopp = 0; loopp < nbPeriods; loopp++) {
final DoublesPair expiryMaturity = new DoublesPair(swaptionCalibration[loopp * nbStrikes].getTimeToExpiry(), swaptionCalibration[loopp * nbStrikes].getMaturityTime());
sensiSABR.addAlpha(expiryMaturity, dPvdTheta[loopp]);
sensiSABR.addRho(expiryMaturity, dPvdTheta[nbPeriods + loopp]);
sensiSABR.addNu(expiryMaturity, dPvdTheta[2 * nbPeriods + loopp]);
}
// 3. Curve sensitivities
final InterestRateCurveSensitivity[] dPvCalBasedC = new InterestRateCurveSensitivity[nbCalibrations];