BrownianBridge.java
/*
* (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
*
* Created on 23.11.2013
*/
package net.finmath.montecarlo;
import java.io.IOException;
import java.util.Arrays;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretization;
/**
* This class implements a Brownian bridge, i.e., samples of realizations of a Brownian motion
* conditional to a given start and end value.
*
* <p>
* A Brownian bridge is a conditional Brownian motion, i.e. for given random variables
* <i>X</i> and <i>Y</i> the Brownian bridge is
* <br>
* <i>(W(t) | W(s) = X , W(T) = Y)</i>,
* <br>
* where <i>W</i> is a Brownian motion and <i>s ≤ t ≤ T</i>.
* </p>
* <p>
* The samples of the Brownian bridge are generated by a Brownian motion which will be used to fill the gap between start and end.
* It is important that this Browninan motion is independent from the one which generated start and end, i.e. here: it should have a different seed.
* </p>
* <p>
* The class implements the {@code BrownianMotion}, i.e., it only provides the increments
* of the Brownian bridge (however, in most application, like refinement of an Euler-scheme, this is
* exactly the desired object).
* </p>
* <p>
* Note: The number of paths needs to be specified, because the start and the end point
* may be not stochastic, i.e. it is not possible to infer this quantity
* from the specified start and end.
* </p>
*
* @author Christian Fries
* @date 24.11.2013
* @version 1.0
*/
public class BrownianBridge implements BrownianMotion {
private final BrownianMotion generator;
private final RandomVariable[] start;
private final RandomVariable[] end;
private final RandomVariableFactory randomVariableFactory = new RandomVariableFromArrayFactory();
private transient RandomVariable[][] brownianIncrements;
private transient Object brownianIncrementsLazyInitLock = new Object();
public BrownianBridge(final BrownianMotion generator, final RandomVariable[] start, final RandomVariable[] end) {
this.generator = generator;
this.start = start;
this.end = end;
}
/**
* Construct a Brownian bridge, bridging from a given start to a given end.
*
* @param timeDiscretization The time discretization used for the Brownian increments.
* @param numberOfPaths Number of paths to simulate.
* @param seed The seed of the random number generator.
* @param start Start value of the Brownian bridge.
* @param end End value of the Brownian bridge.
*/
public BrownianBridge(final TimeDiscretization timeDiscretization, final int numberOfPaths, final int seed, final RandomVariable[] start, final RandomVariable[] end) {
this(new BrownianMotionFromMersenneRandomNumbers(timeDiscretization, start.length, numberOfPaths, seed), start, end);
}
/**
* Construct a Brownian bridge, bridging from a given start to a given end.
*
* @param timeDiscretization The time discretization used for the Brownian increments.
* @param numberOfPaths Number of paths to simulate.
* @param seed The seed of the random number generator.
* @param start Start value of the Brownian bridge.
* @param end End value of the Brownian bridge.
*/
public BrownianBridge(final TimeDiscretization timeDiscretization, final int numberOfPaths, final int seed, final RandomVariable start, final RandomVariable end) {
this(timeDiscretization, numberOfPaths, seed, new RandomVariable[] {start}, new RandomVariable[] {end});
}
@Override
public RandomVariable getBrownianIncrement(final int timeIndex, final int factor) {
// Thread safe lazy initialization
synchronized(brownianIncrementsLazyInitLock) {
if(brownianIncrements == null) {
doGenerateBrownianMotion();
}
}
/*
* For performance reasons we return directly the stored data (no defensive copy).
* We return an immutable object to ensure that the receiver does not alter the data.
*/
return brownianIncrements[timeIndex][factor];
}
/**
* Lazy initialization of brownianIncrement. Synchronized to ensure thread safety of lazy init.
*/
private void doGenerateBrownianMotion() {
if(brownianIncrements != null)
{
return; // Nothing to do
}
// Allocate memory
brownianIncrements = new RandomVariable[generator.getTimeDiscretization().getNumberOfTimeSteps()][generator.getNumberOfFactors()];
final double endTime = getTimeDiscretization().getTime(getTimeDiscretization().getNumberOfTimeSteps());
for(int factor=0; factor<start.length; factor++) {
// The end point
final RandomVariable endOfFactor = end[factor];
// Initialized the bridge to the start point
RandomVariable brownianBridge = start[factor];
for(int timeIndex=0; timeIndex<getTimeDiscretization().getNumberOfTimeSteps(); timeIndex++) {
final double currentTime = getTimeDiscretization().getTime(timeIndex);
final double nextTime = getTimeDiscretization().getTime(timeIndex+1);
final double alpha = (nextTime-currentTime)/(endTime-currentTime);
// Calculate the next point using the "scheme" of the Brownian bridge
final RandomVariable nextRealization = brownianBridge.mult(1.0-alpha).add(endOfFactor.mult(alpha)).add(generator.getBrownianIncrement(timeIndex, factor).mult(Math.sqrt(1-alpha)));
// Store the increment
brownianIncrements[timeIndex][factor] = nextRealization.sub(brownianBridge);
// Update the bridge to the current point
brownianBridge = nextRealization;
}
}
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotion#getTimeDiscretization()
*/
@Override
public TimeDiscretization getTimeDiscretization() {
return generator.getTimeDiscretization();
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotion#getNumberOfFactors()
*/
@Override
public int getNumberOfFactors() {
return generator.getNumberOfFactors();
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotion#getNumberOfPaths()
*/
@Override
public int getNumberOfPaths() {
return generator.getNumberOfPaths();
}
@Override
public RandomVariable getRandomVariableForConstant(final double value) {
return randomVariableFactory.createRandomVariable(value);
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotion#getCloneWithModifiedSeed(int)
*/
@Override
public BrownianMotion getCloneWithModifiedSeed(final int seed) {
return new BrownianBridge(getTimeDiscretization(), getNumberOfPaths(), seed, start, end);
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotion#getCloneWithModifiedTimeDiscretization(net.finmath.time.TimeDiscretization)
*/
@Override
public BrownianMotion getCloneWithModifiedTimeDiscretization(final TimeDiscretization newTimeDiscretization) {
return new BrownianBridge(generator.getCloneWithModifiedTimeDiscretization(newTimeDiscretization), start, end);
}
@Override
public RandomVariable[] getIncrement(final int timeIndex) {
// Thread safe lazy initialization
synchronized(brownianIncrementsLazyInitLock) {
if(brownianIncrements == null) {
doGenerateBrownianMotion();
}
}
return brownianIncrements[timeIndex].clone();
}
@Override
public RandomVariable getIncrement(final int timeIndex, final int factor) {
return getBrownianIncrement(timeIndex, factor);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "BrownianBridge [generator=" + generator
+ Arrays.toString(start) + ", end=" + Arrays.toString(end)
+ "]";
}
private void readObject(final java.io.ObjectInputStream in) throws ClassNotFoundException, IOException {
in.defaultReadObject();
// initialization of transients
brownianIncrementsLazyInitLock = new Object();
}
}