docs: public Map<String, double[]> getLogSwapRateDerivative(TimeDiscretization liborPeriodDiscretization, DiscountCurve discountCurve, ForwardCurve forwardCurve) {
  
  		/*
  		 * We cache the calculation of the log swaprate derivative. In a calibration this method might be called quite often with the same arguments.
  		 */
  		synchronized (cachedLogSwaprateDerivativeLock) {
  
  			if(
  					cachedLogSwaprateDerivative != null
  					&& liborPeriodDiscretization == cachedLogSwaprateDerivativeTimeDiscretization.get()
  					&& discountCurve == cachedLogSwaprateDerivativeDiscountCurve.get()
  					&& forwardCurve == cachedLogSwaprateDerivativeForwardCurve.get()
  					) {
  				return cachedLogSwaprateDerivative;
  			}
  			cachedLogSwaprateDerivativeTimeDiscretization = new WeakReference<>(liborPeriodDiscretization);
  			cachedLogSwaprateDerivativeDiscountCurve = new WeakReference<>(discountCurve);
  			cachedLogSwaprateDerivativeForwardCurve = new WeakReference<>(forwardCurve);
  
  			/*
  			 * Small workaround for the case that the discount curve is not set. This part will be removed later.
  			 */
  			AnalyticModelFromCurvesAndVols model = null;
  			if(discountCurve == null) {
  				discountCurve	= new DiscountCurveFromForwardCurve(forwardCurve.getName());
  				model					= new AnalyticModelFromCurvesAndVols(new Curve[] { forwardCurve, discountCurve });
  			}
  
  			double swapStart    = swapTenor[0];
  			double swapEnd      = swapTenor[swapTenor.length-1];
  
  			// Get the indices of the swap start and end on the forward rate tenor
  			int swapStartIndex  = liborPeriodDiscretization.getTimeIndex(swapStart);
  			int swapEndIndex    = liborPeriodDiscretization.getTimeIndex(swapEnd);
  
  			// Precalculate forward rates and discount factors. Note: the swap contains swapEndIndex-swapStartIndex forward rates
  			double[] forwardRates       = new double[swapEndIndex-swapStartIndex+1];
  			double[] discountFactors    = new double[swapEndIndex-swapStartIndex+1];
  
  			// Calculate discount factor at swap start
  			discountFactors[0] = discountCurve.getDiscountFactor(model, swapStart);
  
  			// Calculate discount factors for swap period ends (used for swap annuity)
  			for(int liborPeriodIndex = swapStartIndex; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) {
  				double libor = forwardCurve.getForward(null, liborPeriodDiscretization.getTime(liborPeriodIndex));
  
  				forwardRates[liborPeriodIndex-swapStartIndex]       = libor;
  				discountFactors[liborPeriodIndex-swapStartIndex+1]  = discountCurve.getDiscountFactor(model, liborPeriodDiscretization.getTime(liborPeriodIndex+1));
  			}
  
  			// Precalculate swap annuities
  			double[]    swapAnnuities   = new double[swapTenor.length-1]; //calculates the annuities value for EVERY libor period (must be used later in the weight calulation)
  			double      swapAnnuity     = 0.0;
  			//note: the final element of the loop, i.e. the final value of the local variable swap annuity is the total annuity discount factor of the swap
  			for(int swapPeriodIndex = swapTenor.length-2; swapPeriodIndex >= 0; swapPeriodIndex--) {
  				int periodEndIndex = liborPeriodDiscretization.getTimeIndex(swapTenor[swapPeriodIndex+1]);
  				swapAnnuity += discountFactors[periodEndIndex-swapStartIndex] * (swapTenor[swapPeriodIndex+1]-swapTenor[swapPeriodIndex]);
  				swapAnnuities[swapPeriodIndex] = swapAnnuity;
  			}
  
  			// Precalculate weights: The formula is take from ISBN 0470047224.
  			double[] swapCovarianceWeights = new double[swapEndIndex-swapStartIndex];
  
  			double valueFloatLeg = 0.0;
  			for(int liborPeriodIndex = swapStartIndex; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) { //loop on the Libor index
  				double liborPeriodLength = liborPeriodDiscretization.getTimeStep(liborPeriodIndex);
  				valueFloatLeg += forwardRates[liborPeriodIndex-swapStartIndex] * discountFactors[liborPeriodIndex-swapStartIndex+1] * liborPeriodLength; //P(0, T_a)-P(0,T_b)
  			}
  
  			int swapPeriodIndex = 0;   //loop on the swap index
  			double valueFloatLegUpToSwapStart = 0.0;
  			for(int liborPeriodIndex = swapStartIndex; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) {
  				if(liborPeriodDiscretization.getTime(liborPeriodIndex) >= swapTenor[swapPeriodIndex+1]) {
  					swapPeriodIndex++;
  				}
  
  				double libor				= forwardRates[liborPeriodIndex-swapStartIndex];  //libor=L_k
  				double liborPeriodLength	= liborPeriodDiscretization.getTimeStep(liborPeriodIndex);
  
  				valueFloatLegUpToSwapStart += forwardRates[liborPeriodIndex-swapStartIndex] * discountFactors[liborPeriodIndex-swapStartIndex+1] * liborPeriodLength;   //P(0, T_a)-P(0,T_k+1)
  
  				double discountFactorAtPeriodEnd = discountCurve.getDiscountFactor(model, liborPeriodDiscretization.getTime(liborPeriodIndex+1)); // P(0,T_k+1)
  				double derivativeFloatLeg	= (discountFactorAtPeriodEnd + valueFloatLegUpToSwapStart - valueFloatLeg) * liborPeriodLength / (1.0 + libor * liborPeriodLength) / valueFloatLeg; //=P(0,T_b)/P(0,T_a)-P(0,T_b)
  				double derivativeFixLeg		= - swapAnnuities[swapPeriodIndex] / swapAnnuity * liborPeriodLength / (1.0 + libor * liborPeriodLength); //second summand pag 379, with a minus
  
  				swapCovarianceWeights[liborPeriodIndex-swapStartIndex] = (derivativeFloatLeg - derivativeFixLeg) * libor;    // and a minus here mathc the formula
  
  			}
  
  			// Return results
  			Map<String, double[]> results = new HashMap<>();
  			results.put("values",			swapCovarianceWeights);
  			results.put("discountFactors",	discountFactors);
  			results.put("swapAnnuities",	swapAnnuities);
  
  			cachedLogSwaprateDerivative = results;
  
  			return results;
  		}
  	}