Skip to content

Edition-2 one-model + condition: simulates the full {action}×{mutant} cross-product but scores only the diagonal — simulate only scored (action, condition) pairs #484

Description

@wshlavacek

Summary

Under edition-2 Mechanism A (ONE model + condition: perturbations, PEtab-aligned), PyBNF
simulates the full {action} × {condition-variant} cross-product but scores only the
diagonal — each experiment's own action under its own condition. For N experiments and
M condition: mutants on one model, that is N×(M+1) simulations to obtain N scored
series. The remaining N×(M+1) − N off-diagonal simulations (e.g. the WT action run under
the n78g mutant → suffix WTn78g) are computed on every objective evaluation and then discarded.

This is the deeper root behind #483. There, the wasted off-diagonal suffixes surfaced as a hard
crash (KeyError: 'WTn78gMEK_pRDS') because the am sampler's output_trajectory bookkeeping
allocates buffers only for the scored diagonal keys but the write loop iterated every raw suffix.
That crash is now fixed (guard the write to allocated keys, ae21d212, closes #483) — but the
redundant work remains, on all job types, not just am.

Why it matters beyond the crash

  • Every job type pays the cost. The de path on the identical model/condition/experiment
    setup runs to completion today, but it still runs the full N×(M+1) simulations per generation
    and simply never references the off-diagonal results. The KeyError was am-specific; the wasted
    compute is not.
  • It scales multiplicatively. For the Miller et al. 2026 MEK-isoform job (N=5 experiments,
    M=4 cell-line condition: mutants) that is 5×5 = 25 simulations per evaluation for 5 scored
    series — a 5× overhead — and it grows with both the experiment count and the condition count.

Proposed direction

Simulate (and emit) only the scored (action, condition) pairs — the diagonal that the
objective and output_trajectory/output_noise_trajectory actually consume — instead of the
full cross-product. This removes both the wasted work and the buffer key-space mismatch at their
source (making the #483 write guard defensive rather than load-bearing).

Caveat — verify the off-diagonal pairs are truly dead work first

Step one should be to confirm nothing quietly depends on the off-diagonal suffixes before pruning
them, so we don't turn a perf win into a silent regression:

  • Cross-condition / cross-model constraints. BPSL .prop/.con constraints (e.g. the
    Miller job's WT.obs < KO.obs) reference scored suffixes, which are all diagonal — but this
    should be checked against Constraint.get_key suffix resolution, not assumed.
  • Any consumer that iterates res.out[model] broadly (trajectory emission, PEtab export,
    diagnostics) rather than by scored data-key.

If any real consumer needs an off-diagonal pair, that pair is by definition part of the scored/used
set and should be added to the diagonal explicitly, not left as an implicit cross-product artifact.

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions