Skip to contents

`ms_fit()` fits per-sample exposures of an [MsCatalog] against a given signature dictionary and returns an [MsFit] – the mirror image of [ms_extract()], which discovers signatures. The method is a single string: `"nnls"` (default; the Lawson-Hanson baseline), or one of the two likelihood methods: `"likelihood_bidirectional"` (MuSiCal epsilon stepping) and `"lrt"` (negative-binomial MLE + entry-wise presence LRT, mSigAct flavour). All violations raise `msuiter_error_*` conditions.

Usage

ms_fit(
  catalog,
  signatures,
  method = "nnls",
  nb_size = 8,
  zero_threshold = 0.01,
  connected = NULL,
  rescale = TRUE
)

Arguments

catalog

An [MsCatalog] object, as returned by [ms_tally()] or [ms_catalog()].

signatures

The dictionary to fit against: a numeric matrix (channels x signatures; rows match the catalog channel labels by label when rownames exist, positionally otherwise), or an [MsSignature] whose `signatures` matrix is used.

method

Single string: `"nnls"`, `"likelihood_bidirectional"` or `"lrt"` (the `c(...)` form of the default documents the legal choices; the default is `"nnls"` for this unit – the MSU-Fit default orchestration is a later unit).

nb_size

Single positive number: the negative-binomial size of every likelihood-based column (mSigAct `nbinom.size`; larger = closer to Poisson). Default 8, the mSigAct SBS96 anchor (DBS78/ID83 anchors: 50 / 100).

zero_threshold

Single number in \([0, 1]\): the compositional share below which an exposure is zeroed (decision rule, sigfit CI-zeroing family anchor 0.01). Zeroed exposures are set to exactly 0 and reported `active = FALSE`; standard errors exist only on the interior support ("boundary parameters get tests, not intervals").

connected

NULL (default; no rejoin) or a vector of distinct 1-based signature indices (dictionary column order) forming the connected-signature group – the clock-like background signatures (e.g. the SBS1/SBS5 pair) of the MSU-Fit "the connected-group configuration" (ARCHITECTURE section 5). The rejoined signatures are FIXED at their initial full-NNLS solution after the method refit (`FixAndRefit`) and the remaining signatures are re-optimized against the residual. **Divergence (D13)**: this is a documented approximation of the upstream MuSiCal connected semantics (research/02 section 1, line 23), which rejoins the connected signatures into the final NNLS support and re-solves ALL signatures jointly; here the connected block never adjusts to the sparse refit. The upstream-exact `JointRefit` strategy is declared future work and is never silently substituted.

rescale

Single logical (default `TRUE`): the TMB rescale – multiply each sample's exposures back onto its original mutation total (the MuSiCal rescale semantics, research/02 section 1 line 27), so the kept exposures carry the sample's own mutation mass on the original count scale. The rescale only multiplies: zeroed entries stay zero and the per-sample shares (hence the zeroing decision) are invariant. Presence evidence (`tests`) is computed before the post-steps and is unaffected by either argument.

Value

An [MsFit] object.

Details

**Method semantics** (research/02 section 1; D11 anchors). `"nnls"` solves, per sample, `argmin ||S t - v_j||_2, t >= 0` through the precomputed Gram pair. `"likelihood_bidirectional"` starts from the full NNLS solution and alternates backward/forward steps on the per-mutation multinomial log-likelihood: a signature leaves when the least costly removal costs less than `eps` (0.001, the pinned MuSiCal LTH default) and enters when the best gain exceeds it, until both steps decline. `"lrt"` maximizes the negative-binomial likelihood over the exposures (warm-started from NNLS; the objective is concave, the internal safeguarded-Newton ascent is deterministic and thread-invariant) and reports, for every signature, the entry-wise presence LRT of the full model against the model refit without it: `D = 2(ll_with - ll_without)`, raw p-value = the Self-Liang boundary half-tail. Presence evidence is method-independent: the LRT columns are always computed at the NB-MLE exposures, so `tests` compares fairly across methods. No BH correction is applied here (that is `ms_test_presence()`, U-M3a-03).

**Hyper-parameters frozen for this unit** (recorded per the Delta discipline): bidirectional `eps = 0.001` (upstream LTH default) and the round/iteration cap `max_iter = 1000` (upstream anti-cycling cap).

**Result.** The [MsFit] carries: `exposures` (signatures x samples, labelled; zeroed entries exactly 0); `support`, one tidy row per (signature, sample) with the exposure, its compositional share and the zeroing decision; `tests`, one tidy row per (signature, sample) with the presence LRT statistic, raw p-value and interior-support Fisher standard error (NA at the boundary); an adhoc `reference_summary` snapshot derived from the signature labels hash (sha256, FIPS 180-4); and `engine` = the method string.

See also

[ms_extract()] for discovery, [ms_catalog()] / [ms_tally()] for the upstream counts, [MsFit] for the result container.

Examples

# Refit exposures of the simulated catalog against the true signatures.
ref <- ms_refdb_bundled()$SBS_GRCh38@matrices[[1]]
S <- ref[, c("SBS1", "SBS5", "SBS13")]
colnames(S) <- paste0("CS", 1:3)
E <- matrix(rep(c(0.5, 0.3, 0.2), each = 24), 3, 24)
sim <- ms_simulate(S, exposures = E, burden = 4000, seed = 7)
ext <- ms_extract(sim, k = 3)
#> Warning: K selection is undecidable: no rank passes the stability veto; falling back to the CV argmin (evidence table attached to the result)
#> ℹ the stability veto rejected every rank in the evaluated grid
#> falling back to k = 3
#> call ms_select_k() explicitly for the full evidence table

fit <- ms_fit(sim, ext@signatures)
fit@exposures[1:3, 1:4]
#>      sim1       sim2      sim3 sim4
#> Sig1    1 0.96012142 0.3425416    0
#> Sig2    0 0.03987858 0.5254025    0
#> Sig3    0 0.00000000 0.1320559    1
head(fit@support)
#>   signature sample   exposure      share active
#> 1      Sig1   sim1 1.00000000 1.00000000   TRUE
#> 2      Sig2   sim1 0.00000000 0.00000000  FALSE
#> 3      Sig3   sim1 0.00000000 0.00000000  FALSE
#> 4      Sig1   sim2 0.96012142 0.96012142   TRUE
#> 5      Sig2   sim2 0.03987858 0.03987858   TRUE
#> 6      Sig3   sim2 0.00000000 0.00000000  FALSE