package umontreal.iro.lecuyer.randvarmulti;

   import cern.colt.matrix.DoubleMatrix2D;
   import cern.colt.matrix.impl.DenseDoubleMatrix2D;
   import cern.colt.matrix.linalg.CholeskyDecomposition;

import umontreal.iro.lecuyer.probdist.NormalDist;
import umontreal.iro.lecuyer.randvar.NormalGen;
import umontreal.iro.lecuyer.rng.RandomStream;


/**
 * Extends {@link MultinormalGen} for a multivariate normal distribution, generated via a Cholesky decomposition of the covariance
 * matrix. The covariance matrix 
 * Σ is decomposed (by the constructor)
 * as 
 * Σ = AAt where 
 * A is a lower-triangular matrix
 * (this is the Cholesky decomposition), and 
 * X is generated via
 * 

*

*
* X = μ + AZ, *

* where * Z is a d-dimensional vector of independent standard normal random * variates, and * At is the transpose of * A. * The covariance matrix * Σ must be positive-definite, otherwise the * Cholesky decomposition will fail. The decomposition method * uses the CholeskyDecomposition class in colt. * */ public class MultinormalCholeskyGen extends MultinormalGen { private void initL() { if (mu.length != sigma.rows() || mu.length != sigma.columns()) throw new IllegalArgumentException ("Incompatible mean vector and covariance matrix"); CholeskyDecomposition decomp = new CholeskyDecomposition (sigma); //if (!decomp.isSymmetricPositiveDefinite()) // throw new IllegalArgumentException // ("The covariance matrix must be symmetric and positive-definite"); sqrtSigma = decomp.getL(); } /** * Equivalent to * {@link #MultinormalCholeskyGen((NormalGen, double[], DoubleMatrix2D)) MultinormalCholeskyGen}(gen1, mu, new DenseDoubleMatrix2D(sigma)). * * @param gen1 the one-dimensional generator * * @param mu the mean vector. * * @param sigma the covariance matrix. * * @exception NullPointerException if any argument is null. * * @exception IllegalArgumentException if the length of the mean * vector is incompatible with the dimensions of the covariance matrix. * * */ public MultinormalCholeskyGen (NormalGen gen1, double[] mu, double[][] sigma) { super(gen1, mu, sigma); initL(); } /** * Constructs a multinormal generator with mean vector mu * and covariance matrix sigma. The mean vector must have the same * length as the dimensions of the covariance matrix, which must be symmetric * and positive-definite. * If any of the above conditions is violated, an exception is thrown. * The vector * Z is generated by calling d times the generator gen1, * which must be a standard normal 1-dimensional generator. * * @param gen1 the one-dimensional generator * * @param mu the mean vector. * * @param sigma the covariance matrix. * * @exception NullPointerException if any argument is null. * * @exception IllegalArgumentException if the length of the mean * vector is incompatible with the dimensions of the covariance matrix. * */ public MultinormalCholeskyGen (NormalGen gen1, double[] mu, DoubleMatrix2D sigma) { super(gen1, mu, sigma); initL(); } /** * Returns the lower-triangular matrix * A in the * Cholesky decomposition of * Σ. * * @return the Cholesky decomposition of the covariance matrix. * */ public DoubleMatrix2D getCholeskyDecompSigma() { return sqrtSigma.copy(); } /** * Sets the covariance matrix * Σ of this multinormal generator * to sigma (and recomputes * A). * * @param sigma the new covariance matrix. * * @exception IllegalArgumentException if sigma has * incorrect dimensions. * * */ public void setSigma (DoubleMatrix2D sigma) { if (sigma.rows() != mu.length || sigma.columns() != mu.length) throw new IllegalArgumentException ("Invalid dimensions of covariance matrix"); CholeskyDecomposition decomp = new CholeskyDecomposition (sigma); //if (!decomp.isSymmetricPositiveDefinite()) // throw new IllegalArgumentException // ("The new covariance matrix must be symmetric and positive-definite"); this.sigma.assign (sigma); this.sqrtSigma = decomp.getL(); } /** * Equivalent to * {@link #nextPoint((NormalGen, double[], DoubleMatrix2D, double[])) nextPoint}(gen1, mu, new DenseDoubleMatrix2D(sigma), p). * */ public static void nextPoint (NormalGen gen1, double[] mu, double[][] sigma, double[] p) { nextPoint (gen1, mu, new DenseDoubleMatrix2D (sigma), p); } /** * Generates a d-dimensional vector from the multinormal * distribution with mean vector mu and covariance matrix * sigma, using the one-dimensional normal generator gen1 to * generate the coordinates of * Z, and using the Cholesky decomposition of * * Σ. The resulting vector is put into p. * Note that this static method will be very slow for large dimensions, since * it computes the Cholesky decomposition at every call. It is therefore * recommended to use a MultinormalCholeskyGen object instead, * if the method is to be called more than once. * * @param p the array to be filled with the generated point. * * @exception IllegalArgumentException if the one-dimensional normal * generator uses a normal distribution with μ not equal to 0, or * σ not equal to 1. * * @exception IllegalArgumentException if the length of the mean * vector is different from the dimensions of the covariance matrix, * or if the covariance matrix is not symmetric and positive-definite. * * @exception NullPointerException if any argument is null. * * */ public static void nextPoint (NormalGen gen1, double[] mu, DoubleMatrix2D sigma, double[] p) { if (gen1 == null) throw new NullPointerException ("gen1 is null"); NormalDist dist = (NormalDist) gen1.getDistribution(); if (dist.getMu() != 0.0) throw new IllegalArgumentException ("mu != 0"); if (dist.getSigma() != 1.0) throw new IllegalArgumentException ("sigma != 1"); if (mu.length != sigma.rows() || mu.length != sigma.columns()) throw new IllegalArgumentException ("Incompatible mean vector and covariance matrix dimensions"); CholeskyDecomposition decomp = new CholeskyDecomposition (sigma); double[] temp = new double[mu.length]; DoubleMatrix2D sqrtSigma = decomp.getL(); for (int i = 0; i < temp.length; i++) { temp[i] = gen1.nextDouble(); if (temp[i] == Double.NEGATIVE_INFINITY) temp[i] = -MYINF; if (temp[i] == Double.POSITIVE_INFINITY) temp[i] = MYINF; } for (int i = 0; i < temp.length; i++) { p[i] = 0; for (int c = 0; c <= i; c++) p[i] += sqrtSigma.getQuick (i, c)*temp[c]; p[i] += mu[i]; } } /** * Generates a point from this multinormal distribution. This is much * faster than the static method as it computes the singular value decomposition * matrix only once in the constructor. * * @param p the array to be filled with the generated point * */ public void nextPoint (double[] p) { int n = mu.length; for (int i = 0; i < n; i++) { temp[i] = gen1.nextDouble(); if (temp[i] == Double.NEGATIVE_INFINITY) temp[i] = -MYINF; if (temp[i] == Double.POSITIVE_INFINITY) temp[i] = MYINF; } for (int i = 0; i < n; i++) { p[i] = 0; // Matrix is lower-triangular for (int c = 0; c <= i; c++) p[i] += sqrtSigma.getQuick (i, c)*temp[c]; p[i] += mu[i]; } } }

Ads help maintain this website.