Before we go into the implementation, a bit of theory on Monte Carlo and linear algebra, and in particular the Cholesky decomposition. Firstly Monte Carlo: assume we have a Standard Gaussian vector $Z=(Z_i)$ and we want a general Gaussian vector $X=(X_i)$ with correlation matrix $C = (C_{ij})$. Because everything is linear we know that $X$ will be of the form $X = Z \cdot M$ with some matrix $M$ (we assume the $X$ have zero expectation for simplicity; also we multiply vectors from the left because we prefer row vectors).
Assuming that we have such matrix M what is the corresponding correlation matrix $C^M$? In order to find out we need to calculate $$ E[X_iX_j] = \sum_{\mu\nu} M_{\mu i} M_{\nu j} E[Z_\mu Z_\nu] = \sum_{\mu} M_{\mu i} M_{\mu j} = (M^tM)_{ij} $$ ie we find that the covariance matrix is the square of $M$ (in the matrix sense, ie $M^tM$ where $M^t$ is the transposed matrix, ie with rows and columns interchanged). So if we want to generate a vector $X$ with covariance $C$ what we effectively need is a matrix $M$ such that $C= M^tM$.
Enter Cholesky decomposition: in a nutshell, if a matrix $M$ is symmetric and positive definite (with strictly positive eigenvaluesl; as every non-degenerate covariance matrix is) then there is a unique decomposition $C = L^tL$ with the $L$ being a triangular matrix.
First we generat a covariance matrix. This is not entirely trivial - the matrix must be symmetric and positive definite - and one way going about is to simply write $C = R^tR$ where $R$ is any random matrix (note that this is not a particularly good covariance matrix, because it is pretty close to the one-systemic-factor model)
In [1]:
import numpy as np
In [2]:
d = 4
R = np.random.uniform(-1,1,(d,d))+np.eye(d)
C = np.dot(R.T, R)
#C, sort(eigvalsh(C))[::-1]
In [3]:
from scipy.linalg import cholesky
M = cholesky(C)
M
Out[3]:
In [4]:
N = 10000
z = np.random.standard_normal((N, d))
z
Out[4]:
In [5]:
x = np.dot(z,M)
x
Out[5]:
In [6]:
C1 = np.cov(x, rowvar=0, bias=1)
C1, C, sort(eigvalsh(C1))[::-1],sort(eigvalsh(C))[::-1]
Out[6]:
(c) Stefan Loesch / oditorium 2014; all rights reserved (license)
In [7]:
import sys
print(sys.version)