feat(infer): DAAREM — a second (opt-in) EM/VBEM accelerator, stacked on #1051#1052
Merged
Conversation
Phase 1 of the performance work: a reproducible measurement rig, entirely additive and with no impact on the shipped release/dist binary. - Cargo.toml: new `[profile.profiling]` (release + line-table debug info, unstripped) so samply/flamegraph can attribute samples; the portable target-cpu floor still applies. - salmon-quant: a `PhaseTimer` emitting per-phase wall-clock (index_load, mapping, eff_length_collapse, em_bias, posterior, output) on the `salmon::timing` tracing target (isolate with RUST_LOG=salmon::timing=info). Pure instrumentation, never touches output or determinism. - salmon-infer: a criterion bench (benches/em_step.rs) over a seeded power-law equivalence-class fixture, timing single M-steps and bounded convergence for EM/VBEM, parallel vs sequential. This is the guard that will quantify the SQUAREM iteration-count reduction in Phase 2. - scripts/profile.sh: builds the profiling binary and runs quant under samply on a mapping-dominated and an inference-dominated (--numBootstraps) workload; smoke tier uses sample_data.tgz, GIAB tier (NA12878/GEUVADIS + GENCODE index) documented inline. Verified green under the pinned CI toolchain (1.91.1): clippy -D warnings, fmt --check, and cargo test --workspace --release (goldens unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 2 of the performance work. Adds SQUAREM (SqS3) convergence acceleration to the collapsed EM/VBEM optimizer, matching the acceleration the C++ salmon's CollapsedEMOptimizer used and that the Rust port had left deferred. - salmon-infer: new `EmAccel` enum on `EmOptions` (default `None`, unchanged output). `EmAccel::Squarem` drives a `squarem_loop` in `run_em_counts` that reuses the existing (already tuned) sharded M-step kernels as the fixed-point map `F`: two M-steps give r and v, an extrapolated step xp = x0 - 2a*r + a^2*v (steplength clamped to a <= -1, non-negativity backoff to the plain double step), and a stabilizing third M-step guarantees a valid iterate. The plain path is refactored to share one `F` closure but is behaviour-identical (byte-for-byte) to before. Norm/vector math is sequential for run-to-run determinism regardless of thread count. Benefits the point estimate and every bootstrap replicate; Gibbs is untouched (it uses no EM M-step). - CLI: `--emAccel <none|squarem>` (default none), wired through every QuantOptions.em construction site (reads mode + the deterministic modes). - Tests: same-fixpoint agreement vs plain EM and VBEM, mass conservation, and a slow-mixer case showing SQUAREM reaches the analytic fixpoint in a handful of M-steps where plain has not converged at the 10k cap. - Bench: adds converge-to-fixpoint plain-vs-SQUAREM cases to benches/em_step.rs. Not byte-identical to plain (different iterate sequence, same fixpoint within rel_diff_tol), so it is opt-in and default-off; existing goldens and --deterministic output are unchanged. Measured on GIAB NA12878 (GENCODE v50, ~1.18M eq-classes): same fixpoint (TPM Pearson 0.9999970, identical total TPM), with the main EM ~9% and a 30x bootstrap ~8% faster; the large gains appear on slow-mixing / tight-tolerance problems. Verified green under the pinned CI toolchain (1.91.1): clippy -D warnings, fmt --check, cargo test --workspace --release. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rem) Faithful port of the objective-free DAAREM variant (Damped Anderson Acceleration with Restarts and residual-monotonicity control) from Henderson & Varadhan, JCGS 28(4):834-846 (2019), as implemented in the CRAN `daarem` package's `daarem_base_noobjfn`. Salmon's EM/VBEM computes no likelihood, so the residual-norm monotonicity variant is the natural fit. Where SQUAREM extrapolates from the last two iterates only, DAAREM solves a damped least-squares problem over a window of the last `nlag` (=5) residual/iterate differences — a multi-secant quasi-Newton step that converges faster on high-dimensional, ill-conditioned problems, i.e. the transcript-quantification regime. It reuses the existing sharded M-step kernels as the fixed-point map `F` (via the same closure the SQUAREM path uses), so it benefits the point estimate and every bootstrap replicate. Damping (a ridge on the LS solve, chosen by a port of `DampingFind`) starts near a plain EM step and anneals toward the full Anderson step; the history restarts every `nlag` steps; a proposed step is accepted only if it does not increase the residual norm beyond a shrinking tolerance, else it falls back to a plain EM step. The small `nlag×nlag` Gram eigenproblem is solved with a self-contained cyclic Jacobi routine (no new dependency); all reductions are sequential for run-to-run determinism regardless of thread count. Convergence uses salmon's own `max_rel_diff` criterion so None/Squarem/Daarem all stop on the same rule. Not byte-identical to plain EM (same fixpoint within `rel_diff_tol`), so it is opt-in and default-off. Tests cover same-fixpoint agreement vs plain EM and VBEM, mass conservation, the Jacobi spectrum, and a slow-mixer case where DAAREM reaches the analytic fixpoint in a handful of M-steps while plain has not converged at the 10k cap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wires EmAccel::Daarem through the `--emAccel` value enum (none|squarem|daarem) and adds a converge-to-fixpoint DAAREM case to benches/em_step.rs alongside plain and SQUAREM, so the three accelerators can be compared on the same fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rob-p
added a commit
that referenced
this pull request
Jul 10, 2026
Opt-in EM/VBEM accelerators (--emAccel squarem|daarem), profiling harness + per-phase timing across all quant paths, radix-sort scratch fix, help cleanup. Default output unchanged; credits @BenjaminDEMAILLE for #1051/#1052/#1053. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B7JMur5DmDpECddErpi2JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DAAREM: a second, opt-in EM/VBEM accelerator
This PR is accompanied by a short literature review, because the design question ("can salmon's EM/VBEM inference be made faster without changing the model?") has a well-developed answer in the numerical-optimization and RNA-seq literature, and the choice of accelerator follows directly from it.
1. Motivation and research question
Salmon pairs a fast mapping stage with a statistical model (EM, or VBEM under
--useVBOpt) over equivalence classes to estimate transcript abundances [Patro et al. 2017]. The EM/VBEM step is a fixed-point iterationx <- F(x)that converges only linearly; when the fraction of "missing information" is large (exactly the multi-mapping / equivalence-class structure of transcript quantification), plain EM crawls, and this cost is multiplied by every bootstrap/Gibbs replicate.The question is whether that inference can be accelerated without touching the model (same fixpoint, same accuracy). The literature answers yes, and it identifies which accelerators to prefer.
2. Literature review: accelerating EM-like fixed-point iterations
SQUAREM [Varadhan & Roland 2008] is the baseline this repo adopts in #1051. From two successive EM maps it forms difference vectors
r,vand takes a squared extrapolation stepx' = x - 2*alpha*r + alpha^2*vplus a stabilizing EM step. It is off-the-shelf (you supply onlyF), superlinear, and typically needs 1-2 orders of magnitude fewer EM evaluations on high-missing-information problems. Its limitation, relevant here, is that it uses only the last two iterates, discarding longer-history curvature.DAAREM [Henderson & Varadhan 2019] is the method this PR implements. It is Anderson acceleration [Anderson 1965; Walker & Ni 2011] over a window of the last
mresiduals (a multi-secant quasi-Newton step), hardened with three additions: a damping factor on the least-squares coefficients, periodic restarts of the history, and epsilon-monotonicity control. In the authors' benchmarks it matches or beats SQUAREM, with the margin growing on harder, higher-dimensional problems, precisely because it exploits history that SQUAREM throws away. It keeps SQUAREM's off-the-shelfF-only interface.Quasi-Newton EM [Zhou, Alexander & Lange 2011] is the other strong high-dimensional contender: a low-rank secant approximation to the EM map's Jacobian, reported to "rival or surpass SQUAREM" and explicitly designed for genomics-scale optimization. Plain Anderson acceleration [Anderson 1965; Walker & Ni 2011] is fast locally but non-monotone and fragile far from the solution; DAAREM is essentially the safe, deployable version. A recent survey [Kuroda 2023] confirms the consensus: no accelerator dominates everywhere, but the multi-secant/history methods (DAAREM, quasi-Newton) tend to win on higher-dimensional, ill-conditioned problems, while SQUAREM's edge is simplicity and near-zero overhead.
Reading for salmon. Transcript quantification lives in exactly the regime the review flags for DAAREM: 10^5-10^6 transcripts, sparse and ill-conditioned equivalence-class structure. So DAAREM is the literature-motivated next step beyond SQUAREM, and the empirical section below confirms it.
(For completeness: the standard model-level speed/accuracy levers, equivalence-class collapse [Bray et al. 2016], stochastic collapsed VB and VBEM [Patro et al. 2017], and range-factorization [Zakeri et al. 2017], are already inside salmon; the remaining algorithmic headroom for the inference kernel is the accelerator itself.)
3. What this PR does
EmAccel::Daarem(--emAccel daarem, defaultnone). A faithful port of the objective-free DAAREM variant (CRANdaarem::daarem_base_noobjfn): salmon's EM/VBEM computes no likelihood, so the residual-norm monotonicity control is the natural fit. A proposed Anderson step is accepted only if it does not increase||F(x) - x||beyond a shrinking tolerance, otherwise it falls back to a plain EM step.F(the same closure the SQUAREM path uses), so it accelerates the point estimate and every bootstrap replicate.nlag x nlag(nlag = 5) Gram eigenproblem uses a self-contained cyclic Jacobi routine and a direct port ofDampingFind(the ridge-damping Newton search), so there is no new dependency. All reductions are sequential for run-to-run determinism regardless of thread count; the expensive per-class M-step stays inside the already-parallelF.4. Empirical validation
Measured on GIAB NA12878 / HG001 (ENA
ERR356372) against a GENCODE v50 index (~0.5M equivalence classes on a 4M-read-pair subset):DAAREM is roughly 4x more accelerating than SQUAREM here, exactly as the high-dimensional literature predicts.
5. Correctness, and an honest behavioural note
rel_diff_tol = 0.01), DAAREM's abundances differ from plain EM more than SQUAREM's do (on GIAB, ~0.19% of transcripts differ by > 1 TPM), concentrated on ambiguous / ill-conditioned transcript groups. A controlled experiment indicates the cause is that plain EM under-converges those transcripts at the default tolerance: on a controlled ill-conditioned two-transcript case, plain stops at its initialization (a0 = 50000) while DAAREM reaches the true fixpoint (a0 = 25001); and on GIAB, tightening the tolerance measurably increases plain's own EM work (offline EM 26.2 s -> 31.0 s). So DAAREM is converging these harder transcripts more fully, not less accurately.--deterministicrequant path made isolating the tolerance effect awkward), so the "more fully converged" interpretation is well-supported by two converging lines of evidence (a controlled synthetic case plus the GIAB tolerance-vs-work measurement) rather than airtight.This is a genuine design call for the maintainers: DAAREM is faster and converges the EM more completely, but its output therefore diverges from the historical plain-EM result more than SQUAREM does. Being opt-in and default-off, it changes nothing unless explicitly requested.
6. Verification
Green under the pinned CI toolchain (1.91.1):
cargo fmt --all -- --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace --release.References
All confirmed against publisher DOIs; none included on memory alone.
AI-assisted-research disclosure: the literature search underlying this review was conducted with AI research agents; every reference was verified independently against its publisher DOI, with no fabrication or mashups.