/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christianfries.com.
*
* Created on 23.11.2013
*/
package net.finmath.montecarlo;
import java.util.Arrays;
import net.finmath.stochastic.RandomVariableInterface;
import net.finmath.time.TimeDiscretizationInterface;
/**
* 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 BrownianMotionInterface}, 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
*/
public class BrownianBridge implements BrownianMotionInterface {
private final TimeDiscretizationInterface timeDiscretization;
private final int numberOfFactors;
private final int numberOfPaths;
private final int seed;
private RandomVariableInterface[] start;
private RandomVariableInterface[] end;
private AbstractRandomVariableFactory randomVariableFactory = new RandomVariableFactory();
private transient RandomVariableInterface[][] brownianIncrements;
private final Object brownianIncrementsLazyInitLock = new Object();
/**
* 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(TimeDiscretizationInterface timeDiscretization, int numberOfPaths, int seed, RandomVariableInterface[] start, RandomVariableInterface[] end) {
super();
this.timeDiscretization = timeDiscretization;
this.numberOfFactors = start.length;
this.numberOfPaths = numberOfPaths;
this.seed = seed;
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(TimeDiscretizationInterface timeDiscretization, int numberOfPaths, int seed, RandomVariableInterface start, RandomVariableInterface end) {
this(timeDiscretization, numberOfPaths, seed, new RandomVariableInterface[] {start}, new RandomVariableInterface[] {end});
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotionInterface#getBrownianIncrement(int, int)
*/
@Override
public RandomVariableInterface getBrownianIncrement(int timeIndex, 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
BrownianMotion generator = new BrownianMotion(timeDiscretization, numberOfFactors, numberOfPaths, seed);
// Allocate memory
brownianIncrements = new RandomVariableInterface[generator.getTimeDiscretization().getNumberOfTimeSteps()][generator.getNumberOfFactors()];
double endTime = getTimeDiscretization().getTime(getTimeDiscretization().getNumberOfTimeSteps());
for(int factor=0; factor<generator.getNumberOfFactors(); factor++) {
// The end point
RandomVariableInterface endOfFactor = end[factor];
// Initialized the bridge to the start point
RandomVariableInterface brownianBridge = start[factor];
for(int timeIndex=0; timeIndex<getTimeDiscretization().getNumberOfTimeSteps(); timeIndex++) {
double currentTime = getTimeDiscretization().getTime(timeIndex);
double nextTime = getTimeDiscretization().getTime(timeIndex+1);
double alpha = (nextTime-currentTime)/(endTime-currentTime);
// Calculate the next point using the "scheme" of the Brownian bridge
RandomVariableInterface 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.BrownianMotionInterface#getTimeDiscretization()
*/
@Override
public TimeDiscretizationInterface getTimeDiscretization() {
return timeDiscretization;
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotionInterface#getNumberOfFactors()
*/
@Override
public int getNumberOfFactors() {
return numberOfFactors;
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotionInterface#getNumberOfPaths()
*/
@Override
public int getNumberOfPaths() {
return numberOfPaths;
}
@Override
public RandomVariableInterface getRandomVariableForConstant(double value) {
return randomVariableFactory.createRandomVariable(value);
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotionInterface#getCloneWithModifiedSeed(int)
*/
@Override
public BrownianMotionInterface getCloneWithModifiedSeed(int seed) {
return new BrownianBridge(timeDiscretization, numberOfPaths, seed, start, end);
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.BrownianMotionInterface#getCloneWithModifiedTimeDiscretization(net.finmath.time.TimeDiscretizationInterface)
*/
@Override
public BrownianMotionInterface getCloneWithModifiedTimeDiscretization(TimeDiscretizationInterface newTimeDiscretization) {
return new BrownianBridge(newTimeDiscretization, getNumberOfFactors(), seed, start, end);
}
@Override
public RandomVariableInterface getIncrement(int timeIndex, int factor) {
return getBrownianIncrement(timeIndex, factor);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "BrownianBridge [timeDiscretization=" + timeDiscretization
+ ", numberOfFactors=" + numberOfFactors + ", numberOfPaths="
+ numberOfPaths + ", seed=" + seed + ", start="
+ Arrays.toString(start) + ", end=" + Arrays.toString(end)
+ "]";
}
}