/* * R : A Computer Language for Statistical Data Analysis * Copyright (C) 2012-2022 The R Core Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, a copy is available at * https://www.R-project.org/Licenses/ */ #ifdef HAVE_CONFIG_H # include #endif #include #include // memset, memcpy #include #include #include #include /* for Lapack (dpotrf, etc.) and BLAS */ #include "stats.h" // for _() #include "statsR.h" /** * Simulate the Cholesky factor of a standardized Wishart variate with * dimension p and nu degrees of freedom. * * @param nu degrees of freedom * @param p dimension of the Wishart distribution * @param upper if 0 the result is lower triangular, otherwise upper triangular * @param ans array of size p * p to hold the result * * @return ans */ static double *std_rWishart_factor(double nu, int p, int upper, double ans[]) { int pp1 = p + 1; if (nu < (double) p || p <= 0) error(_("inconsistent degrees of freedom and dimension")); memset(ans, 0, p * p * sizeof(double)); for (int j = 0; j < p; j++) { /* jth column */ ans[j * pp1] = sqrt(rchisq(nu - (double) j)); for (int i = 0; i < j; i++) { int uind = i + j * p, /* upper triangle index */ lind = j + i * p; /* lower triangle index */ ans[(upper ? uind : lind)] = norm_rand(); ans[(upper ? lind : uind)] = 0; } } return ans; } /** * Simulate a sample of random matrices from a Wishart distribution * * @param ns Number of samples to generate * @param nuP Degrees of freedom * @param scal Positive-definite scale matrix * * @return */ SEXP rWishart(SEXP ns, SEXP nuP, SEXP scal) { SEXP ans; int *dims = INTEGER(getAttrib(scal, R_DimSymbol)), info, n = asInteger(ns), psqr; double *scCp, *ansp, *tmp, nu = asReal(nuP), one = 1, zero = 0; if (!isMatrix(scal) || !isReal(scal) || dims[0] != dims[1]) error(_("'scal' must be a square, real matrix")); if (n <= 0) n = 1; // allocate early to avoid memory leaks in R_Callocs below. PROTECT(ans = alloc3DArray(REALSXP, dims[0], dims[0], n)); psqr = dims[0] * dims[0]; tmp = R_Calloc(psqr, double); scCp = R_Calloc(psqr, double); Memcpy(scCp, REAL(scal), psqr); memset(tmp, 0, psqr * sizeof(double)); F77_CALL(dpotrf)("U", &(dims[0]), scCp, &(dims[0]), &info FCONE); // LAPACK if (info) error(_("'scal' matrix is not positive-definite")); ansp = REAL(ans); GetRNGstate(); for (int j = 0; j < n; j++) { double *ansj = ansp + j * psqr; std_rWishart_factor(nu, dims[0], 1, tmp); F77_CALL(dtrmm)("R", "U", "N", "N", dims, dims, &one, scCp, dims, tmp, dims FCONE FCONE FCONE FCONE); // BLAS F77_CALL(dsyrk)("U", "T", &(dims[1]), &(dims[1]), &one, tmp, &(dims[1]), &zero, ansj, &(dims[1]) FCONE FCONE); // BLAS for (int i = 1; i < dims[0]; i++) for (int k = 0; k < i; k++) ansj[i + k * dims[0]] = ansj[k + i * dims[0]]; } PutRNGstate(); R_Free(scCp); R_Free(tmp); UNPROTECT(1); return ans; }