LIBORCorrelationModelThreeParameterExponentialDecay.java

/*
 * (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
 *
 * Created on 20.05.2006
 */
package net.finmath.montecarlo.interestrate.models.covariance;

import java.util.Map;

import net.finmath.functions.LinearAlgebra;
import net.finmath.stochastic.RandomVariable;
import net.finmath.stochastic.Scalar;
import net.finmath.time.TimeDiscretization;

/**
 * Simple correlation model given by R, where R is a factor reduced matrix
 * (see {@link net.finmath.functions.LinearAlgebra#factorReduction(double[][], int)}) created from the
 * \( n \) Eigenvectors of \( \tilde{R} \) belonging to the \( n \) largest non-negative Eigenvalues,
 * where \( \tilde{R} = \tilde{\rho}_{i,j} \) and
 * \[ \tilde{\rho}_{i,j} = b + (1-b) * \exp(-a |T_{i} - T_{j}| - c \max(T_{i},T_{j}))
 *
 * @see net.finmath.functions.LinearAlgebra#factorReduction(double[][], int)
 *
 * @author Christian Fries
 * @version 1.0
 */
public class LIBORCorrelationModelThreeParameterExponentialDecay extends LIBORCorrelationModel {

	private static final long serialVersionUID = 5063076041285957177L;

	private final int		numberOfFactors;
	private final double	a;
	private final double	b;
	private final double	c;
	private final boolean isCalibrateable;

	private final Object lazyInitLock = new Object();	// lock used for lazy init of correlationMatrix and factorMatrix

	private transient double[][]	correlationMatrix;
	private transient double[][]	factorMatrix;

	public LIBORCorrelationModelThreeParameterExponentialDecay(final TimeDiscretization timeDiscretization, final TimeDiscretization liborPeriodDiscretization, final int numberOfFactors, final double a, final double b, final double c, final boolean isCalibrateable) {
		super(timeDiscretization, liborPeriodDiscretization);

		this.numberOfFactors = numberOfFactors;
		this.a = a;
		this.b = b;
		this.c = c;
		this.isCalibrateable = isCalibrateable;
	}

	@Override
	public RandomVariable[] getParameter() {
		if(!isCalibrateable) {
			return null;
		}

		final RandomVariable[] parameter = new RandomVariable[3];

		parameter[0] = new Scalar(a);
		parameter[1] = new Scalar(b);
		parameter[2] = new Scalar(c);

		return parameter;
	}

	@Override
	public LIBORCorrelationModelThreeParameterExponentialDecay getCloneWithModifiedParameter(final RandomVariable[] parameter) {
		if(!isCalibrateable) {
			return this;
		}

		// TODO The model does not consider the parameters to be of type RandomVariable. This also breaks AAD w.r.t. these parameters.
		return new LIBORCorrelationModelThreeParameterExponentialDecay(
				getTimeDiscretization(),
				getLiborPeriodDiscretization(),
				getNumberOfFactors(),
				parameter[0].doubleValue(), parameter[1].doubleValue(), parameter[2].doubleValue(), isCalibrateable);
	}

	@Override
	public double	getFactorLoading(final int timeIndex, final int factor, final int component) {
		synchronized (lazyInitLock) {
			if(factorMatrix == null) {
				initialize(numberOfFactors, a, b, c);
			}
		}

		return factorMatrix[component][factor];
	}
	@Override
	public double	getCorrelation(final int timeIndex, final int component1, final int component2) {
		synchronized (lazyInitLock) {
			if(correlationMatrix == null) {
				initialize(numberOfFactors, a, b, c);
			}
		}

		return correlationMatrix[component1][component2];
	}

	@Override
	public int		getNumberOfFactors() {
		return numberOfFactors;
	}

	private void initialize(final int numberOfFactors, double a, double b, double c) {
		/*
		 * Create instantaneous correlation matrix
		 */

		a = Math.max(a, 0.0);
		b = Math.min(Math.max(b, 0.0), 1.0);
		c = Math.max(c, 0.0);

		correlationMatrix = new double[getLiborPeriodDiscretization().getNumberOfTimeSteps()][getLiborPeriodDiscretization().getNumberOfTimeSteps()];
		for(int row=0; row<correlationMatrix.length; row++) {
			for(int col=row+1; col<correlationMatrix[row].length; col++) {
				// Exponentially decreasing instantaneous correlation
				final double T1 = getLiborPeriodDiscretization().getTime(row);
				final double T2 = getLiborPeriodDiscretization().getTime(col);
				final double correlation = b + (1-b) * Math.exp(-a * Math.abs(T1 - T2) - c * Math.max(T1, T2));
				correlationMatrix[row][col] = correlation;
				correlationMatrix[col][row] = correlation;
			}
			correlationMatrix[row][row] = 1.0;
		}

		/*
		 * Perform a factor decomposition (and reduction if numberOfFactors < correlationMatrix.columns())
		 */
		factorMatrix = LinearAlgebra.factorReduction(correlationMatrix, numberOfFactors);

		for(int component1=0; component1<factorMatrix.length; component1++) {
			for(int component2=component1+1; component2<factorMatrix.length; component2++) {
				double correlation = 0.0;
				for(int factor=0; factor<factorMatrix[component1].length; factor++) {
					correlation += factorMatrix[component1][factor] * factorMatrix[component2][factor];
				}
				correlationMatrix[component1][component2] = correlation;
				correlationMatrix[component2][component1] = correlation;
			}
			correlationMatrix[component1][component1] = 1.0;
		}
	}

	@Override
	public Object clone() {
		initialize(numberOfFactors, a, b, c);

		final LIBORCorrelationModelThreeParameterExponentialDecay newModel = new LIBORCorrelationModelThreeParameterExponentialDecay(
				super.getTimeDiscretization(),
				super.getLiborPeriodDiscretization(),
				numberOfFactors,
				a, b, c, isCalibrateable);

		newModel.correlationMatrix	= correlationMatrix;
		newModel.factorMatrix		= factorMatrix;

		return newModel;
	}

	@Override
	public LIBORCorrelationModel getCloneWithModifiedData(final Map<String, Object> dataModified) {
		TimeDiscretization timeDiscretization = this.getTimeDiscretization();
		TimeDiscretization liborPeriodDiscretization = this.getLiborPeriodDiscretization();
		int numberOfFactors = this.getNumberOfFactors();
		double a = this.a;
		double b = this.b;
		double c = this.c;
		boolean isCalibrateable = this.isCalibrateable;

		if(dataModified != null) {
			timeDiscretization = (TimeDiscretization)dataModified.getOrDefault("timeDiscretization", timeDiscretization);
			liborPeriodDiscretization = (TimeDiscretization)dataModified.getOrDefault("liborPeriodDiscretization", liborPeriodDiscretization);
			numberOfFactors = (int)dataModified.getOrDefault("numberOfFactors", numberOfFactors);
			a = (double)dataModified.getOrDefault("a", a);
			b = (double)dataModified.getOrDefault("a", b);
			c = (double)dataModified.getOrDefault("a", c);
			isCalibrateable = (boolean)dataModified.getOrDefault("isCalibrateable", isCalibrateable);
		}

		final LIBORCorrelationModel newModel = new LIBORCorrelationModelThreeParameterExponentialDecay(timeDiscretization, liborPeriodDiscretization, numberOfFactors, a, b, c, isCalibrateable);
		return newModel;
	}
}