| Title: | Randomized Matrix Computations from Few Entries and Products |
|---|---|
| Description: | Implements recent randomized algorithms that answer questions about a large positive-semidefinite matrix while touching only a small part of it. Randomly pivoted Cholesky builds a low-rank approximation from a few rows of a kernel matrix (Chen, Epperly, Tropp and Webber (2025) <doi:10.1002/cpa.22234>), with an accelerated variant based on rejection sampling (Epperly, Tropp and Webber (2025) <doi:10.1137/24m1699048>). The XTrace, XNysTrace and XDiag estimators recover the trace and diagonal of a matrix that is available only through matrix-vector products (Epperly, Tropp and Webber (2024) <doi:10.1137/23m1548323>), alongside the Hutch++ estimator of Meyer, Musco, Musco and Woodruff (2021) <doi:10.1137/1.9781611976496.16>. Randomized Nystrom preconditioning speeds up the conjugate gradient method for regularized linear systems (Frangella, Tropp and Udell (2023) <doi:10.1137/21m1466244>). These pieces are combined to fit restricted maximum likelihood variance-component models on genomic relationship matrices without forming or factorizing the covariance matrix. |
| Authors: | Muhammad Farooqi [aut, cre] (ORCID: <https://orcid.org/0000-0003-4918-9791>) |
| Maintainer: | Muhammad Farooqi <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-09-21 21:19:34 UTC |
| Source: | https://github.com/cran/matsketch |
Estimates the diagonal of a matrix that is available only through
products , using the XDiag estimator, which applies the same
low-rank-plus-correction and leave-one-out ideas as trace_est().
diag_est(A, m, n = NULL, adjoint = NULL)diag_est(A, m, n = NULL, adjoint = NULL)
A |
A square matrix, a function computing |
m |
Number of matrix-vector products to spend. |
n |
Dimension of |
adjoint |
For a non-symmetric |
A numeric vector of length .
Epperly, E. N., Tropp, J. A. & Webber, R. J. (2024) XTrace: making the most of every sample in stochastic trace estimation. SIAM Journal on Matrix Analysis and Applications 45, 1-23. doi:10.1137/23m1548323
set.seed(1) U <- qr.Q(qr(matrix(rnorm(200 * 200), 200))) A <- U %*% diag((1:200)^-1.5) %*% t(U) est <- diag_est(A, m = 60) cor(est, diag(A))set.seed(1) U <- qr.Q(qr(matrix(rnorm(200 * 200), 200))) A <- U %*% diag((1:200)^-1.5) %*% t(U) est <- diag_est(A, m = 60) cor(est, diag(A))
Estimates from the eigenvalues of a low-rank approximation, and the rank
that Frangella, Tropp and
Udell show is enough for a well-conditioned preconditioner.
effective_dim(approx, mu)effective_dim(approx, mu)
approx |
|
mu |
Positive regularization parameter. |
The estimate uses only the retained eigenvalues, so it can only
understate the true effective dimension. If the recommended rank exceeds
the rank of approx, build a larger approximation and ask again.
A list with d_eff, recommended_rank, and sufficient, which
is TRUE when approx already has at least the recommended rank.
set.seed(1) X <- matrix(rnorm(1000), ncol = 2) effective_dim(nystrom(kernel_matrix(X), l = 60), mu = 1e-2)set.seed(1) X <- matrix(rnorm(1000), ncol = 2) effective_dim(nystrom(kernel_matrix(X), l = 60), mu = 1e-2)
Describes the genomic relationship matrix of VanRaden (2008),
where holds the marker genotypes centred by twice the allele
frequencies , without forming it. A product with costs
two products with , and rows are computed on demand, so the
functions in this package can work with while only the
genotypes are held in memory.
grm_matrix(M, freq = NULL)grm_matrix(M, freq = NULL)
M |
Genotype matrix with one row per individual and one column per marker, coded as allele counts between 0 and 2. Missing genotypes must be imputed first. |
freq |
Allele frequencies used for centring. Defaults to the observed
frequencies, |
This matters once is large: for 50,000 individuals the full matrix
takes 20 GB, while the genotypes on a 10,000-marker panel take 4 GB.
An object of class matsketch_grm, accepted wherever matsketch
expects a matrix. as.matrix() forms the full matrix.
VanRaden, P. M. (2008) Efficient methods to compute genomic predictions. Journal of Dairy Science 91, 4414-4423. doi:10.3168/jds.2007-0980
set.seed(1) M <- matrix(rbinom(200 * 500, 2, 0.3), 200) G <- grm_matrix(M) G G$block(1:3, 1:3)set.seed(1) M <- matrix(rbinom(200 * 500, 2, 0.3), 200) G <- grm_matrix(M) G G$block(1:3, 1:3)
Describes the kernel matrix with entries without
computing it. Randomly pivoted Cholesky then evaluates only the entries it
needs, roughly of the , which is what makes it
practical when the full kernel matrix would not fit in memory.
kernel_matrix( X, kernel = c("gaussian", "laplace", "matern32", "matern52"), bandwidth = NULL, block = 1000L )kernel_matrix( X, kernel = c("gaussian", "laplace", "matern32", "matern52"), bandwidth = NULL, block = 1000L )
X |
Numeric matrix with one row per point. |
kernel |
Kernel family: |
bandwidth |
Length scale |
block |
Number of rows computed at a time when multiplying by the whole matrix, which bounds memory use. |
Each product with the whole matrix recomputes the kernel, block by block.
For methods that multiply many times, such as pcg(), it is faster to
form the matrix once with as.matrix() whenever it fits in memory.
An object of class matsketch_kernel, accepted wherever
matsketch expects a matrix. as.matrix() forms the full matrix.
X <- matrix(rnorm(400), ncol = 2) K <- kernel_matrix(X, "gaussian") K K$block(1:3, 1:3)X <- matrix(rnorm(400), ncol = 2) K <- kernel_matrix(X, "gaussian") K K$block(1:3, 1:3)
Computes a rank- approximation of a positive-semidefinite matrix from products with a
random test matrix. A tiny shift keeps the Cholesky step stable and is
removed from the eigenvalues afterwards.
nystrom(A, l, n = NULL)nystrom(A, l, n = NULL)
A |
A positive-semidefinite matrix, a function computing |
l |
Rank of the approximation, which is also the number of products used. |
n |
Dimension of |
An object of class nystrom with the orthonormal eigenvectors U
and eigenvalues values, in decreasing order.
Frangella, Z., Tropp, J. A. & Udell, M. (2023) Randomized Nystrom preconditioning. SIAM Journal on Matrix Analysis and Applications 44, 718-752. doi:10.1137/21m1466244
set.seed(1) X <- matrix(rnorm(600), ncol = 3) nys <- nystrom(kernel_matrix(X), l = 30) head(nys$values)set.seed(1) X <- matrix(rnorm(600), ncol = 3) nys <- nystrom(kernel_matrix(X), l = 30) head(nys$values)
Builds the preconditioner for the regularized system
from a low-rank approximation
:
where is the smallest retained eigenvalue.
nystrom_precond(approx, mu)nystrom_precond(approx, mu)
approx |
|
mu |
Positive regularization parameter. |
The preconditioned system has a small condition number once the rank
reaches about the effective dimension
: Frangella,
Tropp and Udell show that a rank of keeps the expected condition number below 28, whatever
the size of the matrix. effective_dim() estimates that rank.
The approximation can come from nystrom() or from rpchol(). Because
only enters the formula after the approximation is built, one
approximation serves any number of values of , which is what
makes it cheap to re-use inside an iterative fit.
An object of class nystrom_precond, to pass to pcg().
Frangella, Z., Tropp, J. A. & Udell, M. (2023) Randomized Nystrom preconditioning. SIAM Journal on Matrix Analysis and Applications 44, 718-752. doi:10.1137/21m1466244
set.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- kernel_matrix(X) pre <- nystrom_precond(nystrom(K, l = 40), mu = 1e-3) preset.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- kernel_matrix(X) pre <- nystrom_precond(nystrom(K, l = 40), mu = 1e-3) pre
Solves for a positive-semidefinite using
only products with . Without a preconditioner this is the ordinary
conjugate gradient method, whose iteration count grows with the condition
number; with a preconditioner from nystrom_precond() the count stays
small and nearly independent of the size of the problem.
pcg(A, b, mu = 0, precond = NULL, tol = 1e-08, maxit = 1000L, x0 = NULL)pcg(A, b, mu = 0, precond = NULL, tol = 1e-08, maxit = 1000L, x0 = NULL)
A |
A positive-semidefinite matrix, a function computing |
b |
Right-hand side: a vector, or a matrix with one system per column. |
mu |
Non-negative regularization parameter. |
precond |
|
tol |
Stop each system when its residual norm falls below |
maxit |
Maximum number of iterations. |
x0 |
Optional starting value, the same shape as |
When b is a matrix, each column is solved as a separate system, but all
of them advance together, so every step multiplies by a block of
vectors. That is much faster in R than solving the columns one at a time.
An object of class pcg_result with the solution x (the same
shape as b), the iterations and whether each system converged,
and the relative residuals after each iteration.
Frangella, Z., Tropp, J. A. & Udell, M. (2023) Randomized Nystrom preconditioning. SIAM Journal on Matrix Analysis and Applications 44, 718-752. doi:10.1137/21m1466244
set.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- as.matrix(kernel_matrix(X)) b <- rnorm(500) plain <- pcg(K, b, mu = 1e-3) pre <- nystrom_precond(rpchol(K, k = 60), mu = 1e-3) fast <- pcg(K, b, mu = 1e-3, precond = pre) c(plain = plain$iterations, preconditioned = fast$iterations)set.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- as.matrix(kernel_matrix(X)) b <- rnorm(500) plain <- pcg(K, b, mu = 1e-3) pre <- nystrom_precond(rpchol(K, k = 60), mu = 1e-3) fast <- pcg(K, b, mu = 1e-3, precond = pre) c(plain = plain$iterations, preconditioned = fast$iterations)
Draws the relative residual after each iteration on a logarithmic scale, one line per system solved.
## S3 method for class 'pcg_result' plot(x, tol = NULL, ...)## S3 method for class 'pcg_result' plot(x, tol = NULL, ...)
x |
An object from |
tol |
Optional tolerance to mark with a horizontal line. |
... |
Passed to |
x, invisibly.
set.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- as.matrix(kernel_matrix(X)) plot(pcg(K, rnorm(500), mu = 1e-3), tol = 1e-8)set.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- as.matrix(kernel_matrix(X)) plot(pcg(K, rnorm(500), mu = 1e-3), tol = 1e-8)
Draws the genetic and residual variance estimates at each iteration, which shows whether the fit settled or was still moving when it stopped.
## S3 method for class 'reml_sketch' plot(x, ...)## S3 method for class 'reml_sketch' plot(x, ...)
x |
An object from |
... |
Passed to |
x, invisibly.
set.seed(1) dat <- sim_genomic(n = 200, p = 400, h2 = 0.5) plot(reml_exact(dat$y, dat$G))set.seed(1) dat <- sim_genomic(n = 200, p = 400, h2 = 0.5) plot(reml_exact(dat$y, dat$G))
Draws the relative trace error after each pivot on a logarithmic scale, which shows how quickly the approximation improves with its rank.
## S3 method for class 'rpchol' plot(x, ...)## S3 method for class 'rpchol' plot(x, ...)
x |
An object from |
... |
Passed to |
x, invisibly.
set.seed(1) X <- matrix(rnorm(1000), ncol = 2) plot(rpchol(kernel_matrix(X), k = 60))set.seed(1) X <- matrix(rnorm(1000), ncol = 2) plot(rpchol(kernel_matrix(X), k = 60))
Fits the same model as reml_sketch() with the same average-information
updates, but forms and factorizes and computes every trace
exactly. It is meant for checking reml_sketch() on problems small
enough to factorize.
reml_exact(y, G, X = NULL, start = NULL, tol = 1e-08, maxit = 100L)reml_exact(y, G, X = NULL, start = NULL, tol = 1e-08, maxit = 100L)
y |
Numeric response. |
G |
A positive-semidefinite relationship matrix, or a lazy matrix from
|
X |
Fixed-effect design matrix. Defaults to an intercept. |
start |
Starting values |
tol |
Stop when no variance component changes by more than this fraction between iterations. |
maxit |
Maximum number of REML iterations. |
An object of class reml_sketch whose approx is "exact".
Gilmour, A. R., Thompson, R. & Cullis, B. R. (1995) Average information REML: an efficient algorithm for variance parameter estimation in linear mixed models. Biometrics 51, 1440-1450. doi:10.2307/2533274
set.seed(1) dat <- sim_genomic(n = 300, p = 600, h2 = 0.5) reml_exact(dat$y, dat$G)set.seed(1) dat <- sim_genomic(n = 300, p = 600, h2 = 0.5) reml_exact(dat$y, dat$G)
Fits , with and
, by average-information REML, without
forming or factorizing the covariance matrix
.
reml_sketch( y, G, X = NULL, rank = 100L, m = 40L, approx = c("auto", "rpchol", "nystrom"), estimator = c("xtrace", "hutchinson"), start = NULL, tol = 1e-04, maxit = 50L, cg_tol = 1e-06 )reml_sketch( y, G, X = NULL, rank = 100L, m = 40L, approx = c("auto", "rpchol", "nystrom"), estimator = c("xtrace", "hutchinson"), start = NULL, tol = 1e-04, maxit = 50L, cg_tol = 1e-06 )
y |
Numeric response. |
G |
Relationship or kernel matrix: a positive-semidefinite matrix, a
lazy matrix from |
X |
Fixed-effect design matrix. Defaults to an intercept. |
rank |
Rank of the approximation of |
m |
Matrix-vector products per trace estimate. Each costs one linear solve at every iteration. |
approx |
How to approximate |
estimator |
Trace estimator, |
start |
Starting values |
tol |
Stop when no variance component changes by more than this fraction between iterations. |
maxit |
Maximum number of REML iterations. |
cg_tol |
Relative residual tolerance for each linear solve. |
Three ideas from the package do the work.
Solves. A system in is the system
with . It is solved by conjugate gradients, preconditioned with a
low-rank approximation of that is built once, by rpchol() or
nystrom(), and reused for every value of the fit visits. The
right-hand sides needed at each step are solved together, each starting
from its solution at the previous step.
One stochastic trace. The score needs , which is
estimated by XTrace or by Hutchinson's estimator. The test matrix is
drawn once and kept for the whole fit, so successive iterations see the
same sketch and converge smoothly rather than jittering.
One exact trace. is idempotent with rank
, so
holds exactly, and the second trace costs
nothing.
Here . The
average-information matrix needs only quadratic forms in , and these
are computed from solves rather than estimated.
Because the test matrix is fixed, the fit converges to the exact solution
of a slightly perturbed set of REML equations. The trace_se column of
history shows the size of the perturbation, which shrinks as m grows.
reml_exact() fits the same model by dense linear algebra, for checking
results on problems small enough to factorize.
An object of class reml_sketch with the variance components
sigma2, the heritability h2, their standard errors se, the
fixed effects beta, the iteration history, the number of linear
systems solved solves, and the total conjugate gradient iterations
cg_iterations they took.
Gilmour, A. R., Thompson, R. & Cullis, B. R. (1995) Average information REML: an efficient algorithm for variance parameter estimation in linear mixed models. Biometrics 51, 1440-1450. doi:10.2307/2533274
Bermann, M., Legarra, A., Aguilar, I., Alvarez-Munera, A., Misztal, I. & Lourenco, D. (2025) Estimation of (co)variance components for very large datasets and complex single-step genomic models. Genetics Selection Evolution 57. doi:10.1186/s12711-025-01006-9
reml_exact(), grm_matrix(), sim_genomic()
set.seed(1) dat <- sim_genomic(n = 300, p = 600, h2 = 0.5) fit <- reml_sketch(dat$y, dat$G, rank = 60, m = 30) fit # the relationship matrix need never be formed reml_sketch(dat$y, grm_matrix(dat$M), rank = 60, m = 30)$h2set.seed(1) dat <- sim_genomic(n = 300, p = 600, h2 = 0.5) fit <- reml_sketch(dat$y, dat$G, rank = 60, m = 30) fit # the relationship matrix need never be formed reml_sketch(dat$y, grm_matrix(dat$M), rank = 60, m = 30)$h2
Builds a rank- approximation of a
positive-semidefinite matrix by choosing pivot columns at random,
each with probability proportional to the diagonal of the part of
not yet explained.
rpchol( A, k, method = c("accelerated", "simple", "greedy", "uniform"), block = NULL, tol = 0 )rpchol( A, k, method = c("accelerated", "simple", "greedy", "uniform"), block = NULL, tol = 0 )
A |
A symmetric positive-semidefinite matrix, or a lazy matrix from
|
k |
Target rank. |
method |
Pivoting rule; see Details. |
block |
Proposals per round for the accelerated method. Defaults to
|
tol |
Stop early once the unexplained trace falls below |
Sampling in proportion to the residual diagonal is what separates the
method from its predecessors. Greedy pivoting always takes the largest
residual and can fixate on outliers; uniform sampling ignores where the
matrix actually has mass. Randomly pivoted Cholesky balances the two, and
reaches near-optimal approximations while reading only about
entries of the matrix, so it never needs in full.
Four pivoting rules are available:
"accelerated" (default) proposes a block of pivots at once and accepts
each by rejection sampling. Its output has the same distribution as
"simple", but most of its work is done in block operations, which is
much faster when is large.
"simple" draws one pivot at a time in proportion to the residual
diagonal.
"greedy" always takes the largest residual diagonal, which is the
classical pivoted partial Cholesky decomposition.
"uniform" takes pivots uniformly at random, which gives the
column-sampling Nystrom approximation.
The last two are included as baselines, so that the gain from random pivoting can be measured on the problem at hand.
The relative trace error reported is exact, not estimated: the residual
is positive semidefinite, so its trace norm is the sum of
the residual diagonal the algorithm maintains anyway.
An object of class rpchol containing the factor F, whose number
of columns is the rank achieved; the chosen pivots; the relative trace
error trace_error, and trace_path, its value after each pivot; and
entries, the number of matrix entries read.
Chen, Y., Epperly, E. N., Tropp, J. A. & Webber, R. J. (2025) Randomly pivoted Cholesky: practical approximation of a kernel matrix with few entry evaluations. Communications on Pure and Applied Mathematics 78, 995-1041. doi:10.1002/cpa.22234
Epperly, E. N., Tropp, J. A. & Webber, R. J. (2025) Embrace rejection: kernel matrix approximation by accelerated randomly pivoted Cholesky. SIAM Journal on Matrix Analysis and Applications 46, 2527-2557. doi:10.1137/24m1699048
kernel_matrix(), nystrom_precond()
set.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- kernel_matrix(X) fit <- rpchol(K, k = 40) fit # the same budget spent on greedy or uniform pivots rpchol(K, k = 40, method = "greedy")$trace_error rpchol(K, k = 40, method = "uniform")$trace_errorset.seed(1) X <- matrix(rnorm(1000), ncol = 2) K <- kernel_matrix(X) fit <- rpchol(K, k = 40) fit # the same budget spent on greedy or uniform pivots rpchol(K, k = 40, method = "greedy")$trace_error rpchol(K, k = 40, method = "uniform")$trace_error
Draws biallelic marker genotypes, forms the genomic relationship matrix of
VanRaden (2008), and simulates a trait with the requested heritability, so
that reml_sketch() can be checked against known variance components.
sim_genomic( n = 1000L, p = 2000L, h2 = 0.5, pops = 1L, fst = 0.05, form_G = TRUE )sim_genomic( n = 1000L, p = 2000L, h2 = 0.5, pops = 1L, fst = 0.05, form_G = TRUE )
n |
Number of individuals. |
p |
Number of markers. |
h2 |
Heritability, between 0 and 1. |
pops |
Number of subpopulations. |
fst |
Fixation index between subpopulations, used when |
form_G |
Return the relationship matrix itself. Set to |
With pops > 1 the individuals come from that many subpopulations whose
allele frequencies have drifted apart under the Balding-Nichols model with
fixation index fst. Population structure gives the relationship matrix a
few large eigenvalues, as real breeding and human cohorts do, and those are
exactly what a low-rank preconditioner captures.
Genetic values are sums of marker effects on the centred genotypes, with
variance chosen so that the genetic and residual variance components on the
scale of the relationship matrix are h2 and 1 - h2.
The function draws random numbers but does not set the seed; call
base::set.seed() first for a reproducible data set.
A list with the phenotype y, an intercept design matrix X, the
genotype matrix M, the relationship matrix G (when form_G = TRUE),
the subpopulation of each individual pop, and the true h2.
VanRaden, P. M. (2008) Efficient methods to compute genomic predictions. Journal of Dairy Science 91, 4414-4423. doi:10.3168/jds.2007-0980
Balding, D. J. & Nichols, R. A. (1995) A method for quantifying differentiation between populations at multi-allelic loci and its implications for investigating identity and paternity. Genetica 96, 3-12. doi:10.1007/bf01441146
set.seed(1) dat <- sim_genomic(n = 200, p = 500, h2 = 0.4, pops = 3) dim(dat$G) table(dat$pop)set.seed(1) dat <- sim_genomic(n = 200, p = 500, h2 = 0.4, pops = 3) dim(dat$G) table(dat$pop)
Estimates for a matrix that is available only through
products , spending about m such products.
trace_est( A, m, method = c("xtrace", "xnystrace", "hutchpp", "hutchinson"), n = NULL )trace_est( A, m, method = c("xtrace", "xnystrace", "hutchpp", "hutchinson"), n = NULL )
A |
A square matrix, a function computing |
m |
Number of matrix-vector products to spend. |
method |
Estimator; see Details. |
n |
Dimension of |
"xtrace" (default) combines a low-rank approximation of with a
correction for what it misses, and uses every product twice through a
leave-one-out construction. It is typically far more accurate than the
older estimators at the same cost, and it reports its own standard
error. It works for any square matrix.
"xnystrace" is the counterpart for positive-semidefinite matrices. It
uses a Nystrom approximation, which lets it spend all m products on a
single sketch.
"hutchpp" is Hutch++, the estimator XTrace improves on.
"hutchinson" is the classical Girard-Hutchinson estimator, the average
of over random sign vectors. It is included as
the baseline the others are measured against.
An object of class trace_est holding the estimate, its
std_error (not available for Hutch++), the method, and the number of
products actually used, matvecs.
Epperly, E. N., Tropp, J. A. & Webber, R. J. (2024) XTrace: making the most of every sample in stochastic trace estimation. SIAM Journal on Matrix Analysis and Applications 45, 1-23. doi:10.1137/23m1548323
Meyer, R. A., Musco, C., Musco, C. & Woodruff, D. P. (2021) Hutch++: optimal stochastic trace estimation. Symposium on Simplicity in Algorithms, 142-155. doi:10.1137/1.9781611976496.16
Hutchinson, M. F. (1989) A stochastic estimator of the trace of the influence matrix for Laplacian smoothing splines. Communications in Statistics - Simulation and Computation 18, 1059-1076. doi:10.1080/03610918908812806
set.seed(1) U <- qr.Q(qr(matrix(rnorm(300 * 300), 300))) A <- U %*% diag((1:300)^-2) %*% t(U) sum(diag(A)) trace_est(A, m = 40) trace_est(A, m = 40, method = "hutchinson")set.seed(1) U <- qr.Q(qr(matrix(rnorm(300 * 300), 300))) A <- U %*% diag((1:300)^-2) %*% t(U) sum(diag(A)) trace_est(A, m = 40) trace_est(A, m = 40, method = "hutchinson")