Skip to content

Commit a72fd59

Browse files
rob-pclaude
andcommitted
feat: forward --small-thresh to alevin-fry from quant and multiplex-quant
alevin-fry resolves cells below --small-thresh through a fast path that applies cr-like semantics regardless of --resolution. That is a deliberate optimization -- such cells carry one or two ambiguous UMIs and are filtered by any QC -- but until now it was neither visible nor adjustable from simpleaf. Forwarded only when the user sets it, so alevin-fry's own default stands otherwise. alevin-fry < 0.17.1 parses the option and ignores it, which is noted in the help text and the docs. Verified end to end against the pbmc_1k_v3 mapping directory with -r parsimony-em: unset records small_thresh=100 with 71,298 cells on the fast path and 132 empty; --small-thresh 0 records small_thresh=0 with 0 on the fast path and 0 empty, recovering 82,069 UMIs (0.53%) across 452,324 extra matrix entries -- matching the figure measured by driving alevin-fry directly. Also refreshes the docs' embedded --help blocks, which still carried the "only if using piscem >= 0.7" text removed from --decoy-paths earlier. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VhyNvEWUgY8ALVacdJHxVJ
1 parent 1347fb3 commit a72fd59

9 files changed

Lines changed: 109 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
`multiplex-quant`, and `atac process`.
2828
* `--tmp-dir` and `--ram-limit-gib` are forwarded to `piscem build` from both
2929
index commands.
30+
* `--small-thresh` on `quant` and `multiplex-quant`, forwarded to alevin-fry.
31+
Cells below it are resolved by alevin-fry's tiny-cell fast path, which
32+
applies `cr-like` semantics regardless of `--resolution`; `--small-thresh 0`
33+
resolves every cell with the requested strategy. Requires alevin-fry
34+
>= 0.17.1 (earlier versions parse the option and ignore it).
3035
* `--sample-bc-ori {forward,reverse}` on `multiplex-quant` (#199).
3136
* `10x-flexv2-gex-3p-config-b` chemistry preset (#201).
3237
* `gene_id_to_name.tsv` emitted from probe sets that carry a gene-name column,

docs-site/src/content/docs/flex-quant-command.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ Quantification Options:
146146
[possible values: cr-like, cr-like-em, parsimony, parsimony-em, parsimony-gene,
147147
parsimony-gene-em]
148148

149+
--small-thresh <N>
150+
Cells with fewer than this many reads are resolved by alevin-fry's tiny-cell fast path,
151+
which applies `cr-like` (winner-take-all) semantics regardless of `--resolution`. Pass 0
152+
to resolve every cell with the requested strategy.
153+
154+
Left unset, alevin-fry's own default applies. Requires alevin-fry >= 0.17.1; earlier
155+
versions parse the option and ignore it.
156+
149157
Piscem Mapping Options:
150158
--skipping-strategy <SKIPPING_STRATEGY>
151159
The skipping strategy to use for k-mer collection
@@ -154,7 +162,7 @@ Piscem Mapping Options:
154162
[possible values: permissive, strict]
155163

156164
--struct-constraints
157-
If piscem >= 0.7.0, enable structural constraints
165+
Enable structural constraints when mapping
158166

159167
--max-ec-card <MAX_EC_CARD>
160168
Maximum cardinality equivalence class to examine

docs-site/src/content/docs/index-command.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ Piscem Index Options:
137137
138138
--decoy-paths <DECOY_PATHS>
139139
Paths to decoy sequence FASTA files used to insert poison k-mer information into the index
140-
(only if using piscem >= 0.7)
141140
142141
--seed <HASH_SEED>
143142
The seed value to use in SSHash index construction (try changing this in the rare event

docs-site/src/content/docs/quant-command.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,40 @@ It is possible to have pieces of geometry repeated, in which case they will be e
3535
:::note
3636
If you use a custom geometry frequently, you can add it to the chemistries registry. For details on adding your own chemistry definition to the registry, please read about the [chemistry command](/simpleaf/chemistry-command/).
3737
:::
38+
## Tiny cells and `--small-thresh`
39+
40+
`alevin-fry` resolves very small cells through a fast path that skips the
41+
equivalence-class machinery entirely. That path implements `cr-like`
42+
(winner-take-all) semantics, and it applies to cells below the threshold
43+
*regardless of what you pass to* `--resolution` — so under `parsimony-em`, a
44+
tiny cell whose UMIs are all gene-ambiguous is resolved by discarding them
45+
rather than by spreading them fractionally.
46+
47+
This is deliberate. Cells that small carry on the order of one or two distinct
48+
UMIs, typically ambiguous between paralogous genes, and are removed by any
49+
reasonable QC filter whichever way they resolve. Skipping the full machinery
50+
for them is a large saving on datasets with many low-count barcodes.
51+
52+
`--small-thresh` makes the behaviour visible and adjustable:
53+
54+
```sh
55+
# resolve every cell with the requested strategy, however small
56+
simpleaf quant ... -r parsimony-em --small-thresh 0
57+
```
58+
59+
Left unset, `alevin-fry`'s own default (currently 100 reads) applies. Which
60+
cells took the fast path is recorded in `af_quant/quant.json` as
61+
`num_tiny_cell_resolved` and `tiny_cell_resolved_cell_numbers`, so the choice
62+
is auditable after the fact.
63+
64+
:::note
65+
This option requires **alevin-fry >= 0.17.1**. Earlier versions parse it and
66+
silently ignore it, so on 0.17.0 passing `--small-thresh 0` will have no
67+
effect. On a 73k-cell PBMC run, `--small-thresh 0` recovers about 0.53% of
68+
total UMI mass and empties no cells, against 132 zero-count cells at the
69+
default.
70+
:::
71+
3872
## Threads, and the shared decode budget
3973

4074
As of `piscem` 0.22.0, the `-t`/`--threads` value is a single budget of execution
@@ -86,7 +120,7 @@ Mapping Options:
86120

87121
Piscem Mapping Options:
88122
--struct-constraints
89-
If piscem >= 0.7.0, enable structural constraints
123+
Enable structural constraints when mapping
90124

91125
--ignore-ambig-hits
92126
Skip checking of the equivalence classes of k-mers that were too ambiguous to be otherwise
@@ -189,6 +223,14 @@ UMI Resolution Options:
189223
[possible values: cr-like, cr-like-em, parsimony, parsimony-em, parsimony-gene,
190224
parsimony-gene-em]
191225

226+
--small-thresh <N>
227+
Cells with fewer than this many reads are resolved by alevin-fry's tiny-cell fast path,
228+
which applies `cr-like` (winner-take-all) semantics regardless of `--resolution`. Pass 0
229+
to resolve every cell with the requested strategy.
230+
231+
Left unset, alevin-fry's own default applies. Requires alevin-fry >= 0.17.1; earlier
232+
versions parse the option and ignore it.
233+
192234
Output Options:
193235
--anndata-out
194236
Generate an anndata (h5ad format) count matrix from the standard (matrix-market format)

src/simpleaf_commands.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,16 @@ pub struct MapQuantOpts {
330330
#[arg(short, long, help_heading = "UMI Resolution Options", value_parser = clap::builder::PossibleValuesParser::new(["cr-like", "cr-like-em", "parsimony", "parsimony-em", "parsimony-gene", "parsimony-gene-em"]))]
331331
pub resolution: String,
332332

333+
/// Cells with fewer than this many reads are resolved by alevin-fry's
334+
/// tiny-cell fast path, which applies `cr-like` (winner-take-all) semantics
335+
/// regardless of `--resolution`. Pass 0 to resolve every cell with the
336+
/// requested strategy.
337+
///
338+
/// Left unset, alevin-fry's own default applies. Requires alevin-fry
339+
/// >= 0.17.1; earlier versions parse the option and ignore it.
340+
#[arg(long, value_name = "N", help_heading = "UMI Resolution Options")]
341+
pub small_thresh: Option<usize>,
342+
333343
/// Generate an anndata (h5ad format) count matrix from the standard (matrix-market format)
334344
/// output.
335345
#[arg(long, help_heading = "Output Options")]
@@ -746,6 +756,16 @@ pub struct MultiplexQuantOpts {
746756
]))]
747757
pub resolution: String,
748758

759+
/// Cells with fewer than this many reads are resolved by alevin-fry's
760+
/// tiny-cell fast path, which applies `cr-like` (winner-take-all) semantics
761+
/// regardless of `--resolution`. Pass 0 to resolve every cell with the
762+
/// requested strategy.
763+
///
764+
/// Left unset, alevin-fry's own default applies. Requires alevin-fry
765+
/// >= 0.17.1; earlier versions parse the option and ignore it.
766+
#[arg(long, value_name = "N", help_heading = "Quantification Options")]
767+
pub small_thresh: Option<usize>,
768+
749769
/// k-mer length for probe index building
750770
#[arg(long, default_value_t = 23, help_heading = "Probe Set Options")]
751771
pub kmer_length: usize,

src/simpleaf_commands/multiplex_quant.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ pub fn multiplex_map_and_quant(af_home: &Path, opts: MultiplexQuantOpts) -> anyh
474474
.arg(&opts.resolution)
475475
.arg("--use-mtx");
476476

477+
// Only forwarded when the user set it, so alevin-fry's own default stands
478+
// otherwise. alevin-fry < 0.17.1 parses this and ignores it.
479+
if let Some(small_thresh) = opts.small_thresh {
480+
quant_cmd
481+
.arg("--small-thresh")
482+
.arg(small_thresh.to_string());
483+
}
484+
477485
let quant_cmd_str = prog_utils::get_cmd_line_string(&quant_cmd);
478486
info!("quant cmd: {}", quant_cmd_str);
479487
let quant_start = Instant::now();
@@ -873,6 +881,7 @@ mod tests {
873881
reads1: Vec::new(),
874882
reads2: Vec::new(),
875883
resolution: String::from("cr-like"),
884+
small_thresh: None,
876885
kmer_length: 23,
877886
skipping_strategy: String::from("permissive"),
878887
struct_constraints: false,

src/simpleaf_commands/quant.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,13 @@ fn run_quant_stage(
555555
alevin_quant_cmd.arg("-t").arg(format!("{}", setup.threads));
556556
alevin_quant_cmd.arg("-m").arg(setup.t2g_map_file.clone());
557557
alevin_quant_cmd.arg("-r").arg(&opts.resolution);
558+
// Only forwarded when the user set it, so alevin-fry's own default stands
559+
// otherwise. alevin-fry < 0.17.1 parses this and ignores it.
560+
if let Some(small_thresh) = opts.small_thresh {
561+
alevin_quant_cmd
562+
.arg("--small-thresh")
563+
.arg(small_thresh.to_string());
564+
}
558565
let quant_cmd_string = prog_utils::get_cmd_line_string(&alevin_quant_cmd);
559566
info!("cmd : {:?}", alevin_quant_cmd);
560567
let input_files = vec![gpl_output.clone(), setup.t2g_map_file.clone()];

tests/snapshots/cli-help/simpleaf_multiplex_quant___help.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ Quantification Options:
103103
[possible values: cr-like, cr-like-em, parsimony, parsimony-em, parsimony-gene,
104104
parsimony-gene-em]
105105

106+
--small-thresh <N>
107+
Cells with fewer than this many reads are resolved by alevin-fry's tiny-cell fast path,
108+
which applies `cr-like` (winner-take-all) semantics regardless of `--resolution`. Pass 0
109+
to resolve every cell with the requested strategy.
110+
111+
Left unset, alevin-fry's own default applies. Requires alevin-fry >= 0.17.1; earlier
112+
versions parse the option and ignore it.
113+
106114
Piscem Mapping Options:
107115
--skipping-strategy <SKIPPING_STRATEGY>
108116
The skipping strategy to use for k-mer collection

tests/snapshots/cli-help/simpleaf_quant___help.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ UMI Resolution Options:
139139
[possible values: cr-like, cr-like-em, parsimony, parsimony-em, parsimony-gene,
140140
parsimony-gene-em]
141141

142+
--small-thresh <N>
143+
Cells with fewer than this many reads are resolved by alevin-fry's tiny-cell fast path,
144+
which applies `cr-like` (winner-take-all) semantics regardless of `--resolution`. Pass 0
145+
to resolve every cell with the requested strategy.
146+
147+
Left unset, alevin-fry's own default applies. Requires alevin-fry >= 0.17.1; earlier
148+
versions parse the option and ignore it.
149+
142150
Output Options:
143151
--anndata-out
144152
Generate an anndata (h5ad format) count matrix from the standard (matrix-market format)

0 commit comments

Comments
 (0)