AcceptanceRejectionRandomNumberGenerator.java
/*
* (c) Copyright Christian P. Fries, Germany. Contact: email@christianfries.com.
*
* Created on 21 May 2018
*/
package net.finmath.randomnumbers;
import java.util.function.DoubleUnaryOperator;
import org.apache.commons.lang3.Validate;
/**
* Class implementing <code>RandomNumberGenerator1D</code> by the acceptance rejection method.
*
* Note that the acceptance rejection methods requires a two dimensional uniform random number sequence with independent components.
*
* @author Christian Fries
* @version 1.1
*/
public class AcceptanceRejectionRandomNumberGenerator implements RandomNumberGenerator1D {
private static final long serialVersionUID = -9060003224133337426L;
private final RandomNumberGenerator uniformRandomNumberGenerator;
private final DoubleUnaryOperator targetDensity;
private final DoubleUnaryOperator referenceDensity;
private final DoubleUnaryOperator referenceDistributionICDF;
private final double acceptanceLevel;
/**
* Create a random number sequence using the given input sequence and acceptance-rejection sampling.
*
* @param uniformRandomNumberGenerator A two dimension (at least) uniform number generator (with i.i.d components). The first two components will be used.
* @param targetDensity The density f of the target distribution F.
* @param referenceDensity The density g.
* @param referenceDistributionICDF The ICDF, i.e. the inverse of G where G' = g.
* @param acceptanceLevel The constant C such that \( f \leq C g \).
*/
public AcceptanceRejectionRandomNumberGenerator(final RandomNumberGenerator uniformRandomNumberGenerator,
final DoubleUnaryOperator targetDensity,
final DoubleUnaryOperator referenceDensity,
final DoubleUnaryOperator referenceDistributionICDF,
final double acceptanceLevel) {
Validate.inclusiveBetween(2, Integer.MAX_VALUE, uniformRandomNumberGenerator.getDimension(), "The acceptance rejection method requires a uniform distributed random number generator with at least dimension 2.");
this.uniformRandomNumberGenerator = uniformRandomNumberGenerator;
this.targetDensity = targetDensity;
this.referenceDensity = referenceDensity;
this.referenceDistributionICDF = referenceDistributionICDF;
this.acceptanceLevel = acceptanceLevel;
}
@Override
public double nextDouble() {
boolean rejected = true;
double y = Double.NaN;
while(rejected) {
final double[] uniform = uniformRandomNumberGenerator.getNext(); // Tuple (u,v) of two uniforms
final double u = uniform[0]; // u that samples acceptance/rejection
y = referenceDistributionICDF.applyAsDouble(uniform[1]); // y = ICDF method applied to v (candidate)
rejected = targetDensity.applyAsDouble(y) < u * acceptanceLevel * referenceDensity.applyAsDouble(y);
}
return y;
}
}