| Title: | Tile-Based Sparse Cholesky Factorization and Selected Inverse |
|---|---|
| Description: | Interface to the 'sTiles' framework for tile-based sparse Cholesky factorization: log-determinants, selected inverse (marginal variances) and triangular solves, with symbolic reuse so that repeated factorization of matrices sharing one sparsity pattern pays the ordering cost only once, as in a hyperparameter sweep. The compiled glue in this package resolves its symbols at run time against the 'sTiles' solver library ('libstiles'), which is a separate component distributed under its own terms and is not part of this package. Install it once with 'sTiles_install_library()', or point the package at a copy you already have with the 'STILES_LIB' environment variable. |
| Authors: | Esmail Abdul Fattah [aut, cre, cph] |
| Maintainer: | Esmail Abdul Fattah <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 2026.9.4 |
| Built: | 2026-09-15 14:12:33 UTC |
| Source: | https://github.com/cran/sTiles |
Print a sTiles factorization object
## S3 method for class 'sTiles' print(x, ...)## S3 method for class 'sTiles' print(x, ...)
x |
An "sTiles" object. |
... |
Ignored, present for consistency with [print()]. |
'x', invisibly.
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) print(sTiles(Q)) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) print(sTiles(Q)) }
Print a sTiles summary
## S3 method for class 'sTiles_summary' print(x, ...)## S3 method for class 'sTiles_summary' print(x, ...)
x |
An object of class "sTiles_summary" from [sTiles_summary()]. |
... |
Ignored, present for consistency with [print()]. |
'x', invisibly.
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) print(sTiles_summary(sTiles(Q))) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) print(sTiles_summary(sTiles(Q))) }
One shot: runs the preprocessing ([sTiles_analyze()]) and then the numeric factorization ([sTiles_factorize()]). To time the two phases apart, or to reuse one preprocessing across many sets of values, call those two yourself.
sTiles( Q, cores = 1L, mode = "auto", tile_size = 40L, inverse = FALSE, log_level = -1L )sTiles( Q, cores = 1L, mode = "auto", tile_size = 40L, inverse = FALSE, log_level = -1L )
Q |
A symmetric positive-definite matrix, either a 'Matrix::sparseMatrix' or a base matrix. Only the lower triangle is read. |
cores |
Worker threads (default 1). |
mode |
Tile regime: "auto" (default), "dense", "semisparse" or "sparse". |
tile_size |
Tile size, or -1 to let the solver choose (default 40). |
inverse |
Reserve selected-inverse storage, required by [sTiles_selinv()] and the 'sTiles_selinv_*()' queries (default 'FALSE'). |
log_level |
Solver verbosity: -1 silent (default), 0 timing, 1 info, 2 debug, 3 trace. |
An object of class "sTiles" wrapping a live factorization.
[sTiles_logdet()], [sTiles_solve()], [sTiles_selinv()], [sTiles_summary()], [sTiles_close()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_logdet(s) sTiles_solve(s, rep(1, 50)) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_logdet(s) sTiles_solve(s, rep(1, 50)) sTiles_close(s) }
The symbolic phase. It depends only on the sparsity pattern, not on the numeric values, and performs no Cholesky. Follow it with [sTiles_factorize()] for the numeric factorization. Timing the two apart separates the preprocessing cost, paid once per pattern, from the numeric cost, paid once per set of values.
sTiles_analyze( Q, cores = 1L, mode = "auto", tile_size = 40L, inverse = FALSE, log_level = -1L )sTiles_analyze( Q, cores = 1L, mode = "auto", tile_size = 40L, inverse = FALSE, log_level = -1L )
Q |
A symmetric positive-definite matrix, either a 'Matrix::sparseMatrix' or a base matrix. Only the lower triangle is read. |
cores |
Worker threads (default 1). |
mode |
Tile regime: "auto" (default), "dense", "semisparse" or "sparse". |
tile_size |
Tile size, or -1 to let the solver choose (default 40). |
inverse |
Reserve selected-inverse storage, required by [sTiles_selinv()] and the 'sTiles_selinv_*()' queries (default 'FALSE'). |
log_level |
Solver verbosity: -1 silent (default), 0 timing, 1 info, 2 debug, 3 trace. |
An object of class "sTiles", analyzed but not yet factorized.
[sTiles_factorize()], [sTiles_update()], [sTiles()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles_analyze(Q) sTiles_factorize(s) sTiles_logdet(s) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles_analyze(Q) sTiles_factorize(s) sTiles_logdet(s) sTiles_close(s) }
Reports whether a solver library can be found on this machine, checking the 'STILES_LIB', 'STILES_LIB_DIR' and 'STILES_BINARIES_DIR' environment variables, a copy bundled in the installed package, a development checkout, and the cache filled by [sTiles_install_library()]. It looks at the file system only: it never contacts the network and never loads anything, which makes it the right guard for examples and tests that have to be skipped where no solver is installed.
sTiles_available()sTiles_available()
'TRUE' when a solver library was found, 'FALSE' otherwise.
[sTiles_install_library()]
sTiles_available()sTiles_available()
Removes the solver copies that [sTiles_install_library()] placed in the package cache. The blunt instrument for a cache believed to be broken or stale, and the way to make room before installing a different release: call [sTiles_install_library()] afterwards to fetch a fresh one. Takes effect immediately unless this session has already built a matrix with the old solver, in which case the files go but a replacement can only load after restarting R (a loaded solver with live handles cannot be swapped underneath them).
sTiles_clean_cache()sTiles_clean_cache()
The number of cached solvers removed, invisibly.
[sTiles_install_library()], [sTiles_available()]
## Not run: sTiles_clean_cache() ## End(Not run)## Not run: sTiles_clean_cache() ## End(Not run)
Releases the solver's memory immediately. Optional: an object that goes out of scope is freed at the next garbage collection anyway. Worth calling in a loop over large matrices, where waiting for the collector means holding several factorizations at once.
sTiles_close(x)sTiles_close(x)
x |
An "sTiles" object. |
'NULL', invisibly.
[sTiles()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_close(s) }
Runs the numeric phase on an object that [sTiles_analyze()] has already prepared, so the ordering and tile layout are not recomputed.
sTiles_factorize(x, Q = NULL)sTiles_factorize(x, Q = NULL)
x |
An "sTiles" object from [sTiles_analyze()] or [sTiles()]. |
Q |
Optional: a matrix with the SAME sparsity pattern, whose values to factor. When omitted, the values captured at analyze time are used. |
The "sTiles" object, now factorized, invisibly.
[sTiles_analyze()], [sTiles_update()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles_analyze(Q) sTiles_factorize(s) sTiles_logdet(s) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles_analyze(Q) sTiles_factorize(s) sTiles_logdet(s) sTiles_close(s) }
Downloads the prebuilt solver library that matches this platform from the sTiles project's releases and stores it in the package cache, under 'tools::R_user_dir("sTiles", "cache")'. Run it once: every later session finds the cached copy without touching the network.
sTiles_install_library(tag = NULL, variant = NULL, force = FALSE)sTiles_install_library(tag = NULL, variant = NULL, force = FALSE)
tag |
Release tag to install, for example "v2026.8.27". Defaults to the latest release. |
variant |
Build variant to prefer, for example "armv82-armpl" or "v3-mkl", or "none" for the portable default. Defaults to the best fit for this CPU, falling back to the portable build when the release does not carry the preferred one. |
force |
Re-download even when a matching solver is already cached. |
The solver is a separate component under its own license terms (see the NOTICE file in this package) and is deliberately not bundled. Nothing is downloaded unless you call this function or accept the prompt an interactive session shows the first time a solver is needed. To skip the download altogether, point 'STILES_LIB' at a shared object you already have, or 'STILES_LIB_DIR' at the directory holding it. Setting 'STILES_NO_DOWNLOAD' disables the download path entirely.
The path of the installed shared library, invisibly.
Before anything is downloaded, and on every later call, the package takes the first solver it finds among:
'STILES_LIB', a shared object named outright;
'STILES_LIB_DIR', a directory holding one;
'STILES_BINARIES_DIR', a CI-artifact tree;
a 'binaries/' tree above the installed package or the working directory;
a copy bundled in the installed package, under 'solver/';
'lib/' in a development checkout above the package;
the download cache, newest release first.
[sTiles_available()], [sTiles_clean_cache()]
## Not run: sTiles_install_library() sTiles_install_library(tag = "v2026.8.27", variant = "none") ## End(Not run)## Not run: sTiles_install_library() sTiles_install_library(tag = "v2026.8.27", variant = "none") ## End(Not run)
Loads the solver if this session has not loaded it yet, then reports which file answered. Useful when several builds are installed and you need to know which one is in use.
sTiles_library_path()sTiles_library_path()
The absolute path of the loaded shared library, as a string.
[sTiles_available()], [sTiles_install_library()]
if (sTiles_available()) { sTiles_library_path() }if (sTiles_available()) { sTiles_library_path() }
Returns 'log(det(Q))', computed from the factor as '2 * sum(log(diag(L)))'.
sTiles_logdet(x)sTiles_logdet(x)
x |
A factorized "sTiles" object. |
The log-determinant, a single number.
[sTiles()], [sTiles_summary()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_logdet(s) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_logdet(s) sTiles_close(s) }
Computes Z, the inverse of Q restricted to the pattern of the Cholesky factor, 'pattern(L + L^T)'. The object must have been built with 'inverse = TRUE'. The call is idempotent, and the computation otherwise happens lazily on the first 'sTiles_selinv_*()' query. Call it explicitly to time the selected inverse on its own, and call it again after each [sTiles_factorize()] to refresh Z for the new values.
sTiles_selinv(x)sTiles_selinv(x)
x |
A factorized "sTiles" object built with 'inverse = TRUE'. |
The "sTiles" object, invisibly.
[sTiles_selinv_diag()], [sTiles_selinv_elm()], [sTiles_selinv_row()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) sTiles_selinv(s) head(sTiles_selinv_diag(s)) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) sTiles_selinv(s) head(sTiles_selinv_diag(s)) sTiles_close(s) }
Returns 'diag(solve(Q))', in the original ordering. Triggers the selected-inverse computation on first use.
sTiles_selinv_diag(x)sTiles_selinv_diag(x)
x |
A factorized "sTiles" object built with 'inverse = TRUE'. |
A numeric vector of length 'n', the diagonal of the inverse.
[sTiles_selinv()], [sTiles_selinv_elm()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) head(sTiles_selinv_diag(s)) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) head(sTiles_selinv_diag(s)) sTiles_close(s) }
Returns the selected inverse at position '(i, j)' when that position lies in the factor pattern, 'pattern(L + L^T)', and exactly 0 outside it. Both triangles are accepted, since Z is symmetric. Triggers the selected-inverse computation on first use.
sTiles_selinv_elm(x, i, j)sTiles_selinv_elm(x, i, j)
x |
A factorized "sTiles" object built with 'inverse = TRUE'. |
i, j
|
Row and column, 1-based, in the original ordering. |
The entry as a single number, 0 outside the factor pattern.
[sTiles_selinv()], [sTiles_selinv_row()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) sTiles_selinv_elm(s, 1, 2) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) sTiles_selinv_elm(s, 1, 2) sTiles_close(s) }
Returns the selected inverse at '(node, k)' for each 'k' in 'neighbors', the access pattern a graph model wants. Entries outside the factor pattern come back as 0.
sTiles_selinv_row(x, node, neighbors)sTiles_selinv_row(x, node, neighbors)
x |
A factorized "sTiles" object built with 'inverse = TRUE'. |
node |
Row index, 1-based, in the original ordering. |
neighbors |
Integer vector of column indices, 1-based. |
A numeric vector, one value per entry of 'neighbors'.
[sTiles_selinv()], [sTiles_selinv_elm()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) sTiles_selinv_row(s, 5, c(4, 5, 6)) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q, inverse = TRUE) sTiles_selinv_row(s, 5, c(4, 5, 6)) sTiles_close(s) }
Solve a linear system with the factorization
sTiles_solve(x, b, system = c("A", "L", "Lt"))sTiles_solve(x, b, system = c("A", "L", "Lt"))
x |
A factorized "sTiles" object. |
b |
Right-hand side: a length-'n' vector, or an 'n' by 'nrhs' matrix. |
system |
Which system to solve: "A" for 'Q x = b' (default), "L" for the forward solve 'L y = b', "Lt" for the backward solve 't(L) x = b'. |
The solution, with the same shape as 'b'.
[sTiles()], [sTiles_logdet()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) x <- sTiles_solve(s, rep(1, 50)) max(abs(as.vector(Q %*% x) - 1)) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) x <- sTiles_solve(s, rep(1, 50)) max(abs(as.vector(Q %*% x) - 1)) sTiles_close(s) }
Collects the dimensions, the fill, the tile mode, the phase the object is in, and the timings the solver measured. The result is an ordinary list, so single numbers are easy to pull out ('sTiles_summary(s)$chol_time'), and it prints as a short report.
sTiles_summary(x)sTiles_summary(x)
x |
An "sTiles" object. |
An object of class "sTiles_summary": a list with elements 'n', 'nnz', 'nnz_factor', 'mode', 'cores', 'inverse', 'factored', 'analyze_time', 'chol_time', 'selinv_time', 'version' and 'library'.
[sTiles()], [sTiles_version()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_summary(s)$nnz_factor sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles(Q) sTiles_summary(s)$nnz_factor sTiles_close(s) }
The ordering and tile layout depend only on WHERE the non-zeros are, so an object built by [sTiles_analyze()] can absorb any number of value updates and pay only the numeric cost each time. This is the loop an iterative method wants.
sTiles_update(x, Q)sTiles_update(x, Q)
x |
An "sTiles" object from [sTiles_analyze()]. |
Q |
A matrix with the SAME sparsity pattern, whose values to factor. |
The "sTiles" object, factorized with the new values, invisibly.
[sTiles_analyze()], [sTiles_factorize()]
if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles_analyze(Q) sTiles_factorize(s) d1 <- sTiles_logdet(s) ## same pattern, different values: no new ordering sTiles_update(s, Q * 2) d2 <- sTiles_logdet(s) c(d1, d2) sTiles_close(s) }if (sTiles_available()) { Q <- Matrix::bandSparse(50, k = c(0, 1), diagonals = list(rep(4, 50), rep(-1, 49)), symmetric = TRUE) s <- sTiles_analyze(Q) sTiles_factorize(s) d1 <- sTiles_logdet(s) ## same pattern, different values: no new ordering sTiles_update(s, Q * 2) d2 <- sTiles_logdet(s) c(d1, d2) sTiles_close(s) }
Version of the sTiles solver library
sTiles_version()sTiles_version()
The solver's version string.
[sTiles_summary()]
if (sTiles_available()) { sTiles_version() }if (sTiles_available()) { sTiles_version() }