ParaMonte MATLAB 3.0.0
Parallel Monte Carlo and Machine Learning Library
See the latest version documentation.
getRand.m
Go to the documentation of this file.
1%> \brief
2%> Generate and return a random positive-definite (correlation or covariance) matrix using the Gram method.<br>
3%>
4%> \details
5%> The Gram method of generating random positive-definite square matrices is based on the
6%> observation that every real positive definite matrix \f$M\f$ has a Cholesky factorization
7%> \f{equation}{
8%> M = LL*
9%> \f}
10%> where \f$L\f$ is a uniquely defined lower triangular matrix with positive diagonal entries.<br>
11%> Therefore, \f$M\f$ can be constructed from a given random \f$L\f$.<br>
12%> The **Gram method** is fast, however, the resulting matrix \f$M\f$ does not possess any particular structure.<br>
13%> because it uses the Cholesky factorization of the distribution covariance matrix.<br>
14%>
15%> \param[in] ndim : The input positive scalar MATLAB whole number(``integer``),
16%> representing the rank of the matrix (the number of dimensions) of shape ``(ndim, ndim)``.<br>
17%> (**optional**. It must be present **if and only if** the input ``scale`` argument is missing or is a scalar.)
18%> \param[in] scale : The input scalar or `contiguous` vector of size `ndim` of type ``real``, representing the scale
19%> of the matrix (e.g., the standard deviation of a covariance matrix) along each dimension.<br>
20%> (**optional**. default = ``1``. It can be present **if and only if** it is a scalar or is a vector of size ``ndim``.)
21%>
22%> \return
23%> `rand` : The output matrix of shape ``(1:ndim, 1:ndim)`` of type MATLAB ``double``,
24%> containing a random positive-definite matrix.<br>
25%> If the optional input scale is missing, then the output ``rand`` is a correlation matrix.<br>
26%>
27%> \interface{getRand}
28%> \code{.F90}
29%>
30%> rand(1:ndim, 1:ndim) = pm.stats.dist.cov.getRand(ndim)
31%> rand(1:ndim, 1:ndim) = pm.stats.dist.cov.getRand(ndim, scale(1:ndim))
32%>
33%> \endcode
34%>
35%> \warning
36%> The condition `all([0 < scale])` must hold for the corresponding input arguments.<br>
37%>
38%> \example{getRand}
39%> \include{lineno} example/stats/dist/cov/getRand/main.m
40%> \output{getRand}
41%> \include{lineno} example/stats/dist/cov/getRand/main.out.m
42%>
43%> \final{getRand}
44%>
45%> \author
46%> \FatemehBagheri, May 20 2024, 1:25 PM, NASA Goddard Space Flight Center (GSFC), Washington, D.C.<br>
47%> \AmirShahmoradi, July 6 2024, 7:07 PM, NASA Goddard Space Flight Center (GSFC), Washington, D.C.<br>
48function rand = getRand(ndim, scale)
49 rand = zeros(ndim, ndim);
50 for idim = 1 : ndim
51 while true
52 rand(1 : idim, idim) = unifrnd(-1, 1, idim, 1);
53 %%%% \todo
54 %%%% The following is redundant for the element and can be optimized.
55 normfac = sqrt(dot(rand(1 : idim, idim), rand(1 : idim, idim)));
56 if normfac ~= 0
57 break;
58 end
59 end
60 normfac = 1 / normfac;
61 rand(1 : idim, idim) = rand(1 : idim, idim) * normfac;
62 end
63 rand = transpose(rand) * rand;
64 %%%%
65 %%%% Set the diagonals to unity to create correlation matrix.
66 %%%% Perhaps not really essential as it is guaranteed to be 1, at least theoretically.
67 %%%%
68 %rand = rand - diag(diag(rand)) + eye(ndim, ndim);
69 %%%%
70 %%%% Rescale if requested.
71 %%%%
72 if 1 < nargin
73 if numel(scale) == 1
74 scalesq = scale^2;
75 rand = rand * scalesq;
76 else
77 for jdim = 1 : ndim
78 for idim = 1 : jdim - 1
79 rand(idim, jdim) = rand(idim, jdim) * scale(idim) * scale(jdim);
80 end
81 rand(jdim, jdim) = rand(jdim, jdim) * scale(jdim)^2;
82 rand(jdim, idim) = rand(idim, jdim);
83 end
84 end
85 end
86end
function getRand(in ndim, in scale)
Generate and return a random positive-definite (correlation or covariance) matrix using the Gram meth...