-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmvnrnd_fast.asv
157 lines (145 loc) · 5.55 KB
/
mvnrnd_fast.asv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
function [r] = mvnrnd_fast(mu,sigma)
%MVNRND Random vectors from the multivariate normal distribution.
% R = MVNRND(MU,SIGMA) returns an N-by-D matrix R of random vectors
% chosen from the multivariate normal distribution with mean vector MU,
% and covariance matrix SIGMA. MU is an N-by-D matrix, and MVNRND
% generates each row of R using the corresponding row of MU. SIGMA is a
% D-by-D symmetric positive semi-definite matrix, or a D-by-D-by-N array.
% If SIGMA is an array, MVNRND generates each row of R using the
% corresponding page of SIGMA, i.e., MVNRND computes R(I,:) using MU(I,:)
% and SIGMA(:,:,I). If the covariance matrix is diagonal, containing
% variances along the diagonal and zero covariances off the diagonal,
% SIGMA may also be specified as a 1-by-D matrix or a 1-by-D-by-N array,
% containing just the diagonal. If MU is a 1-by-D vector, MVNRND
% replicates it to match the trailing dimension of SIGMA.
%
% R = MVNRND(MU,SIGMA,N) returns a N-by-D matrix R of random vectors
% chosen from the multivariate normal distribution with 1-by-D mean
% vector MU, and D-by-D covariance matrix SIGMA.
%
% Example:
%
% mu = [1 -1]; Sigma = [.9 .4; .4 .3];
% r = mvnrnd(mu, Sigma, 500);
% plot(r(:,1),r(:,2),'.');
%
% See also MVTRND, MVNPDF, MVNCDF, NORMRND.
% R = MVNRND(MU,SIGMA,N,T) supplies the Cholesky factor T of
% SIGMA, so that SIGMA(:,:,J) == T(:,:,J)'*T(:,:,J) if SIGMA is a 3D array or SIGMA
% == T'*T if SIGMA is a matrix. No error checking is done on T.
%
% [R,T] = MVNRND(...) returns the Cholesky factor T, so it can be
% re-used to make later calls more efficient, although there are greater
% efficiency gains when SIGMA can be specified as a diagonal instead.
% Copyright 1993-2020 The MathWorks, Inc.
[n,d] = size(mu);
sz = size(sigma);
sz(1) = sz(2);
sigmaIsDiag = true;
% Special case: if mu is a column vector, then use sigma to try
% to interpret it as a row vector.
if d == 1 && sz(1) == n
mu = mu';
[n,d] = size(mu);
end
% Get size of data.
if nargin < 3 || isempty(cases)
nocases = true; % cases not supplied
else
nocases = false; % cases was supplied
if n == cases
% mu is ok
elseif n == 1 % mu is a single row, make cases copies
n = cases;
mu = repmat(mu,n,1);
else
error(message('stats:mvnrnd:InputSizeMismatchMu'));
end
end
outtype = internal.stats.dominantType(mu, sigma); % single if mu or sigma is
% Single covariance matrix
if ismatrix(sigma)
% Make sure sigma is the right size
if sz(1) ~= sz(2)
error(message('stats:mvnrnd:BadCovariance2DSize'));
elseif ~isequal(sz, [d d])
error(message('stats:mvnrnd:InputSizeMismatch2DSigma'));
end
% Just the diagonal of sigma has been specified.
t = sqrt(sigma);
r = randn(n,d,'like',outtype).*t + mu;
r(:,t==0) = mu(:,t==0); % force exact mean when variance is 0
else
% Factor sigma using a function that will perform a Cholesky-like
% factorization as long as the sigma matrix is positive
% semi-definite (can have perfect correlation). Cholesky requires a
% positive definite matrix. sigma == T'*T
[T,err] = cholcov(sigma);
if isnan(err)
error(message('stats:mvnrnd:BadCovariance2DSym'));
elseif err ~= 0
error(message('stats:mvnrnd:BadCovariance2DPos'));
end
r = randn(n,size(T,1),'like',outtype) * T + mu;
t = diag(sigma);
r(:,t==0) = mu(:,t==0); % force exact mean when variance is 0
end
% Multiple covariance matrices
elseif ndims(sigma) == 3
% mu is a single row and cases not given, rep mu out to match sigma
if n == 1 && nocases % already know size(sigma,3) > 1
n = sz(3);
mu = repmat(mu,n,1);
end
% Make sure sigma is the right size
if sz(1) ~= sz(2) % Sigma is 3-D
error(message('stats:mvnrnd:BadCovariance3DSize'));
elseif (sz(1) ~= d) || (sz(2) ~= d) % Sigma is 3-D
error(message('stats:mvnrnd:InputSizeMismatch3DSigma'));
elseif sz(3) ~= n
error(message('stats:mvnrnd:InputSizeMismatchSigmaDimension'));
end
if nargin < 4
if nargout > 1
T = zeros(sz,'like',outtype);
end
if sigmaIsDiag
sigma = reshape(sigma,sz(2),sz(3))';
if any(any(sigma<0))
error(message('stats:mvnrnd:BadDiagSigma'));
end
R = sqrt(sigma);
r = randn(n,d,'like',outtype).*R + mu;
if nargout > 1
for i=1:n
t = R(i,:);
T(:,:,i) = diag(t);
r(i,t==0) = mu(i,t==0); % force exact mean
end
end
else
r = zeros(n,d,'like',outtype);
for i = 1:n
[R,err] = cholcov(sigma(:,:,i));
if isnan(err)
error(message('stats:mvnrnd:BadCovariance3DSym'));
elseif err ~= 0
error(message('stats:mvnrnd:BadCovariance3DPos'));
end
Rrows = size(R,1);
r(i,:) = randn(1,Rrows,'like',outtype) * R + mu(i,:);
t = diag(sigma(:,:,i));
r(i,t==0) = mu(i,t==0); % force exact mean
if nargout > 1
T(1:Rrows,:,i) = R;
end
end
end
else
% T specified
r = zeros(n,d,'like',outtype);
for i = 1:n
r(i,:) = randn(1,d,'like',outtype) * T(:,:,i) + mu(i,:);
end
end
end