Skip to main content

salmon_model/
lib.rs

1//! `salmon-model`: statistical models used during quantification.
2//!
3//! # What these models are for
4//!
5//! Sequencing is not a uniform sampler. A fragment's chance of being observed
6//! depends on how long it is, what sequence sits at its ends, how GC-rich it is,
7//! and where in the transcript it starts. Left uncorrected, those effects are
8//! read as differences in *abundance*, which they are not. Each model here
9//! measures one such effect from the data itself and hands the quantifier a
10//! correction factor:
11//!
12//! * [`fld`] — the fragment-length distribution, and automatic library-type
13//!   detection ([`libdetect`]);
14//! * [`seqbias`] — sequence-specific bias, from primer/ligation preferences at
15//!   fragment ends;
16//! * [`gcbias`] — fragment GC bias, largely from PCR amplification efficiency;
17//! * [`posbias`] — positional bias along the transcript body, e.g. 3' pileup from
18//!   degraded RNA;
19//! * [`bias`] — combines them into a corrected effective length per transcript.
20//!
21//! Every model is *observed vs expected*: count what was actually seen, count
22//! what would have been seen under no bias, and take the ratio. That framing is
23//! why the dump files in [`dumps`] always come in `obs`/`exp` pairs.
24
25/// Fixed-point scale for deterministic bias-model mass accumulation. Bias
26/// observed models sum per-fragment posterior masses (each in `[0,1]`) into
27/// bins; an f64 `+=` is non-associative, so the accumulated model — and hence
28/// bias correction — varies with the worker-thread fragment partition (thread
29/// count). Accumulating `round_down(mass * BIAS_WEIGHT_SCALE)` as integers makes
30/// the sum associative (order/thread-count independent). `2^20` keeps the
31/// per-contribution resolution at ~1e-6 (ample for these coarse models) while a
32/// bin total (~num_fragments × 2^20) stays far below `u64::MAX`.
33///
34/// Same reasoning as the equivalence-class weight accumulator: integer addition
35/// commutes, floating-point addition does not, so identical input must give
36/// identical output regardless of how the work was split.
37pub const BIAS_WEIGHT_SCALE: f64 = (1u64 << 20) as f64;
38
39/// Quantize a bias mass (`[0,∞)`, typically a `[0,1]` posterior) to the
40/// fixed-point integer accumulator. Truncates (rounds toward zero) — cheaper
41/// than `round()` and the ≤1-ULP downward bias is negligible and cancels under
42/// the model's normalization.
43///
44/// ("ULP" is a unit in the last place: the smallest difference representable at
45/// that magnitude.)
46#[inline]
47pub fn bias_mass_to_fp(mass: f64) -> u64 {
48    (mass * BIAS_WEIGHT_SCALE) as u64
49}
50
51pub mod bias;
52pub mod dumps;
53pub mod fld;
54pub mod gcbias;
55pub mod libdetect;
56pub mod posbias;
57pub mod seqbias;
58pub mod spline;
59
60// Re-exports so callers can write `salmon_model::SBModel` rather than naming the
61// submodule; the modules above remain the real homes.
62pub use bias::{
63    build_expected_pos, corrected_effective_length_full, positional_factor, positional_factor_into,
64    BiasInputs,
65};
66pub use fld::FragLengthSource;
67pub use fld::{
68    ambig_frag_log_prob, smoothed_effective_length, DiscreteFld, FragmentLengthDistribution,
69};
70pub use gcbias::{
71    build_expected_gc, gc_corrected_effective_length, gc_desc, gc_prefix, gc_ratio, GcFragModel,
72    GcRank, GcStore, GcView, GC_SAMP_STRIDE,
73};
74pub use libdetect::{infer_format_from_counts, LibraryTypeDetector};
75pub use posbias::{
76    compute_length_quantiles, length_class_index, SimplePosBias, NUM_LENGTH_CLASSES, NUM_POS_BINS,
77};
78pub use seqbias::{build_expected, corrected_effective_length, LogBiasTable, SBModel};