Get started: install, the five-minute path, and the anatomy of an output
Source:vignettes/get-started.Rmd
get-started.RmdInstall
msuiter is a source install: the numerical kernels are a Rust
workspace that R CMD INSTALL builds through the package
configure face, so the toolchain floors stated in
DESCRIPTION bind the install itself. The committed renv lockfile pins
the R and package universe the campaign is validated against; restoring
it before installing keeps the floor question answered by bytes rather
than by luck.
if (!is.na(PKG)) {
desc <- readLines(file.path(PKG, "DESCRIPTION"), warn = FALSE)
print(desc[grepl("^Package:|^Version:|^Depends:|^SystemRequirements:",
desc)])
lockfile <- file.path(PKG, "inst", "repro", "renv.lock")
if (file.exists(lockfile)) {
print(basename(lockfile))
} else if (!dir.exists(file.path(PKG, "inst", "repro"))) {
# inst/repro ships inside the source package, so this branch is the
# synthetic-root face of the governing test rather than a real build
# context; say so rather than stopping.
print("renv.lock: outside this build context")
} else {
stop("committed renv lockfile missing from its own committed directory")
}
} else {
print("install face reads the source DESCRIPTION")
}
#> [1] "Package: msuiter"
#> [2] "Version: 0.1.0.9000"
#> [3] "Depends: R (>= 4.3)"
#> [4] "SystemRequirements: cargo, rustc (>= 1.71)"
#> [1] "renv.lock"With a checkout in hand, the install face is one command:
Without a checkout, install the development build straight from
GitHub – the same configure face builds the Rust core on
the way in:
Prebuilt packages are attached to the tagged release and install from
a local file with
install.packages(<file>, repos = NULL). There is no
CRAN release yet.
Five-minute path
One chain, four faces, end to end: ms_variants()
validates the typed input IR, ms_tally() runs it through
the Rust kernel over a micro twoBit genome into a channel table,
ms_signature() mounts the committed specimen matrix as a
signature dictionary pinned to that channel table, and the bundled
reference face arrives as an MsRefDb – the COSMIC v3.6
dictionaries ship inside the package, so the trusted default needs no
download.
The genome is the same synthetic writer the tour uses: a trivial chromosome whose bases agree with the variant records base for base, so every printed number below is real kernel output over committed bytes.
.u32le <- function(v) writeBin(as.integer(v), raw(), size = 4L,
endian = "little")
.pack_bases <- function(bases) {
out <- raw((length(bases) + 3L) %/% 4L)
acc <- 0L; filled <- 0L; k <- 0L
for (b in bases) {
acc <- bitwOr(bitwShiftL(acc, 2L), b); filled <- filled + 1L
if (filled == 4L) { k <- k + 1L; out[k] <- as.raw(acc); acc <- 0L; filled <- 0L }
}
if (filled > 0L) out[k + 1L] <- as.raw(bitwShiftL(acc, 2L * (4L - filled)))
out
}
.write_2bit <- function(path) {
chr1 <- rep(c(2L, 1L), 32L)
chr2 <- rep(c(3L, 1L), 20L)
recs <- list(
list(name = "chr1", size = 64L, dna = .pack_bases(chr1),
nb = integer(0), nl = integer(0)),
list(name = "chr2", size = 40L, dna = .pack_bases(chr2),
nb = integer(0), nl = integer(0)))
off <- 16L + sum(vapply(recs, function(r) 1L + nchar(r$name) + 4L, 0L))
index <- raw(); body <- raw()
for (r in recs) {
index <- c(index, as.raw(nchar(r$name)), charToRaw(r$name), .u32le(off))
rec <- c(.u32le(r$size), .u32le(length(r$nb)), .u32le(r$nb), .u32le(r$nl),
.u32le(0L), .u32le(0L), r$dna)
body <- c(body, rec); off <- off + length(rec)
}
bytes <- c(.u32le(0x1A412743L), .u32le(0L), .u32le(length(recs)),
.u32le(0L), index, body)
con <- file(path, "wb"); on.exit(close(con), add = TRUE)
writeBin(bytes, con); invisible(path)
}
genome <- file.path(tempdir(), "getstarted-2bit.2bit")
.write_2bit(genome)
tab <- data.frame(
chrom = c("chr1", "chr1", "chr1"),
start = c(12L, 22L, 14L),
end = c(12L, 22L, 14L),
ref = c("C", "C", "C"),
alt = c("A", "T", "T"),
sample = c("S1", "S1", "S2"))
v <- ms_variants(tab, "GRCh38", caller = "get-started",
matched_normal = "none")
v
#> <MsVariants> 3 variants | genome: GRCh38
cat1 <- ms_tally(v, genome)
cat1
#> <MsCatalog> SBS96: 96 channels x 2 samples; skipped 0/3 variantsThe signature dictionary is the committed specimen itself – read verbatim, its first two columns mounted under the channel labels the tally produced, so every exposure figure below is the fixture’s own number and the channel hash is derived from the validated catalog, never hand-forged:
raw_spec <- utils::read.delim(FIX, check.names = FALSE,
stringsAsFactors = FALSE)
labels <- cat1@channels$labels
spec <- as.matrix(raw_spec[, 2L:3L, drop = FALSE])
rownames(spec) <- raw_spec[[1L]]
if (!identical(rownames(spec), labels)) stop("specimen rows must ride the channel labels")
exposures <- matrix(c(0.6, 0.4), 2L, 1L,
dimnames = list(colnames(spec), "S1"))
summary1 <- list(
n_channels = length(labels), n_samples = 1L,
channel_name = cat1@channels$name, channel_hash = cat1@channels$hash,
build = "GRCh38")
sig <- ms_signature(spec, exposures, summary1, engine = "synthetic", seed = 1L)
sig
#> <MsSignature> 2 signatures x 1 samples | engine: synthetic | seed: 1
br <- ms_refdb_bundled()
db <- br[["SBS_GRCh38"]]
db
#> <MsRefDb> class: SBS | version: v3.6 | 1 matrices
head(db@matrices[[1L]][, 1L:3L], 3L)
#> SBS1 SBS2 SBS3
#> A[C>A]A 0.0008760226 5.789892e-07 0.020919749
#> A[C>A]C 0.0022201200 1.455043e-04 0.016343192
#> A[C>A]G 0.0001797268 5.361706e-05 0.001807548Anatomy of the output
The MsSignature above is a typed object, not a loose
matrix: the signature matrix, the exposure matrix, the pinned channel
summary and the provenance (engine, seed) are separate slots, each
validated at the boundary that owns it.
print(class(sig))
#> [1] "msuiter::MsSignature" "S7_object"
str(sig)
#> <msuiter::MsSignature>
#> @ signatures : num [1:96, 1:2] 2.18e-03 8.57e-05 1.30e-04 4.07e-04 2.39e-03 ...
#> .. - attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:96] "A[C>A]A" "A[C>A]C" "A[C>A]G" "A[C>A]T" ...
#> .. ..$ : chr [1:2] "SBS96A" "SBS96B"
#> @ exposures : num [1:2, 1] 0.6 0.4
#> .. - attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:2] "SBS96A" "SBS96B"
#> .. ..$ : chr "S1"
#> @ stability : list()
#> @ k_evidence :'data.frame': 0 obs. of 0 variables
#> @ engine : chr "synthetic"
#> @ seed : int 1
#> @ catalog_summary:List of 5
#> .. $ n_channels : int 96
#> .. $ n_samples : int 1
#> .. $ channel_name: chr "SBS96"
#> .. $ channel_hash: chr "1edf6b63f9d98c7b6c62744d09d69889"
#> .. $ build : chr "GRCh38"
print(dim(sig@signatures))
#> [1] 96 2
print(dim(sig@exposures))
#> [1] 2 1
print(sig@engine)
#> [1] "synthetic"
print(substr(sig@catalog_summary$channel_hash, 1L, 16L))
#> [1] "1edf6b63f9d98c7b"The channel hash printed above is the tamper seal: it is recomputed from the validated channel table, and a hand-edited dictionary is refused at construction – the same seal the tour demonstrates from the refusing side.
Where to go next
The concept bridge is vignettes/articles/msu-map.qmd (one row per export: concept, pillar, peer counterpart). The narrative tour continues in tour.html; the whole docs corpus is navigated from docs-bridge.html; every exported face is documented in the reference index.