feat(ftinit): opt-in deterministic extraction (strict gap + canonical optimum) - #85
Draft
edkerk wants to merge 2 commits into
Draft
feat(ftinit): opt-in deterministic extraction (strict gap + canonical optimum)#85edkerk wants to merge 2 commits into
edkerk wants to merge 2 commits into
Conversation
… optimum) The ftINIT MILP is degenerate: many reaction subsets reach the same score optimum and the solver returns an arbitrary one. This is reproducible for a fixed solver build + seed but fragile to the Gurobi version or platform, so a downstream metric such as gene essentiality can drift on solver noise rather than on the model. Measured on Human-GEM/DLD1: changing only the solver seed moves ~125 of 7766 kept reactions and flips 5 essential-gene calls. Add two opt-in flags (default off, exact RAVEN behaviour preserved): - strict_gap: one near-proven-optimal solve per step instead of RAVEN's loose absolute-gap escalation, whose final near-zero-objective run accepts an arbitrary within-gap incumbent. - canonical: a lexicographic phase 2 that, holding the score objective at its optimum, minimises the id-ordered count of kept reactions, selecting the unique sparsest optimum instead of an arbitrary tie-break. Threaded through run_ftinit / _solve_step / ftinit. Tests cover degenerate-tie resolution, no regression on the unique-optimum oracle, and staged stability.
edkerk
marked this pull request as draft
July 21, 2026 01:58
… biological accuracy
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.
Opt-in deterministic ftINIT extraction
The ftINIT extraction MILP is highly degenerate: many reaction subsets reach the same
score optimum, and the solver returns an arbitrary one.
Threads=1+ a fixedSeedmakethat choice reproducible for a fixed solver build, but not unique: a different Gurobi
version or platform lands on a different optimum. Downstream that surfaces as
gene-essentiality predictions that drift for reasons unrelated to the model.
Measured on Human-GEM v2.0.0 / DLD1 (Gurobi 13, Threads=1): changing only the solver
Seedfrom 1234 to 7 moves 125 of ~7766 kept reactions and flips 5 of ~281essential-gene calls (PC, ACADVL, DUT, ALDH3A2, RDH5).
What this adds
Two opt-in flags on
ftinit()(and the underlyingrun_ftinit/fill_tasks),both default off, so the existing behaviour is byte-for-byte unchanged:
strict_gap— one solve per step proven to a fixed absolute gap (0.05, below the0.1 reaction-score granularity), replacing the loose relative-gap escalation whose
final near-zero-objective run otherwise accepts an arbitrary within-gap incumbent. A
relative gap is meaningless on that step; an absolute one is optimal at any scale.
canonical— a best-effort lexicographic phase 2, applied to both each extractionstep and the task gap-fill: hold the primary objective at its optimum, then minimise the
count of kept/added removable reactions (parsimony), then their summed id rank. Over the
binary (removable) reactions only, so the objectives are integer and provable with a
cheap absolute gap below 1. Selects a stable, near-unique optimum instead of a solver
tie-break.
Both are guarded by a solution-availability check (
_has_solution): a solve that ends atthe time limit with no incumbent now raises a clear error (or, for the canonical phase,
falls back to the primary optimum) rather than throwing an opaque
AttributeErroron anull primal.
Tests
tests/test_init_ftinit.py,test_init_pipeline.py,test_init_taskfill.py: adegenerate-tie model (two interchangeable reactions) is resolved to the lower-id one
deterministically; the flags are no-ops on the unique-optimum oracle (T0001) and are
run-to-run identical. Full suite green (852 passed).
Empirical results (Human-GEM v2.0.0 / DLD1) — and an important caveat
Measured seed 1234 vs seed 7:
The flags reduce the reaction-level swing as intended, but on this benchmark they did
not improve — and actually worsened — the gene-essentiality determinism (5 -> 19
flips). Two compounding reasons: (1) the 34 residual reactions are all GPR-less (mostly
transport), so the reaction metric is a poor proxy, and (2)
canonicalminimises the keptset, producing sparser models with more essential genes (283/296 vs the baseline
281/278) that are more sensitive to the residual alternative-pathway degeneracy, which
propagates to enzymatic genes' essentiality through connectivity.
strict_gapalso failsto prove the genome-scale optimum in reasonable time (a build hit ~67 min of timeouts and
fell back to arbitrary incumbents), adding noise.
Conclusion: these are a correct, tested, opt-in tool for a more parsimonious and
reproducible extraction, but they are not a fix for gene-essentiality determinism and
can make it worse. For run-to-run essentiality reproducibility the reliable lever is
pinning the solver stack (raven-toolbox commit +
gurobipyversion) so a fixed stackgives bit-identical results. Posting these numbers so the trade-off is on the record;
marking the PR draft pending that discussion.