|
13 | 13 | #' @param K An integer (default = 3) specifying the number of groups. |
14 | 14 | #' Ignored if \code{block.sizes} is provided; then \code{K <- length(block.sizes)}. |
15 | 15 | #' |
16 | | -#' @param prob.mat A \eqn{K}-by-\eqn{K} symmetric matrix (default = \code{NULL}) |
17 | | -#' specifying the Bernoulli rates. Element (i,j) gives the probability of |
18 | | -#' creating an edge between vertices from groups i and j. If \code{NULL}, |
19 | | -#' a matrix with \code{within.prob} on the diagonal and \code{between.prob} |
20 | | -#' off-diagonal is used. |
| 16 | +#' @param prob.mat A \eqn{K \times K} symmetric matrix (default = \code{NULL}) |
| 17 | +#' specifying the Bernoulli rates. Element \eqn{(i, j)} gives the probability of |
| 18 | +#' creating an edge between vertices from groups \eqn{i} and \eqn{j}. |
| 19 | +#' If \code{NULL}, a matrix with \code{within.prob} on the diagonal and |
| 20 | +#' \code{between.prob} on the off-diagonal is used. |
21 | 21 | #' |
22 | | -#' @param within.prob A scalar in [0,1] (default = 0.25) specifying |
| 22 | +#' @param within.prob A numeric value in [0, 1] (default = 0.25) specifying |
23 | 23 | #' the probability of creating an edge between vertices within the same group. |
24 | 24 | #' This argument is used only when \code{prob.mat = NULL}. |
25 | 25 | #' |
26 | | -#' @param between.prob A scalar in [0,1] (default = 0.05) specifying |
| 26 | +#' @param between.prob A numeric value in [0, 1] (default = 0.05) specifying |
27 | 27 | #' the probability of creating an edge between vertices from different groups. |
28 | 28 | #' This argument is used only when \code{prob.mat = NULL}. |
29 | 29 | #' |
30 | | -#' @param weight.mat A \eqn{d}-by-\eqn{d} symmetric matrix (default = \code{NULL}) |
| 30 | +#' @param weight.mat A \eqn{d \times d} symmetric matrix (default = \code{NULL}) |
31 | 31 | #' specifying the edge weights. If \code{NULL}, weights are generated block-wise |
32 | 32 | #' according to \code{weight.dists} and \code{weight.paras}. |
33 | 33 | #' |
|
39 | 39 | #' \item length = 2: First for within-group blocks, second for between-group |
40 | 40 | #' blocks. |
41 | 41 | #' \item length = \eqn{K + K(K-1)/2}: Full specification for each block. |
42 | | -#' The first \eqn{K} elements correspond to within-group blocks with indices 1, |
43 | | -#' \dots, K, and the remaining \eqn{K(K-1)/2} elements correspond to |
44 | | -#' between-group blocks ordered as (1,2), (1,3), \dots, (1,K), (2,3), \dots, |
45 | | -#' (K-1,K). |
| 42 | +#' The first \eqn{K} elements correspond to within-group blocks with indices |
| 43 | +#' \eqn{1, \dots, K}, and the remaining \eqn{K(K-1)/2} elements correspond to |
| 44 | +#' between-group blocks ordered as \eqn{(1,2)}, \eqn{(1,3)}, \eqn{(1,4)}, \dots, |
| 45 | +#' \eqn{(1,K)}, \eqn{(2,3)}, \dots, \eqn{(K-1,K)}. |
46 | 46 | #' } |
47 | 47 | #' Each element of \code{weight.dists} can be: |
48 | 48 | #' \enumerate{ |
49 | | -#' \item A string specifying the distribution family. Accepted distributions |
50 | | -#' (base R samplers in parentheses) include: \itemize{ |
| 49 | +#' \item A user-supplied sampling function. The function must accept an argument |
| 50 | +#' \code{n} specifying the number of samples. |
| 51 | +#' \item A character string specifying the distribution family. |
| 52 | +#' Accepted distributions (base R samplers in parentheses) include: |
| 53 | +#' \itemize{ |
51 | 54 | #' \item "beta": Beta distribution (\code{\link[stats]{rbeta}}) |
52 | 55 | #' \item "cauchy": Cauchy distribution (\code{\link[stats]{rcauchy}}). |
53 | 56 | #' \item "chisq": Chi-squared distribution (\code{\link[stats]{rchisq}}). |
|
60 | 63 | #' \item "unif": Uniform distribution (\code{\link[stats]{runif}}). |
61 | 64 | #' \item "weibull": Weibull distribution (\code{\link[stats]{rweibull}}). |
62 | 65 | #' } |
63 | | -#' \item A user-supplied function used for sampling. The function must accept |
64 | | -#' an argument \code{n} specifying the number of samples. |
65 | 66 | #' } |
66 | 67 | #' |
67 | | -#' @param weight.paras A list (default = |
68 | | -#' \code{list(c(shape = 1e4, rate = 1e2), c(min = 0, max = 5))}) specifying |
69 | | -#' the parameters associated with \code{weight.dists}. It must follow the same |
70 | | -#' length rules as \code{weight.dists}. Each element should be a named vector |
71 | | -#' or list suitable for the corresponding sampler. |
| 68 | +#' @param weight.paras A list |
| 69 | +#' (default = \code{list(c(shape = 1e4, rate = 1e2), c(min = 0, max = 5))}) |
| 70 | +#' specifying the parameters associated with \code{weight.dists}. It must follow |
| 71 | +#' the same length rules as \code{weight.dists}. Each element should be a named |
| 72 | +#' vector or list suitable for the corresponding sampler. |
72 | 73 | #' |
73 | | -#' @param cond.target A scalar (default = 100) specifying the target condition |
74 | | -#' number for the precision matrix. A diagonal shift is applied so that |
75 | | -#' the smallest eigenvalue satisfies |
76 | | -#' \eqn{\lambda_{\min} \geq \lambda_{\max}/\code{cond.target}}, |
77 | | -#' ensuring both positive definiteness and numerical stability. |
| 74 | +#' @param cond.target A numeric value > 1 (default = 100) specifying the target |
| 75 | +#' condition number for the precision matrix. When necessary, a diagonal shift |
| 76 | +#' is applied to ensure positive definiteness and numerical stability. |
| 77 | +#' |
| 78 | +#' @return |
| 79 | +#' An object with S3 class "gen_prec_sbm" containing the following components: |
| 80 | +#' \describe{ |
| 81 | +#' \item{Omega}{The precision matrix with SBM block structure.} |
| 82 | +#' \item{Sigma}{The covariance matrix, i.e., the inverse of \code{Omega}.} |
| 83 | +#' \item{sparsity}{Proportion of zero entries in \code{Omega}.} |
| 84 | +#' \item{membership}{An integer vector specifying the group membership.} |
| 85 | +#' } |
78 | 86 | #' |
79 | 87 | #' @details |
80 | 88 | #' \strong{Edge sampling.} |
|
91 | 99 | #' \item length = 1: Same specification for all blocks. |
92 | 100 | #' \item length = 2: first for within-group blocks, second for between-group |
93 | 101 | #' blocks. |
94 | | -#' \item length = \eqn{K + K(K - 1)/2}: Full specification for each block. |
| 102 | +#' \item length = \eqn{K + K(K-1)/2}: Full specification for each block. |
95 | 103 | #' } |
96 | 104 | #' |
97 | 105 | #' \strong{Block indexing.} |
98 | 106 | #' The order for blocks is: |
99 | 107 | #' \itemize{ |
100 | | -#' \item Within-group blocks: Indices 1, \dots, K. |
101 | | -#' \item Between-group blocks: \eqn{K(K-1)/2} blocks in order (1,2), (1,3), |
102 | | -#' \dots, (1,K), (2,3), \dots, (K-1,K). |
| 108 | +#' \item Within-group blocks: Indices \eqn{1, \dots, K}. |
| 109 | +#' \item Between-group blocks: \eqn{K(K-1)/2} blocks in order \eqn{(1,2)}, |
| 110 | +#' \eqn{(1,3)}, \eqn{(1,4)}, \dots, \eqn{(1,K)}, \eqn{(2,3)}, \dots, \eqn{(K-1,K)}. |
103 | 111 | #' } |
104 | 112 | #' |
105 | 113 | #' \strong{Positive definiteness.} |
106 | 114 | #' The weighted adjacency matrix is symmetrized and used as the precision matrix |
107 | 115 | #' \eqn{\Omega_0}. Since arbitrary block-structured weights may not be positive |
108 | 116 | #' definite, a diagonal adjustment is applied to control the eigenvalue spectrum. |
109 | 117 | #' Specifically, let \eqn{\lambda_{\max}} and \eqn{\lambda_{\min}} denote |
110 | | -#' the largest and smallest eigenvalues of the initial matrix. A scalar |
111 | | -#' \eqn{\tau} is added to the diagonal so that |
112 | | -#' \deqn{\lambda_{\min}(\Omega_0 + \tau I) \;\geq\; |
113 | | -#' \lambda_{\max} / \code{cond.target}, |
114 | | -#' } |
115 | | -#' which ensures both positive definiteness and that the condition number |
116 | | -#' does not exceed \code{cond.target}. This guarantees numerical stability even |
117 | | -#' in high-dimensional settings. |
118 | | -#' |
119 | | -#' @importFrom igraph as_adjacency_matrix sample_sbm |
120 | | -#' |
121 | | -#' @return |
122 | | -#' An object with S3 class "grasps" containing the following components: |
123 | | -#' \describe{ |
124 | | -#' \item{Omega}{The precision matrix with SBM block structure.} |
125 | | -#' \item{Sigma}{The covariance matrix, i.e., the inverse of \code{Omega}.} |
126 | | -#' \item{sparsity}{Proportion of zero entries in \code{Omega}.} |
127 | | -#' \item{membership}{An integer vector specifying the group membership.} |
| 118 | +#' the largest and smallest eigenvalues of a matrix. A non-negative numeric |
| 119 | +#' value \eqn{\tau} is added to the diagonal so that |
| 120 | +#' \deqn{ |
| 121 | +#' \left\{ |
| 122 | +#' \begin{array}{l} |
| 123 | +#' \dfrac{\lambda_{\max}(\Omega_0 + \tau I)}{\lambda_{\min}(\Omega_0 + \tau I)} |
| 124 | +#' \leq \texttt{cond.target} \\[1em] |
| 125 | +#' \lambda_{\min}(\Omega_0 + \tau I) > 0 \\[.5em] |
| 126 | +#' \tau \geq 0 |
| 127 | +#' \end{array} |
| 128 | +#' \right. |
128 | 129 | #' } |
| 130 | +#' which ensures both positive definiteness and guarantees that the condition |
| 131 | +#' number does not exceed \code{cond.target}, providing numerical stability |
| 132 | +#' even in high-dimensional settings. |
129 | 133 | #' |
130 | 134 | #' @example |
131 | 135 | #' inst/example/ex-gen_prec_sbm.R |
132 | 136 | #' |
| 137 | +#' @importFrom igraph as_adjacency_matrix sample_sbm |
| 138 | +#' |
133 | 139 | #' @export |
134 | 140 |
|
135 | 141 | gen_prec_sbm <- function(d, |
@@ -212,15 +218,15 @@ gen_prec_sbm <- function(d, |
212 | 218 | Omega <- (Omega + t(Omega)) / 2 ## symmetric; diag still 0 |
213 | 219 |
|
214 | 220 | ## ensure positive definiteness and control the condition number |
215 | | - ## add a scalar tau to the diagonal so that lambda_min(Omega + tau*I) >= target_min, |
216 | | - ## where target_min = lambda_max / cond.target |
217 | | - ## this guarantees the condition number satisfies kappa(Omega) <= cond.target |
218 | | - ## (shift all eigenvalues by the same tau) |
| 221 | + ## add a scalar tau to the diagonal so that |
| 222 | + ## lambda_max(Omega + tau*I) / lambda_min(Omega + tau*I) <= cond.target |
| 223 | + ## (diagonal loading shifts all eigenvalues by the same tau) |
219 | 224 | eigvals <- eigen(Omega, only.values = TRUE)$values |
220 | 225 | eigval_max <- max(eigvals) |
221 | 226 | eigval_min <- min(eigvals) |
222 | | - target_min <- eigval_max / cond.target |
223 | | - tau <- ifelse(eigval_min < target_min, target_min - eigval_min, 0) |
| 227 | + tau <- max(0, ## no adjustment |
| 228 | + -eigval_min, ## ensure positive definiteness |
| 229 | + (eigval_max - cond.target * eigval_min) / (cond.target - 1)) ## enforce kappa <= cond.target |
224 | 230 | diag(Omega) <- diag(Omega) + tau |
225 | 231 |
|
226 | 232 | ## covariance matrix |
|
0 commit comments