net.finmath.montecarlo.interestrate.models.LIBORMarketModelFromCovarianceModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of finmath-lib Show documentation
Show all versions of finmath-lib Show documentation
finmath lib is a Mathematical Finance Library in Java.
It provides algorithms and methodologies related to mathematical finance.
/* * (c) Copyright Christian P. Fries, Germany. Contact: [email protected]. * * Created on 09.02.2004 */ package net.finmath.montecarlo.interestrate.models; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import net.finmath.exception.CalculationException; import net.finmath.functions.AnalyticFormulas; import net.finmath.marketdata.model.AnalyticModel; import net.finmath.marketdata.model.curves.DiscountCurve; import net.finmath.marketdata.model.curves.DiscountCurveFromForwardCurve; import net.finmath.marketdata.model.curves.ForwardCurve; import net.finmath.marketdata.model.volatilities.SwaptionMarketData; import net.finmath.marketdata.products.Swap; import net.finmath.marketdata.products.SwapAnnuity; import net.finmath.montecarlo.RandomVariableFactory; import net.finmath.montecarlo.RandomVariableFromArrayFactory; import net.finmath.montecarlo.interestrate.CalibrationProduct; import net.finmath.montecarlo.interestrate.LIBORMarketModel; import net.finmath.montecarlo.interestrate.models.covariance.AbstractLIBORCovarianceModelParametric; import net.finmath.montecarlo.interestrate.models.covariance.LIBORCovarianceModel; import net.finmath.montecarlo.interestrate.models.covariance.LIBORCovarianceModelCalibrateable; import net.finmath.montecarlo.interestrate.products.AbstractTermStructureMonteCarloProduct; import net.finmath.montecarlo.interestrate.products.SwaptionAnalyticApproximation; import net.finmath.montecarlo.interestrate.products.SwaptionSimple; import net.finmath.montecarlo.model.AbstractProcessModel; import net.finmath.montecarlo.process.MonteCarloProcess; import net.finmath.stochastic.RandomVariable; import net.finmath.stochastic.Scalar; import net.finmath.time.RegularSchedule; import net.finmath.time.Schedule; import net.finmath.time.TimeDiscretization; import net.finmath.time.TimeDiscretizationFromArray; /** * Implements a (generalized) LIBOR market model with generic covariance structure (lognormal, normal, displaced or stochastic volatility) * with some drift approximation methods. *
or
* In its default case the class specifies a multi-factor LIBOR market model in its log-normal formulation, that is * Lj = exp(Yj) where * \[ * dY_{j} = \mu_{j} dt + \lambda_{1,j} dW_{1} + \ldots + \lambda_{m,j} dW_{m} * \] *
* The model uses an {@link net.finmath.montecarlo.interestrate.models.covariance.LIBORCovarianceModel} for the specification of * (λ1,j,...,λm,j) as a covariance model. * See {@link net.finmath.montecarlo.model.ProcessModel} for details on the implemented interface *
* However, the class is more general: **
* *- * The model may be log-normal or normal specification with a given local volatility. *
*- * The class implements different measure(drift) / numeraire pairs: terminal measure and spot measure. *
*- * The class allows to configure a discounting curve (e.g. for "OIS discounting") using a simple deterministic zero spread. * In this case, the numeraire \( N(t) \) is adjusted by \( \exp( \int_0^t -\lambda(\tau) d\tau ) \). *
*
* The class specifies a LIBOR market model, that is * Lj = f(Yj) where **
* and *- * f is f(x) = exp(x) (default, log-normal LIBOR Market Model) or *
*- * f is f(x) = x (normal model, used if
*property.set("stateSpace","NORMAL"))
*
* dYj = μj dt + λ1,j dW1 + ... + λm,j dWm
*
* see {@link net.finmath.montecarlo.model.ProcessModel} for details on the implemented interface. *
* The model uses anAbstractLIBORCovarianceModel
as a covariance model. * If the covariance model is of typeAbstractLIBORCovarianceModelParametric
* a calibration to swaptions can be performed. *
* Note that λ may still depend on L, hence generating a log-normal dynamic for L even * if the stateSpace property has been set to NORMAL. *
* * The mapproperties
allows to configure the model. The following keys may be used: **
*- *
*measure
: Possible values: **
*- *
*SPOT
: Simulate under spot measure. In this case, the single curve numeraire * is \( N(T_{i}) = \prod_{j=0}^{i-1} (1 + L(T_{j},T_{j+1};T_{j}) (T_{j+1}-T_{j})) \). *- *
*TERMINAL
: Simulate under terminal measure. In this case, the single curve numeraire * is \( N(T_{i}) = P(T_{n};T_{i}) = \prod_{j=i}^{n-1} (1 + L(T_{j},T_{j+1};T_{i}) (T_{j+1}-T_{j}))^{-1} \). *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
: The state space transform is set to exp, i.e., L = exp(Y). When the covariance model is deterministic, then this is the classical lognormal LIBOR market model. Note that the covariance model may still provide a local volatility function. *- *
*NORMAL
: The state space transform is set to identity, i.e., L = Y. When the covariance model is deterministic, then this is a normal LIBOR market model. Note that the covariance model may still provide a local volatility function. *- *
*simulationTimeInterpolationMethod
: Possible values: **
*- *
*ROUND_DOWN
: \( L(S,T;t) \) is mapped to \( L(S,T,t_{j}) \) with \( t_{j} \) being the largest time in the time discretization such that \( t_{j} \leq t \). *- *
*ROUND_NEAREST
: \( L(S,T;t) \) is mapped to \( L(S,T,t_{j}) \) with \( t_{j} \) being the nearest time in the time discretization. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *
* The main task of this class is to calculate the risk-neutral drift and the * corresponding numeraire given the covariance model. * * The calibration of the covariance structure is not part of this class. For the calibration * of parametric models of the instantaneous covariance see * {@link net.finmath.montecarlo.interestrate.models.covariance.AbstractLIBORCovarianceModelParametric#getCloneCalibrated(LIBORMarketModel, CalibrationProduct[], Map)}. * * @author Christian Fries * @version 1.2 * @see net.finmath.montecarlo.process.MonteCarloProcess The interface for numerical schemes. * @see net.finmath.montecarlo.model.ProcessModel The interface for models provinding parameters to numerical schemes. * @see net.finmath.montecarlo.interestrate.models.covariance.AbstractLIBORCovarianceModel The abstract covariance model plug ins. * @see net.finmath.montecarlo.interestrate.models.covariance.AbstractLIBORCovarianceModelParametric A parametic covariance model including a generic calibration algorithm. */ public class LIBORMarketModelFromCovarianceModel extends AbstractProcessModel implements LIBORMarketModel, Serializable { private static final long serialVersionUID = 4166077559001066615L; public enum Measure { SPOT, TERMINAL } public enum StateSpace { NORMAL, LOGNORMAL } public enum Driftapproximation { EULER, LINE_INTEGRAL, PREDICTOR_CORRECTOR } public enum InterpolationMethod { LINEAR, LOG_LINEAR_UNCORRECTED, LOG_LINEAR_CORRECTED } public enum SimulationTimeInterpolationMethod { ROUND_DOWN, ROUND_NEAREST } private final TimeDiscretization liborPeriodDiscretization; private String forwardCurveName; private final AnalyticModel curveModel; private final ForwardCurve forwardRateCurve; private final DiscountCurve discountCurve; private final RandomVariableFactory randomVariableFactory; private LIBORCovarianceModel covarianceModel; private SwaptionMarketData swaptionMarketData; private final Driftapproximation driftApproximationMethod = Driftapproximation.EULER; private Measure measure = Measure.SPOT; private StateSpace stateSpace = StateSpace.LOGNORMAL; private SimulationTimeInterpolationMethod simulationTimeInterpolationMethod = SimulationTimeInterpolationMethod.ROUND_NEAREST; private InterpolationMethod interpolationMethod = InterpolationMethod.LOG_LINEAR_UNCORRECTED; private double liborCap = 1E5; // This is a cache of the integrated covariance. private double[][][] integratedLIBORCovariance; private transient Object integratedLIBORCovarianceLazyInitLock = new Object(); // Cache for the numeraires, needs to be invalidated if process changes - move out of the object (to process?) private transient MonteCarloProcess numerairesProcess = null; private transient ConcurrentHashMapnumeraires = new ConcurrentHashMap<>(); private transient ConcurrentHashMap numeraireDiscountFactorForwardRates = new ConcurrentHashMap<>(); private transient ConcurrentHashMap numeraireDiscountFactors = new ConcurrentHashMap<>(); private transient Vector interpolationDriftAdjustmentsTerminal = new Vector<>(); /** * Creates a LIBOR Market Model for given covariance with a calibration (if calibration items are given). *
* If calibrationItems in non-empty and the covariance model is a parametric model, * the covariance will be replaced by a calibrate version of the same model, i.e., * the LIBOR Market Model will be calibrated. Note: Calibration is not lazy. *
* The mapproperties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param analyticModel The associated analytic model of this model (containing the associated market data objects like curve). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param randomVariableFactory The random variable factory used to create the initial values of the model. * @param covarianceModel The covariance model to use. * @param calibrationProducts The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i). * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*simulationTimeInterpolationMethod
: Possible values: **
*- *
*ROUND_DOWN
: \( L(S,T;t) \) is mapped to \( L(S,T,t_{j}) \) with \( t_{j} \) being the largest time in the time discretization such that \( t_{j} \leq t \). *- *
*ROUND_NEAREST
: \( L(S,T;t) \) is mapped to \( L(S,T,t_{j}) \) with \( t_{j} \) being the nearest time in the time discretization. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @return A new instance of LIBORMarketModelFromCovarianceModel, possibly calibrated. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ @SuppressWarnings("unchecked") public static LIBORMarketModelFromCovarianceModel of( final TimeDiscretization liborPeriodDiscretization, final AnalyticModel analyticModel, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final RandomVariableFactory randomVariableFactory, final LIBORCovarianceModel covarianceModel, final CalibrationProduct[] calibrationProducts, final Mapproperties ) throws CalculationException { final LIBORMarketModelFromCovarianceModel model = new LIBORMarketModelFromCovarianceModel(liborPeriodDiscretization, analyticModel, forwardRateCurve, discountCurve, randomVariableFactory, covarianceModel, properties); // Perform calibration, if data is given if(calibrationProducts != null && calibrationProducts.length > 0) { Map calibrationParameters = null; if(properties != null && properties.containsKey("calibrationParameters")) { calibrationParameters = (Map )properties.get("calibrationParameters"); } LIBORCovarianceModelCalibrateable covarianceModelParametric = null; try { covarianceModelParametric = (LIBORCovarianceModelCalibrateable)covarianceModel; } catch(final Exception e) { throw new ClassCastException("Calibration restricted to covariance models implementing LIBORCovarianceModelCalibrateable."); } final LIBORCovarianceModel covarianceModelCalibrated = covarianceModelParametric.getCloneCalibrated(model, calibrationProducts, calibrationParameters); final LIBORMarketModelFromCovarianceModel modelCalibrated = model.getCloneWithModifiedCovarianceModel(covarianceModelCalibrated); return modelCalibrated; } else { return model; } } /** * Creates a LIBOR Market Model for given covariance. *
* If calibrationItems in non-empty and the covariance model is a parametric model, * the covariance will be replaced by a calibrate version of the same model, i.e., * the LIBOR Market Model will be calibrated. *
* The mapproperties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param analyticModel The associated analytic model of this model (containing the associated market data objects like curve). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param randomVariableFactory The random variable factory used to create the initial values of the model. * @param covarianceModel The covariance model to use. * @param calibrationProducts The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i). * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final AnalyticModel analyticModel, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final RandomVariableFactory randomVariableFactory, final LIBORCovarianceModel covarianceModel, final CalibrationProduct[] calibrationProducts, final Mapproperties ) throws CalculationException { // Set some properties if(properties != null && properties.containsKey("measure")) { measure = Measure.valueOf(((String)properties.get("measure")).toUpperCase()); } if(properties != null && properties.containsKey("stateSpace")) { stateSpace = StateSpace.valueOf(((String)properties.get("stateSpace")).toUpperCase()); } if(properties != null && properties.containsKey("interpolationMethod")) { interpolationMethod = InterpolationMethod.valueOf(((String)properties.get("interpolationMethod")).toUpperCase()); } if(properties != null && properties.containsKey("simulationTimeInterpolationMethod")) { simulationTimeInterpolationMethod = SimulationTimeInterpolationMethod.valueOf(((String)properties.get("simulationTimeInterpolationMethod")).toUpperCase()); } if(properties != null && properties.containsKey("liborCap")) { liborCap = (Double)properties.get("liborCap"); } Map calibrationParameters = null; if(properties != null && properties.containsKey("calibrationParameters")) { calibrationParameters = (Map )properties.get("calibrationParameters"); } this.liborPeriodDiscretization = liborPeriodDiscretization; curveModel = analyticModel; this.forwardRateCurve = forwardRateCurve; this.discountCurve = discountCurve; this.randomVariableFactory = randomVariableFactory; // Perform calibration, if data is given if(calibrationProducts != null && calibrationProducts.length > 0) { LIBORCovarianceModelCalibrateable covarianceModelParametric = null; try { covarianceModelParametric = (LIBORCovarianceModelCalibrateable)covarianceModel; } catch(final Exception e) { throw new ClassCastException("Calibration restricted to covariance models implementing LIBORCovarianceModelCalibrateable."); } this.covarianceModel = covarianceModelParametric.getCloneCalibrated(this, calibrationProducts, calibrationParameters); } else { this.covarianceModel = covarianceModel; } } /** * Creates a LIBOR Market Model for given covariance. * * The map properties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param analyticModel The associated analytic model of this model (containing the associated market data objects like curve). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param randomVariableFactory The random variable factory used to create the initial values of the model. * @param covarianceModel The covariance model to use. * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final AnalyticModel analyticModel, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final RandomVariableFactory randomVariableFactory, final LIBORCovarianceModel covarianceModel, final Mapproperties ) throws CalculationException { this(liborPeriodDiscretization, analyticModel, forwardRateCurve, discountCurve, randomVariableFactory, covarianceModel, null, properties); } /** * Creates a LIBOR Market Model for given covariance. *
* If calibrationItems in non-empty and the covariance model is a parametric model, * the covariance will be replaced by a calibrate version of the same model, i.e., * the LIBOR Market Model will be calibrated. *
* The mapproperties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param analyticModel The associated analytic model of this model (containing the associated market data objects like curve). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param calibrationItems The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i). * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. * @deprecated Use LIBORMarketModelFromCovarianceModel.of() instead. */ @Deprecated public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final AnalyticModel analyticModel, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final LIBORCovarianceModel covarianceModel, final CalibrationProduct[] calibrationItems, final Mapproperties ) throws CalculationException { this(liborPeriodDiscretization, analyticModel, forwardRateCurve, discountCurve, new RandomVariableFromArrayFactory(), covarianceModel, calibrationItems, properties); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param covarianceModel The covariance model to use. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the cause()
method. */ public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final ForwardCurve forwardRateCurve, final LIBORCovarianceModel covarianceModel ) throws CalculationException { this(liborPeriodDiscretization, forwardRateCurve, new DiscountCurveFromForwardCurve(forwardRateCurve), covarianceModel, new CalibrationProduct[0], null); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final LIBORCovarianceModel covarianceModel ) throws CalculationException { this(liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, new CalibrationProduct[0], null); } /** * Creates a LIBOR Market Model using a given covariance model and calibrating this model * to given swaption volatility data. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param covarianceModel The covariance model to use. * @param swaptionMarketData The set of swaption values to calibrate to. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final ForwardCurve forwardRateCurve, final LIBORCovarianceModel covarianceModel, final SwaptionMarketData swaptionMarketData ) throws CalculationException { this(liborPeriodDiscretization, forwardRateCurve, new DiscountCurveFromForwardCurve(forwardRateCurve), covarianceModel, swaptionMarketData, null); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param swaptionMarketData The set of swaption values to calibrate to. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final LIBORCovarianceModel covarianceModel, final SwaptionMarketData swaptionMarketData ) throws CalculationException { this(liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, swaptionMarketData, null); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param swaptionMarketData The set of swaption values to calibrate to. * @param properties Key value map specifying properties likemeasure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final LIBORCovarianceModel covarianceModel, final SwaptionMarketData swaptionMarketData, final Mapproperties ) throws CalculationException { this( liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, getCalibrationItems( liborPeriodDiscretization, forwardRateCurve, swaptionMarketData, // Condition under which we use analytic approximation (properties == null || properties.get("stateSpace") == null || ((String)properties.get("stateSpace")).toUpperCase().equals(StateSpace.LOGNORMAL.name())) && AbstractLIBORCovarianceModelParametric.class.isAssignableFrom(covarianceModel.getClass()) ), properties ); } /** * Creates a LIBOR Market Model for given covariance. *
* If calibrationItems in non-empty and the covariance model is a parametric model, * the covariance will be replaced by a calibrate version of the same model, i.e., * the LIBOR Market Model will be calibrated. *
* The mapproperties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param calibrationItems The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i). * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. * @deprecated Use LIBORMarketModelFromCovarianceModel.of() instead. */ @Deprecated public LIBORMarketModelFromCovarianceModel( final TimeDiscretization liborPeriodDiscretization, final ForwardCurve forwardRateCurve, final DiscountCurve discountCurve, final LIBORCovarianceModel covarianceModel, final CalibrationProduct[] calibrationItems, final Mapproperties ) throws CalculationException { this(liborPeriodDiscretization, null, forwardRateCurve, discountCurve, covarianceModel, calibrationItems, properties); } private static CalibrationProduct[] getCalibrationItems(final TimeDiscretization liborPeriodDiscretization, final ForwardCurve forwardCurve, final SwaptionMarketData swaptionMarketData, final boolean isUseAnalyticApproximation) { if(swaptionMarketData == null) { return null; } final TimeDiscretization optionMaturities = swaptionMarketData.getOptionMaturities(); final TimeDiscretization tenor = swaptionMarketData.getTenor(); final double swapPeriodLength = swaptionMarketData.getSwapPeriodLength(); final ArrayList calibrationProducts = new ArrayList<>(); for(int exerciseIndex=0; exerciseIndex<=optionMaturities.getNumberOfTimeSteps(); exerciseIndex++) { for(int tenorIndex=0; tenorIndex<=tenor.getNumberOfTimeSteps()-exerciseIndex; tenorIndex++) { // Create a swaption final double exerciseDate = optionMaturities.getTime(exerciseIndex); final double swapLength = tenor.getTime(tenorIndex); if(liborPeriodDiscretization.getTimeIndex(exerciseDate) < 0) { continue; } if(liborPeriodDiscretization.getTimeIndex(exerciseDate+swapLength) <= liborPeriodDiscretization.getTimeIndex(exerciseDate)) { continue; } final int numberOfPeriods = (int)(swapLength / swapPeriodLength); final double[] fixingDates = new double[numberOfPeriods]; final double[] paymentDates = new double[numberOfPeriods]; final double[] swapTenorTimes = new double[numberOfPeriods+1]; for(int periodStartIndex=0; periodStartIndex t for which the numeraire should be returned N(t). * @return The numeraire at the specified time as RandomVariable
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ @Override public RandomVariable getNumeraire(final MonteCarloProcess process, double time) throws CalculationException { if(time < 0) { return randomVariableFactory.createRandomVariable(discountCurve.getDiscountFactor(curveModel, time)); } RandomVariable numeraire = getNumerairetUnAdjusted(process, time); /* * Adjust for discounting, i.e. funding or collateralization */ if (discountCurve != null) { final RandomVariable defaultableZeroBondAsOfTimeZero = getNumeraireDefaultableZeroBondAsOfTimeZero(process, time); final double nonDefaultableZeroBond = numeraire.invert().mult(getNumerairetUnAdjusted(process, 0.0)).getAverage(); numeraire = numeraire.mult(nonDefaultableZeroBond).div(defaultableZeroBondAsOfTimeZero); } return numeraire; } /* * Calculate the numeraire adjustment, that is, the adjustment between the forward curve and the discount curve. * * This methods performs the interpolation only, if the numeraire adjustment is not on the time grid. */ private RandomVariable getNumeraireDefaultableZeroBondAsOfTimeZero(final MonteCarloProcess process, final double time) { final boolean isInterpolateDiscountFactorsOnLiborPeriodDiscretization = true; final TimeDiscretization timeDiscretizationForCurves = isInterpolateDiscountFactorsOnLiborPeriodDiscretization ? liborPeriodDiscretization : process.getTimeDiscretization(); final int timeIndex = timeDiscretizationForCurves.getTimeIndex(time); if(timeIndex >= 0) { return getNumeraireDefaultableZeroBondAsOfTimeZero(process, timeIndex); } else { // Interpolation final int timeIndexPrev = Math.min(-timeIndex-2, getLiborPeriodDiscretization().getNumberOfTimes()-2); final int timeIndexNext = timeIndexPrev+1; final double timePrev = timeDiscretizationForCurves.getTime(timeIndexPrev); final double timeNext = timeDiscretizationForCurves.getTime(timeIndexNext); final RandomVariable numeraireAdjustmentPrev = getNumeraireDefaultableZeroBondAsOfTimeZero(process, timeIndexPrev); final RandomVariable numeraireAdjustmentNext = getNumeraireDefaultableZeroBondAsOfTimeZero(process, timeIndexNext); return numeraireAdjustmentPrev.mult(numeraireAdjustmentNext.div(numeraireAdjustmentPrev).pow((time-timePrev)/(timeNext-timePrev))); } } /* * Calculate the numeraire adjustment, that is, the adjustment of the between the forward curve and the discount curve. * * The numeraire adjustment is the ratio of the time-0 discount factor from the given discount curve P^d(T;0) * and the discount factor P(T;0) calculated from the forward curve constituting the forward rates. * * P^d(T;0) is a given curve. * * P(T;0) is calculated as product (1+L_i(0) (T_{i+1}-T_{i})) * */ private RandomVariable getNumeraireDefaultableZeroBondAsOfTimeZero(final MonteCarloProcess process, final int timeIndex) { final boolean isInterpolateDiscountFactorsOnLiborPeriodDiscretization = true; final TimeDiscretization timeDiscretizationForCurves = isInterpolateDiscountFactorsOnLiborPeriodDiscretization ? liborPeriodDiscretization : process.getTimeDiscretization(); final double time = timeDiscretizationForCurves.getTime(timeIndex); synchronized(numeraireDiscountFactorForwardRates) { ensureCacheConsistency(process); RandomVariable deterministicNumeraireAdjustment = numeraireDiscountFactors.get(time); if(deterministicNumeraireAdjustment == null) { final double dfInitial = discountCurve.getDiscountFactor(curveModel, timeDiscretizationForCurves.getTime(0)); deterministicNumeraireAdjustment = randomVariableFactory.createRandomVariable(dfInitial); numeraireDiscountFactors.put(timeDiscretizationForCurves.getTime(0), deterministicNumeraireAdjustment); for(int i=0; igetNumeraireAdjustments() { return Collections.unmodifiableMap(numeraireDiscountFactorForwardRates); } @Override public RandomVariable[] getInitialState(MonteCarloProcess process) { final double[] liborInitialStates = new double[liborPeriodDiscretization.getNumberOfTimeSteps()]; for(int timeIndex=0; timeIndex Measure.SPOT Measure.TERMINAL
- depending how the * model object was constructed. ForMeasure.TERMINAL
the j-th entry of the return value is the random variable * \[ * \mu_{j}^{\mathbb{Q}^{P(T_{n})}}(t) \ = \ - \mathop{\sum_{l\geq j+1}}_{l\leq n-1} \frac{\delta_{l}}{1+\delta_{l} L_{l}(t)} (\lambda_{j}(t) \cdot \lambda_{l}(t)) * \] * and forMeasure.SPOT
the j-th entry of the return value is the random variable * \[ * \mu_{j}^{\mathbb{Q}^{N}}(t) \ = \ \sum_{m(t) < l\leq j} \frac{\delta_{l}}{1+\delta_{l} L_{l}(t)} (\lambda_{j}(t) \cdot \lambda_{l}(t)) * \] * where \( \lambda_{j} \) is the vector for factor loadings for the j-th component of the stochastic process (that is, the diffusion part is * \( \sum_{k=1}^m \lambda_{j,k} \mathrm{d}W_{k} \)). * * Note: The scalar product of the factor loadings determines the instantaneous covariance. If the model is written in log-coordinates (using exp as a state space transform), we find * \(\lambda_{j} \cdot \lambda_{l} = \sum_{k=1}^m \lambda_{j,k} \lambda_{l,k} = \sigma_{j} \sigma_{l} \rho_{j,l} \). * If the model is written without a state space transformation (in its orignial coordinates) then \(\lambda_{j} \cdot \lambda_{l} = \sum_{k=1}^m \lambda_{j,k} \lambda_{l,k} = L_{j} L_{l} \sigma_{j} \sigma_{l} \rho_{j,l} \). * * * @see net.finmath.montecarlo.interestrate.models.LIBORMarketModelFromCovarianceModel#getNumeraire(MonteCarloProcess, double) The calculation of the drift is consistent with the calculation of the numeraire ingetNumeraire
. * @see net.finmath.montecarlo.interestrate.models.LIBORMarketModelFromCovarianceModel#getFactorLoading(MonteCarloProcess, int, int, RandomVariable[]) The factor loading \( \lambda_{j,k} \). * * @param process The discretization process generating this model. The process provides call backs for TimeDiscretization and allows calls to getProcessValue for timeIndices less or equal the given one. * @param timeIndex Time index i for which the drift should be returned μ(ti). * @param realizationAtTimeIndex Time current forward rate vector at time index i which should be used in the calculation. * @return The drift vector μ(ti) asRandomVariableFromDoubleArray[]
*/ @Override public RandomVariable[] getDrift(final MonteCarloProcess process, final int timeIndex, final RandomVariable[] realizationAtTimeIndex, final RandomVariable[] realizationPredictor) { final double time = process.getTime(timeIndex); // t - current simulation time int firstForwardRateIndex = this.getLiborPeriodIndex(time)+1; // m(t)+1 - the end of the current period if(firstForwardRateIndex<0) { firstForwardRateIndex = -firstForwardRateIndex-1 + 1; } final RandomVariable zero = Scalar.of(0.0); // Allocate drift vector and initialize to zero (will be used to sum up drift components) final RandomVariable[] drift = new RandomVariable[getNumberOfComponents()]; for(int componentIndex=firstForwardRateIndex; componentIndex=firstForwardRateIndex; componentIndex--) { final double periodLength = getLiborPeriodDiscretization().getTimeStep(componentIndex); final RandomVariable forwardRate = realizationAtTimeIndex[componentIndex]; RandomVariable oneStepMeasureTransform = Scalar.of(-periodLength).discount(forwardRate, periodLength); if(stateSpace == StateSpace.LOGNORMAL) { oneStepMeasureTransform = oneStepMeasureTransform.mult(forwardRate); } final RandomVariable[] factorLoading = getFactorLoading(process, timeIndex, componentIndex, realizationAtTimeIndex); drift[componentIndex] = drift[componentIndex].addSumProduct(factorLoadingsSums, factorLoading); for(int factorIndex=0; factorIndex process.getTime(timeIndex+1)-time) { timeIndex++; } } // The forward rates are provided on fractional tenor discretization points using linear interpolation. See ISBN 0470047224. // Interpolation on tenor using interpolationMethod if(periodEndIndex < 0) { final int previousEndIndex = (-periodEndIndex-1)-1; final double nextEndTime = getLiborPeriod(previousEndIndex+1); // Interpolate libor from periodStart to periodEnd on periodEnd final RandomVariable onePlusLongLIBORdt = getForwardRate(process, time, periodStart, nextEndTime).mult(nextEndTime - periodStart).add(1.0); final RandomVariable onePlusInterpolatedLIBORDt = getOnePlusInterpolatedLIBORDt(process, timeIndex, periodEnd, previousEndIndex); return onePlusLongLIBORdt.div(onePlusInterpolatedLIBORDt).sub(1.0).div(periodEnd - periodStart); } // Interpolation on tenor using interpolationMethod if(periodStartIndex < 0) { final int previousStartIndex = (-periodStartIndex-1)-1; final double prevStartTime = getLiborPeriod(previousStartIndex); final double nextStartTime = getLiborPeriod(previousStartIndex+1); if(nextStartTime > periodEnd) { throw new AssertionError("Interpolation not possible."); } if(nextStartTime == periodEnd) { return getOnePlusInterpolatedLIBORDt(process, timeIndex, periodStart, previousStartIndex).sub(1.0).div(periodEnd - periodStart); } // RandomVariable onePlusLongLIBORdt = getLIBOR(Math.min(prevStartTime, time), nextStartTime, periodEnd).mult(periodEnd - nextStartTime).add(1.0); final RandomVariable onePlusLongLIBORdt = getForwardRate(process, time, nextStartTime, periodEnd).mult(periodEnd - nextStartTime).add(1.0); final RandomVariable onePlusInterpolatedLIBORDt = getOnePlusInterpolatedLIBORDt(process, timeIndex, periodStart, previousStartIndex); return onePlusLongLIBORdt.mult(onePlusInterpolatedLIBORDt).sub(1.0).div(periodEnd - periodStart); } if(periodStartIndex < 0 || periodEndIndex < 0) { throw new AssertionError("LIBOR requested outside libor discretization points and interpolation was not performed."); } // If this is a model primitive then return it if(periodStartIndex+1==periodEndIndex) { return getLIBOR(process, timeIndex, periodStartIndex); } // The requested LIBOR is not a model primitive. We need to calculate it (slow!) RandomVariable accrualAccount = null; // Calculate the value of the forward bond for(int periodIndex = periodStartIndex; periodIndex = liborPeriodDiscretization.getNumberOfTimes() || timeIndex < 0) { throw new ArrayIndexOutOfBoundsException("Index for LIBOR period discretization out of bounds: " + timeIndex + "."); } return liborPeriodDiscretization.getTime(timeIndex); } @Override public int getLiborPeriodIndex(final double time) { return liborPeriodDiscretization.getTimeIndex(time); } @Override public TimeDiscretization getLiborPeriodDiscretization() { return liborPeriodDiscretization; } /** * @return Returns the LIBOR rates interpolation method. See {@link InterpolationMethod}. */ public InterpolationMethod getInterpolationMethod() { return interpolationMethod; } /** * @return Returns the measure. See {@link Measure}. */ public Measure getMeasure() { return measure; } @Override public double[][][] getIntegratedLIBORCovariance(TimeDiscretization simulationTimeDiscretization) { synchronized (integratedLIBORCovarianceLazyInitLock) { if(integratedLIBORCovariance == null) { final TimeDiscretization liborPeriodDiscretization = getLiborPeriodDiscretization(); integratedLIBORCovariance = new double[simulationTimeDiscretization.getNumberOfTimeSteps()][liborPeriodDiscretization.getNumberOfTimeSteps()][liborPeriodDiscretization.getNumberOfTimeSteps()]; for(int timeIndex = 0; timeIndex < simulationTimeDiscretization.getNumberOfTimeSteps(); timeIndex++) { final double dt = simulationTimeDiscretization.getTime(timeIndex+1) - simulationTimeDiscretization.getTime(timeIndex); final RandomVariable[][] factorLoadings = new RandomVariable[liborPeriodDiscretization.getNumberOfTimeSteps()][]; // Prefetch factor loadings for(int componentIndex = 0; componentIndex < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex++) { factorLoadings[componentIndex] = covarianceModel.getFactorLoading(simulationTimeDiscretization.getTime(timeIndex), liborPeriodDiscretization.getTime(componentIndex), null); } for(int componentIndex1 = 0; componentIndex1 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex1++) { final RandomVariable[] factorLoadingOfComponent1 = factorLoadings[componentIndex1]; // Sum the libor cross terms (use symmetry) for(int componentIndex2 = componentIndex1; componentIndex2 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex2++) { double integratedLIBORCovarianceValue = 0.0; if(getLiborPeriod(componentIndex1) > simulationTimeDiscretization.getTime(timeIndex)) { final RandomVariable[] factorLoadingOfComponent2 = factorLoadings[componentIndex2]; for(int factorIndex = 0; factorIndex < factorLoadingOfComponent2.length; factorIndex++) { integratedLIBORCovarianceValue += factorLoadingOfComponent1[factorIndex].get(0) * factorLoadingOfComponent2[factorIndex].get(0) * dt; } } integratedLIBORCovariance[timeIndex][componentIndex1][componentIndex2] = integratedLIBORCovarianceValue; } } } // Integrate over time (i.e. sum up). for(int timeIndex = 1; timeIndex < simulationTimeDiscretization.getNumberOfTimeSteps(); timeIndex++) { final double[][] prevIntegratedLIBORCovariance = integratedLIBORCovariance[timeIndex-1]; final double[][] thisIntegratedLIBORCovariance = integratedLIBORCovariance[timeIndex]; for(int componentIndex1 = 0; componentIndex1 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex1++) { for(int componentIndex2 = componentIndex1; componentIndex2 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex2++) { thisIntegratedLIBORCovariance[componentIndex1][componentIndex2] = prevIntegratedLIBORCovariance[componentIndex1][componentIndex2] + thisIntegratedLIBORCovariance[componentIndex1][componentIndex2]; thisIntegratedLIBORCovariance[componentIndex2][componentIndex1] = thisIntegratedLIBORCovariance[componentIndex1][componentIndex2]; } } } } } return integratedLIBORCovariance; } @Override public AnalyticModel getAnalyticModel() { return curveModel; } @Override public DiscountCurve getDiscountCurve() { return discountCurve; } @Override public ForwardCurve getForwardRateCurve() { return forwardRateCurve; } /** * Return the swaption market data used for calibration (if any, may be null). * * @return The swaption market data used for calibration (if any, may be null). */ public SwaptionMarketData getSwaptionMarketData() { return swaptionMarketData; } @Override public LIBORCovarianceModel getCovarianceModel() { return covarianceModel; } @Override public Object clone() { try { final Map properties = new HashMap<>(); properties.put("measure", measure.name()); properties.put("stateSpace", stateSpace.name()); properties.put("interpolationMethod", interpolationMethod.name()); properties.put("liborCap", liborCap); return LIBORMarketModelFromCovarianceModel.of(getLiborPeriodDiscretization(), getAnalyticModel(), getForwardRateCurve(), getDiscountCurve(), randomVariableFactory, covarianceModel, null, properties); } catch (final CalculationException e) { return null; } } /** * @param covarianceModel A covariance model * @return A new LIBORMarketModelFromCovarianceModel
using the specified covariance model. */ @Override public LIBORMarketModelFromCovarianceModel getCloneWithModifiedCovarianceModel(final LIBORCovarianceModel covarianceModel) { final LIBORMarketModelFromCovarianceModel model = (LIBORMarketModelFromCovarianceModel)this.clone(); model.covarianceModel = covarianceModel; return model; } @Override public LIBORMarketModelFromCovarianceModel getCloneWithModifiedData(final MapdataModified) throws CalculationException { RandomVariableFactory randomVariableFactory = this.randomVariableFactory; TimeDiscretization liborPeriodDiscretization = this.liborPeriodDiscretization; AnalyticModel analyticModel = curveModel; ForwardCurve forwardRateCurve = this.forwardRateCurve; DiscountCurve discountCurve = this.discountCurve; LIBORCovarianceModel covarianceModel = this.covarianceModel; final Map properties = new HashMap<>(); properties.put("measure", measure.name()); properties.put("stateSpace", stateSpace.name()); properties.put("interpolationMethod", interpolationMethod.name()); properties.put("liborCap", liborCap); if(dataModified != null) { randomVariableFactory = (RandomVariableFactory)dataModified.getOrDefault("randomVariableFactory", randomVariableFactory); liborPeriodDiscretization = (TimeDiscretization)dataModified.getOrDefault("liborPeriodDiscretization", liborPeriodDiscretization); analyticModel = (AnalyticModel)dataModified.getOrDefault("analyticModel", analyticModel); forwardRateCurve = (ForwardCurve)dataModified.getOrDefault("forwardRateCurve", forwardRateCurve); discountCurve = (DiscountCurve)dataModified.getOrDefault("discountCurve", discountCurve); covarianceModel = (LIBORCovarianceModel)dataModified.getOrDefault("covarianceModel", covarianceModel); if(dataModified.containsKey("swaptionMarketData")) { throw new RuntimeException("Swaption market data as input for getCloneWithModifiedData not supported."); } if(dataModified.containsKey("forwardRateShift")) { try { final double[] forwardCurveValues = getForwardRateCurve().getParameter(); final double[] forwardCurveValuesShift = (double[])dataModified.get("forwardRateShift"); final double[] forwardCurveValuesShifted = new double[forwardCurveValues.length]; for(int i=0; i getModelParameters() { // Using process from cache final MonteCarloProcess process = numerairesProcess; final Map modelParameters = new TreeMap<>(); // Add initial values for(int liborIndex=0; liborIndex numeraireAdjustment : numeraireDiscountFactorForwardRates.entrySet()) { modelParameters.put("NUMERAIREADJUSTMENT("+ numeraireAdjustment.getKey() + ")", numeraireAdjustment.getValue()); } return modelParameters; } private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); /* * Init transient fields */ integratedLIBORCovarianceLazyInitLock = new Object(); numeraires = new ConcurrentHashMap<>(); numeraireDiscountFactorForwardRates = new ConcurrentHashMap<>(); numeraireDiscountFactors = new ConcurrentHashMap<>(); interpolationDriftAdjustmentsTerminal = new Vector<>(); } @Override public String toString() { return "LIBORMarketModelFromCovarianceModel [liborPeriodDiscretization=" + liborPeriodDiscretization + ", forwardCurveName=" + forwardCurveName + ", curveModel=" + curveModel + ", forwardRateCurve=" + forwardRateCurve + ", discountCurve=" + discountCurve + ", covarianceModel=" + covarianceModel + ", driftApproximationMethod=" + driftApproximationMethod + ", measure=" + measure + ", stateSpace=" + stateSpace + "]"; } }