/* * (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de. * * Created on 09.02.2004 */ package net.finmath.montecarlo; import java.io.Serializable; import java.util.Arrays; import net.finmath.functions.PoissonDistribution; import net.finmath.randomnumbers.MersenneTwister; import net.finmath.stochastic.RandomVariableInterface; import net.finmath.time.TimeDiscretizationInterface; /** * Implementation of a time-discrete n-dimensional jump process * <i>J = (J<sub>1</sub>,...,J<sub>n</sub>)</i> where <i>J<sub>i</sub></i> is * a Poisson jump process and <i>J<sub>i</sub></i>, <i>J<sub>j</sub></i> are * independent for <i>i</i> not equal <i>j</i>. * * Here the dimension <i>n</i> is called factors since the increments are used to * generate multi-dimensional multi-factor processes and there one might * use a different number of factors to generate processes of different * dimension. * * The quadruppel (time discretization, jumpIntensities, number of paths, seed) * defines the state of an object of this class. * * The class is immutable and thread safe. It uses lazy initialization. * * @author Christian Fries * @version 1.6 */ public class JumpProcessIncrements implements IndependentIncrementsInterface, Serializable { private static final long serialVersionUID = -5430067621669213475L; private final TimeDiscretizationInterface timeDiscretization; private final int numberOfPaths; private final int seed; private final double[] jumpIntensities; private final AbstractRandomVariableFactory randomVariableFactory; private transient RandomVariableInterface[][] increments; private final Object incrementsLazyInitLock = new Object(); /** * Construct a jump process. * * The constructor allows to set the factory to be used for the construction of * random variables. This allows to generate increments represented * by different implementations of the RandomVariableInterface (e.g. the RandomVariableLowMemory internally * using float representations). * * @param timeDiscretization The time discretization used for the increments. * @param jumpIntensities The jump intensities, one for each factor. * @param numberOfPaths Number of paths to simulate. * @param seed The seed of the random number generator. * @param randomVariableFactory Factory to be used to create random variable. */ public JumpProcessIncrements( TimeDiscretizationInterface timeDiscretization, double[] jumpIntensities, int numberOfPaths, int seed, AbstractRandomVariableFactory randomVariableFactory) { super(); this.timeDiscretization = timeDiscretization; this.jumpIntensities = jumpIntensities; this.numberOfPaths = numberOfPaths; this.seed = seed; this.randomVariableFactory = randomVariableFactory; this.increments = null; // Lazy initialization } /** * Construct a jump process. * * @param timeDiscretization The time discretization used for the Brownian increments. * @param jumpIntensities The vector of jump intensities, one intensity for each factor. * @param numberOfPaths Number of paths to simulate. * @param seed The seed of the random number generator. */ public JumpProcessIncrements( TimeDiscretizationInterface timeDiscretization, double[] jumpIntensities, int numberOfPaths, int seed) { this(timeDiscretization, jumpIntensities, numberOfPaths, seed, new RandomVariableFactory()); } @Override public JumpProcessIncrements getCloneWithModifiedSeed(int seed) { return new JumpProcessIncrements(getTimeDiscretization(), jumpIntensities, getNumberOfPaths(), seed); } @Override public JumpProcessIncrements getCloneWithModifiedTimeDiscretization(TimeDiscretizationInterface newTimeDiscretization) { /// @TODO This can be improved: a complete recreation of the Brownian motion wouldn't be necessary! return new JumpProcessIncrements(newTimeDiscretization, jumpIntensities, getNumberOfPaths(), getSeed()); } @Override public RandomVariableInterface getIncrement(int timeIndex, int factor) { // Thread safe lazy initialization synchronized(incrementsLazyInitLock) { if(increments == null) doGenerateIncrements(); } /* * We return an immutable object which ensures that the receiver does not alter the data. */ return increments[timeIndex][factor]; } /** * Lazy initialization of brownianIncrement. Synchronized to ensure thread safety of lazy init. */ private void doGenerateIncrements() { if(increments != null) return; // Nothing to do // Create random number sequence generator MersenneTwister mersenneTwister = new MersenneTwister(seed); // Allocate memory double[][][] incrementsArray = new double[timeDiscretization.getNumberOfTimeSteps()][jumpIntensities.length][numberOfPaths]; // Pre-calculate Poisson distributions PoissonDistribution[][] poissonDistribution = new PoissonDistribution[timeDiscretization.getNumberOfTimeSteps()][jumpIntensities.length]; for(int timeIndex=0; timeIndex<timeDiscretization.getNumberOfTimeSteps(); timeIndex++) { for(int factorIndex=0; factorIndex<jumpIntensities.length; factorIndex++) { poissonDistribution[timeIndex][factorIndex] = new PoissonDistribution(timeDiscretization.getTimeStep(timeIndex)*jumpIntensities[factorIndex]); } } /* * Generate independent increments. * * The inner loop goes over time and factors. * MersenneTwister is known to generate "independent" increments in 623 dimensions. * Since we want to generate independent streams (paths), the loop over path is the outer loop. */ for(int pathIndex=0; pathIndex<numberOfPaths; pathIndex++) { for(int timeIndex=0; timeIndex<timeDiscretization.getNumberOfTimeSteps(); timeIndex++) { for(int factorIndex=0; factorIndex<jumpIntensities.length; factorIndex++) { double uniformIncrement = mersenneTwister.nextDouble(); double numberOfJumps = poissonDistribution[timeIndex][factorIndex].inverseCumulativeDistribution(uniformIncrement); incrementsArray[timeIndex][factorIndex][pathIndex] = numberOfJumps; } } } // Allocate memory for RandomVariable wrapper objects. increments = new RandomVariableInterface[timeDiscretization.getNumberOfTimeSteps()][jumpIntensities.length]; // Wrap the values in RandomVariable objects for(int timeIndex=0; timeIndex<timeDiscretization.getNumberOfTimeSteps(); timeIndex++) { double time = timeDiscretization.getTime(timeIndex+1); for(int factor=0; factor<jumpIntensities.length; factor++) { increments[timeIndex][factor] = randomVariableFactory.createRandomVariable(time, incrementsArray[timeIndex][factor]); } } } @Override public TimeDiscretizationInterface getTimeDiscretization() { return timeDiscretization; } @Override public int getNumberOfFactors() { return jumpIntensities.length; } @Override public int getNumberOfPaths() { return numberOfPaths; } @Override public RandomVariableInterface getRandomVariableForConstant(double value) { return randomVariableFactory.createRandomVariable(value); } /** * @return Returns the seed. */ public int getSeed() { return seed; } @Override public String toString() { return "JumpProcessIncrements [timeDiscretization=" + timeDiscretization + ", numberOfPaths=" + numberOfPaths + ", seed=" + seed + ", jumpIntensities=" + Arrays.toString(jumpIntensities) + ", randomVariableFactory=" + randomVariableFactory + "]"; } }