/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de.
*
* Created on 26.11.2012
*/
package net.finmath.marketdata.calibration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import net.finmath.marketdata.model.AnalyticModelInterface;
import net.finmath.marketdata.products.AnalyticProductInterface;
import net.finmath.optimizer.OptimizerFactoryInterface;
import net.finmath.optimizer.OptimizerFactoryLevenbergMarquardt;
import net.finmath.optimizer.OptimizerInterface;
import net.finmath.optimizer.SolverException;
/**
* Generates a calibrated model for a given set
* of <code>calibrationProducts</code> with respect to given <code>Curve</code>s.
*
* The model and the curve are assumed to be immutable, i.e., the solver
* will return a calibrate clone, containing clones for every curve
* which is part of the set of curves to be calibrated.
*
* The calibration is performed as a multi-threaded global optimization.
* I will greatly profit from a multi-core architecture.
*
* @author Christian Fries
*/
public class Solver {
private final AnalyticModelInterface model;
private final List<AnalyticProductInterface> calibrationProducts;
private final List<Double> calibrationTargetValues;
private final double calibrationAccuracy;
private final ParameterTransformation parameterTransformation;
private OptimizerFactoryInterface optimizerFactory;
private final double evaluationTime;
private final int maxIterations = 1000;
private int iterations = 0;
private double accuracy = Double.POSITIVE_INFINITY;
/**
* Generate a solver for the given parameter objects (independents) and
* objective functions (dependents).
*
* @param model The model from which a calibrated clone should be created.
* @param calibrationProducts The objective functions.
* @param calibrationTargetValues Array of target values for the objective functions.
* @param parameterTransformation A parameter transformation, if any, otherwise null.
* @param evaluationTime Evaluation time applied to the calibration products.
* @param optimizerFactory A factory providing the optimizer (for the given objective function)
*/
public Solver(AnalyticModelInterface model, Vector<AnalyticProductInterface> calibrationProducts, List<Double> calibrationTargetValues, ParameterTransformation parameterTransformation, double evaluationTime, OptimizerFactoryInterface optimizerFactory) {
super();
this.model = model;
this.calibrationProducts = calibrationProducts;
this.calibrationTargetValues = calibrationTargetValues;
this.parameterTransformation = parameterTransformation;
this.evaluationTime = evaluationTime;
this.optimizerFactory = optimizerFactory;
this.calibrationAccuracy = 0.0;
}
/**
* Generate a solver for the given parameter objects (independents) and
* objective functions (dependents).
*
* @param model The model from which a calibrated clone should be created.
* @param calibrationProducts The objective functions.
* @param calibrationTargetValues Array of target values for the objective functions.
* @param parameterTransformation A parameter transformation, if any, otherwise null.
* @param evaluationTime Evaluation time applied to the calibration products.
* @param calibrationAccuracy The error tolerance of the solver.
*/
public Solver(AnalyticModelInterface model, Vector<AnalyticProductInterface> calibrationProducts, List<Double> calibrationTargetValues, ParameterTransformation parameterTransformation, double evaluationTime, double calibrationAccuracy) {
super();
this.model = model;
this.calibrationProducts = calibrationProducts;
this.calibrationTargetValues = calibrationTargetValues;
this.parameterTransformation = parameterTransformation;
this.evaluationTime = evaluationTime;
this.calibrationAccuracy = calibrationAccuracy;
this.optimizerFactory = null;
}
/**
* Generate a solver for the given parameter objects (independents) and
* objective functions (dependents).
*
* @param model The model from which a calibrated clone should be created.
* @param calibrationProducts The objective functions.
* @param calibrationTargetValues Array of target values for the objective functions.
* @param evaluationTime Evaluation time applied to the calibration products.
* @param calibrationAccuracy The error tolerance of the solver.
*/
public Solver(AnalyticModelInterface model, Vector<AnalyticProductInterface> calibrationProducts, List<Double> calibrationTargetValues, double evaluationTime, double calibrationAccuracy) {
this(model, calibrationProducts, calibrationTargetValues, null, evaluationTime, calibrationAccuracy);
}
/**
* Generate a solver for the given parameter objects (independents) and
* objective functions (dependents).
*
* @param model The model from which a calibrated clone should be created.
* @param calibrationProducts The objective functions.
* @param evaluationTime Evaluation time applied to the calibration products.
* @param calibrationAccuracy The error tolerance of the solver.
*/
public Solver(AnalyticModelInterface model, Vector<AnalyticProductInterface> calibrationProducts, double evaluationTime, double calibrationAccuracy) {
this(model, calibrationProducts, null, null, evaluationTime, calibrationAccuracy);
}
/**
* Generate a solver for the given parameter objects (independents) and
* objective functions (dependents).
*
* @param model The model from which a calibrated clone should be created.
* @param calibrationProducts The objective functions.
*/
public Solver(AnalyticModelInterface model, Vector<AnalyticProductInterface> calibrationProducts) {
this(model, calibrationProducts, 0.0, 0.0);
}
/**
* Find the model such that the equation
* <center>
* <code>
* objectiveFunctions.getValue(model) = 0
* </code>
* </center>
* holds.
*
* @param objectsToCalibrate The set of parameterized objects to calibrate.
* @return A reference to a calibrated clone of the given model.
* @throws net.finmath.optimizer.SolverException Thrown if the underlying optimizer does not find a solution.
*/
public AnalyticModelInterface getCalibratedModel(Set<ParameterObjectInterface> objectsToCalibrate) throws SolverException {
final ParameterAggregation<ParameterObjectInterface> parameterAggregate = new ParameterAggregation<ParameterObjectInterface>(objectsToCalibrate);
// Set solver parameters
final double[] initialParameters;
// Apply parameter transformation to solver parameter space
if(parameterTransformation != null) initialParameters = parameterTransformation.getSolverParameter(parameterAggregate.getParameter());
else initialParameters = parameterAggregate.getParameter();
final double[] zeros = new double[calibrationProducts.size()];
final double[] ones = new double[calibrationProducts.size()];
final double[] lowerBound = new double[initialParameters.length];
final double[] upperBound = new double[initialParameters.length];
java.util.Arrays.fill(zeros, 0.0);
java.util.Arrays.fill(ones, 1.0);
java.util.Arrays.fill(lowerBound, Double.NEGATIVE_INFINITY);
java.util.Arrays.fill(upperBound, Double.POSITIVE_INFINITY);
OptimizerInterface.ObjectiveFunction objectiveFunction = new OptimizerInterface.ObjectiveFunction() {
public void setValues(double[] parameters, double[] values) throws SolverException {
double[] modelParameters = parameters;
try {
if(parameterTransformation != null) {
modelParameters = parameterTransformation.getParameter(parameters);
// Copy back the parameter constrain to inform the optimizer
System.arraycopy(parameterTransformation.getSolverParameter(modelParameters), 0, parameters, 0, parameters.length);
}
Map<ParameterObjectInterface, double[]> curvesParameterPairs = parameterAggregate.getObjectsToModifyForParameter(modelParameters);
AnalyticModelInterface modelClone = model.getCloneForParameter(curvesParameterPairs);
for(int i=0; i<calibrationProducts.size(); i++) {
values[i] = calibrationProducts.get(i).getValue(evaluationTime, modelClone);
}
if(calibrationTargetValues != null) {
for(int i=0; i<calibrationTargetValues.size(); i++) {
values[i] -= calibrationTargetValues.get(i);
}
}
} catch (CloneNotSupportedException e) {
throw new SolverException(e);
}
}
};
if(optimizerFactory == null) {
int maxThreads = Math.min(2 * Math.max(Runtime.getRuntime().availableProcessors(), 1), initialParameters.length);
optimizerFactory = new OptimizerFactoryLevenbergMarquardt(maxIterations, calibrationAccuracy, maxThreads);
}
OptimizerInterface optimizer = optimizerFactory.getOptimizer(objectiveFunction, initialParameters, lowerBound, upperBound, zeros);
optimizer.run();
iterations = optimizer.getIterations();
double[] bestParameters = optimizer.getBestFitParameters();
if(parameterTransformation != null) bestParameters = parameterTransformation.getParameter(bestParameters);
AnalyticModelInterface calibratedModel = null;
try {
Map<ParameterObjectInterface, double[]> curvesParameterPairs = parameterAggregate.getObjectsToModifyForParameter(bestParameters);
calibratedModel = model.getCloneForParameter(curvesParameterPairs);
} catch (CloneNotSupportedException e) {
throw new SolverException(e);
}
accuracy = 0.0;
for(int i=0; i<calibrationProducts.size(); i++) {
double error = calibrationProducts.get(i).getValue(evaluationTime, calibratedModel);
if(calibrationTargetValues != null) error -= calibrationTargetValues.get(i);
accuracy += error * error;
}
accuracy = Math.sqrt(accuracy/calibrationProducts.size());
return calibratedModel;
}
/**
* Returns the number of iterations required in the last solver step.
*
* @return The number of iterations required.
*/
public int getIterations() {
return iterations;
}
/**
* Returns the accuracy achieved in the last solver run.
*
* @return The accuracy achieved in the last solver run.
*/
public double getAccuracy() {
return accuracy;
}
}