Skip to main content

Module rules

Module rules 

Source
Expand description

Pipeline rules: what each method requires, recommends and discourages, with sources.

Many methods only work as intended with certain other methods, settings or values (the pipeline their authors ran them in), and some combinations are known to fail. This module publishes that knowledge as data, so a host can check a configuration and offer the change instead of letting a user silently run something a method was never meant for.

A Rule reads:

when every Condition in Rule::when holds, then Rule::setting should pass Rule::expect (one of a set of values, none of a set, or a range), with Rule::default as the value to apply, at a Strength, for a Rule::reason, citing a Rule::source.

evaluate checks a configuration (RuleInput) against every rule in RULES, given what is known about the scan (ScanFacts), and returns a Finding for each rule whose conditions hold and whose setting does not pass. RULES is the catalogue; both it and the findings serialise to JSON (serde::Serialize) for hosts that are not written in Rust.

§Decisions

  • No source, no rule. A rule says “the method’s reference pipeline does this” (the authors’ code, file and line, or their paper) or “QSM.rs measured this” (and the measurement is recorded in that method’s QSM.rs docs). It never encodes a preference. Where the reference is ambiguous, there is no rule; the open question is recorded in the method’s docs instead.

  • Scoped to the triggering choice. The first condition in Rule::when is the choice that triggers the rule (usually a method being selected), and the rule is about what that choice needs. “MEDI recommends PDF” fires only when MEDI is chosen, never as a general statement about background removal. This keeps two rules from disagreeing about one setting except where two chosen methods genuinely want different things, and then the host sees both findings with their triggers.

  • Two kinds of rule (Rule::kind, a Kind; decided 2026-11-04). A host must be able to tell “this is what the method’s authors did” from “this is better, for this reason”, because only the second is advice:

    • Kind::Reference, the original authors’ setting: their code, paper or README does or instructs it, and nobody has shown that it is needed or better. Following it reproduces their pipeline; departing from it is a legitimate choice. E.g. QSMART’s bet2 -f 0.2 in brainmask.m, MEDI’s region-growing unwrapper after its complex fit, a network trained on V-SHARP fields, or a toolbox default such as Chisep_script.m’s Tukey window, even where the code’s comment calls it a “Recommendation”.
    • Kind::Reasoned, a reasoned recommendation: there is a stated reason why the setting is needed or better (the method cannot run, a combination is known to fail, the authors explain why, or QSM.rs measured it), and the reason is in Rule::reason. E.g. HEIDI’s LSQR seed (the cone fill keeps everything outside the cone), PDF’s n_cg (measured).

    The kind is fixed by the Strength: Strength::Reference is the reference kind, every other strength is reasoned. It is one field rather than two so that the two cannot disagree. The JSON form carries both ("kind" and "strength").

  • Strength, and the basis it rests on (Rule::basis, a Basis):

    Precedence is Required > Recommended > Discouraged > Reference (Strength: Ord). A reason outranks a bare reference choice; a recommendation outranks a discouragement because a recommendation names the value to use, while a discouragement only names one to avoid. This module reports every finding and leaves resolving them to the host.

  • Intended front-end handling (QSMxT/QSMxT#419, astewartau/qsmbly#194; 2026-11-04):

    • Required: keeps the host’s “forces” behaviour; the run does not proceed as configured.
    • Recommended and Discouraged (reasoned): offered as a change, labelled as a recommendation with its reason, with the recommended value preselected; a host’s “accept recommendations” applies them. A badge on the setting afterwards.
    • Reference: shown (e.g. when its method is chosen), labelled as the original authors’ setting, but never preselected and never applied by “accept recommendations”; a host applies it only when the user picks it or asks for the authors’ settings explicitly. Recorded in the run record, and listed in the methods’ Note section in a softer form (“differs from the reference pipeline”), separately from departures from recommendations.
  • Rules that restate a default. Since #293 most methods’ own defaults are their reference values, so many Reference rules (STAR’s padding and TE, RESHARP’s radius and iterations, iLSQR’s iterations and padding, QSMART’s iLSQR settings, the reference echo times) hold on the default config and fire only when a user departs from it. They are kept: they say what the departure is from and why, which the methods Note needs. A host that also shows “differs from default” should show one or the other for these, not both.

  • Data, not code. Conditions and expectations are values (Condition, Test, Value) over named settings and scan facts, not closures, so the JSON form says everything the Rust evaluation does. The only code is current (a setting’s value in a config) and ScanFacts::fact (a fact’s value for a scan).

  • Settings are QSM.rs’s own. A setting is a dotted path into QSM.rs’s config types: masking.*, field_mapping.*, bg_removal.*, inversion.* (QsmPipelineConfig), separation.* (SeparationConfig) and segmentation.* (SynthSeg’s SynthSegParams). Enum choices are named by their id() (e.g. InversionAlgorithm::id). A host maps these to its own config paths. SETTINGS lists every path a rule may use.

  • Settings not in play have no value. A setting of a stage that does not run (background removal ahead of a total-field inversion; field mapping ahead of a wrapped-phase one), or of a method that is not selected anywhere (HD-BET’s parameters with no HD-BET step in the mask recipe), has no current value, and a rule on it does not fire. A setting with several values (one per mask section) triggers a condition if any value does, and passes a rule’s expectation only if every value does.

  • Scan facts may be unknown. A host checks a config before it has seen data. A condition on a fact that is not known (None in ScanFacts) does not hold, so scan-dependent rules stay silent until the scan is known; evaluate again at plan time with the scan’s facts. Rule::needs_scan says which rules those are.

  • Values with scan-relative meaning are stated in the unit the setting uses (Unit): millimetres, voxels, bytes. A rule never converts mm to voxels itself; where that matters it conditions on the voxel size.

  • Inclusive ranges. Test::Range bounds are inclusive. Byte and voxel counts are integers, so “more than the heap” is min: heap + 1.

Structs§

Finding
A rule that fires on a configuration: its conditions hold and its setting does not pass.
Rule
A rule: when Self::when holds, Self::setting should pass Self::expect.
RuleInput
The configuration a host checks: the QSM pipeline, and the optional stages it runs.
ScanFacts
What is known about the scan and the host. Every scan fact is optional: a host checks a config before it has seen data, and a rule conditioned on an unknown fact does not fire.

Enums§

Basis
What a rule rests on.
Condition
One condition of a rule.
Current
The current value of a setting, owned (a config’s vectors are not 'static).
Fact
A fact about the scan or the host, as opposed to a setting.
Kind
Whether a rule is the original authors’ setting or a reasoned recommendation (see the module docs, Two kinds of rule). Fixed by the rule’s Strength (Strength::kind).
Manufacturer
A scanner manufacturer, as far as a rule distinguishes them.
Strength
How strongly a rule applies. Ordered Reference < Discouraged < Recommended < Required, so max is the finding that takes precedence on a setting (see the module docs, Strength).
Target
Where the pipeline runs. A WASM host has a 32-bit heap (WASM32_HEAP_BYTES).
Test
A test on a value: what a condition requires of a setting or fact, or what a rule expects of its setting.
Unit
The unit of a rule’s setting, for numbers and vectors.
Value
A setting’s (or a scan fact’s) value. Serialised untagged: true, 3.0, "pdf", [8.0, 8.0, 20.0].

Constants§

BET 🔒
CHISEP_TUKEY_SIEMENS_GE
The taper ratio the χ-separation toolbox recommends for Siemens and GE data.
CHI_SEPNET_TOOLBOX_ROUTE 🔒
χ-sepnet’s toolbox route (ChiSepNetRoute::Toolbox).
CHI_SEP_TOOLBOX 🔒
CHI_SEP_TOOLBOX_ITERATIVE 🔒
The χ-separation toolbox’s iterative methods (χ-sepnet has two routes; only its toolbox route is the toolbox’s pipeline).
COMPLEX_FIT 🔒
DECOMPOSE_UNWRAP_FROM_T 🔒
The field strength from which decompose.unwrap-7t and decompose.chi-bound-7t fire: 7 T scanners, including those that report 6.98 T (Siemens), and nothing at 3 T or below.
FANSI_FAMILY 🔒
HEIDI_CONE_SOURCE 🔒
Upstream’s error when a truncating seed’s threshold is above the cone’s.
HEIDI_DEFAULT_CONE 🔒
HEIDI’s default cone threshold: the heidi.*-cone rules are exact for it; heidi_seed_problem checks any.
HEIDI_NO_LOCAL_FIELD 🔒
Inversions with no local field to fill the cone from (the total-field and phase methods).
HEIDI_ON 🔒
HEIDI’s cone fill is on (inversion.apply_heidi): the trigger of the heidi.* rules.
HEIDI_REGULARISED_SEEDS 🔒
Seeds whose regularisation outside the cone HEIDI would keep.
HEIDI_UNREGULARISED_SEEDS 🔒
Seeds whose region outside the cone is near data-consistent (the HEIDI module docs, Any seed), but for LSQR (the reference) and RTS (heidi.seed-rts).
LAPLACIAN 🔒
LBV 🔒
MULTI_ECHO 🔒
ON_WASM 🔒
PDF 🔒
PDF_SUGGESTED_N_CG 🔒
The CG iteration count pdf.cg-iterations suggests (measured; see PdfParams::n_cg).
QSMNETME 🔒
QSMnetME: the χ-separation toolbox’s QSM network, trained on that toolbox’s pipeline.
ROMEO 🔒
SETTINGS
Every setting path a rule may name, with what it is. current reads each of them.
STI 🔒
TUKEY_PHILIPS_REASON 🔒
TUKEY_SIEMENS_GE_REASON 🔒
TUKEY_SOURCE 🔒
VSHARP 🔒
WASM32_HEAP_BYTES
The hard ceiling of a 32-bit WASM linear memory: 4 GiB. A step whose estimated peak exceeds it cannot run in a browser at all.
WEIGHTED_AVG 🔒

Statics§

RULES
The catalogue. Each rule’s reason and source are also recorded in the docs of the method it is about (its “Reference pipeline” section). Rules that depend on work not yet merged are listed in issue #289, not here.

Functions§

bg 🔒
check
Check one rule: Some finding when its conditions hold and its setting does not pass.
current
The current value(s) of setting in input: empty when the setting is not in play (its stage does not run, or its method is not selected), one value per place it is set otherwise. None for a path that is not in SETTINGS.
discourage 🔒
A discouraged choice: setting should be none of avoid; default replaces it.
echo_time_id
Stable id of an EchoTime choice (Seconds is "seconds", whatever its value).
evaluate
Every finding for input on a scan with facts, in RULES order.
fact_at_least 🔒
fact_at_most 🔒
hd_bet_steps 🔒
Every HD-BET step in the mask recipe.
heidi_truncation 🔒
heidi.<method>-cone: a truncating seed’s threshold must not be above HEIDI’s cone threshold.
holds 🔒
inv 🔒
manufacturer 🔒
measured_better 🔒
What QSM.rs measured to be better (recorded in the method’s docs).
method 🔒
one_of 🔒
A choice of strength on basis: setting should be one of values, the first being the default.
recommend 🔒
A reasoned recommendation the authors make: they say why it is needed or better, and reason says it.
reference 🔒
The original authors’ setting: what their code or paper does, without saying it is needed or better.
reference_stated 🔒
The original authors’ setting as their README or a code comment instructs it (“do X”, “we recommend X”), without saying why it is needed or better: still Kind::Reference.
sep 🔒
weight_id
Stable id of a weighted average’s per-echo weight.