Skip to content

specs/017 + 018: loop pool (refuted) and derived-edge welds (generator fix adopted; held-out +173) - #58

Merged
adamjohnwright merged 21 commits into
mainfrom
feat/018-derived-edge-loops
Sep 20, 2026
Merged

adamjohnwright merged 21 commits into
mainfrom
feat/018-derived-edge-loops

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

Loops: the pool (specs/017, refuted) and the derived-edge welds (specs/018, generator fix adopted)

Replaces #56 and #57, which GitHub closed when their base branch (#54) was merged and deleted. Same commits, rebased onto main by merge; every commit is in order (spec → plan → pre-registration → implementation → scoring → review errata).

specs/017 — loop as a conserved pool: recorded negative result

DS_SCC_METHOD=pool | pool_parity | pool_all, default off and bit-identical. Pre-registered, measured on the production catalog on both axes, refuted by its own rule: pool −53 held-out (it rails small loops the fixed point held still); pool_all −326 held-out, −17 experimental, AKT cases 100 → 2. Traced mechanism: product-over-entries is AND applied to alternative routes (RAS isoforms in one GTPase cycle). Two adversarial reviews folded in (baseline error corrected before scoring; pool ≡ pool_parity on this catalog; entry-test noise fixed; mid-run edit shown value-neutral by a clean re-run). test/test_loop_pool.jl 81.

specs/018 — where our loops come from

Diff against MP-BioPath's hand-curated networks (bipartite join on dbIds): they keep the reaction backbone inside our cycles (48–64% present) and never carry our derived classes. Census: removing assembly + depletion edges takes TP53's component 836 → 34, DSB 1,127 → 126. Cause (Adam's diagnosis): boundary expansion reused a leaf node the root complex itself reaches downstream — 1,994 of 2,077 cycle-carrying assembly edges. Fixed in the generator (reactome/logic-network-generator#91, targeted after review: refuse only downstream nodes; edge multisets identical to before in all 92 pathways, +336 fresh leaf nodes).

Regenerated catalog, production solver, no rule: 84.71% / mF1 0.8137 (from 83.47 / 0.8009); held-out +173 conditioned (p < 1e-4), tuning +109, false change 1,437 → 1,150, AKT 100/102, experimental +3, Mitotic G1 −4. Stated plainly: 94% of the held-out gain is DSB Repair and 187 of 199 fixes are DOWN → NORMAL with truth NORMAL. Adopted on exact correctness grounds; the accuracy gain is one pathway's.

Solver rule in this PR: DS_SCC_BREAK_ROLES=<catalyst|assembly|depletion> — the measured record (assembly alone: +214 held-out on the welded catalog; ≈ 0 on the fixed one, i.e. absorbed by the generator fix). Default off, bit-identical (verified on TP53 and DSB networks against the base). Review fixes: closures read the entry state on the pool/flat/influence paths; self-loops are not closures. test/test_scc_break_roles.jl 56.

Re-evaluation of every welded-era A/B on the fixed catalog: eight verdicts stand (elasticity is +29/−85 with or without the welds — it damps the real MDM2–TP53 loop; jacobi, dedup, catalyst-break, own-product not adoptable; depletion floor and hill_sat right; assembly rule redundant); sharing becomes adoptable on structure (accuracy churn-sized); composition edges become clearly harmful (they re-weld: DSB −241).

Records

specs/017-loop-pool/, specs/018-derived-edge-loops/ (spec, plan, tasks, research with P1–P8 pre-registered and scored, two review errata sections each), CI runs the new assertion files, CLAUDE.md test table and knob list updated.

🤖 Generated with Claude Code

adamjohnwright and others added 21 commits September 19, 2026 21:51
Adam's proposal: a strongly-connected component reads the product of its
external entry folds instead of iterating its gain-1 internal edges. Two
variants for internal negatives (fall back vs sign parity); default
bit-identical; measured on both axes with predictions committed first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…served pool (specs/017)

A cyclic component is resolved without iteration: every member reaction is
evaluated with its in-component inputs at baseline (external fold), the entry
folds multiply into one pool fold (0 absorbs, capped at 100x), and every
non-pinned member reads its own baseline x that fold. Entry factors multiply in
sorted order so the result is bit-identical under relabelling.

  pool         a component with any internal inhibitor/depletion edge falls back
               to the damped fixed point
  pool_parity  each member reads each entry's fold to the power +-1 by the
               parity of negative edges on the internal path; inconsistent signs
               (an odd negative cycle = genuine negative feedback) fall back
  pool_all     pool regardless; an internal negative contributes nothing

Finding while building the fixtures: a cycle with exactly one negative edge
(MDM2 -| TP53 -> MDM2) IS an odd cycle, so parity cannot pool the AKT -> TP53
case the spec named; only pool_all pools the giant components, in the
known-wrong direction for that case. Recorded in the spec's results.

Pooled / iterated / fallback counts are reported in SolverResult.diagnostics
and in an additive `scc` object on the API solve response (docs/API.md), so an
arm cannot silently measure the old solver. Default fixed_point is untouched
(asserted bit-for-bit). test/test_loop_pool.jl: 77 assertions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he arms

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…stration

Two independent reviewers (solver diff; methodology), findings verified before
acting. The pre-registration's baseline for the AKT cases was wrong (100/102,
not 2 -- the 2 was the shared catalog); P3 restated. pool == pool_parity on
this catalog (0 of 60 negative components are sign-balanced). P1 failed for
pool: held-out -53, the broken cases at exactly 0 / 100 / 80 where the fixed
point read baseline -- the pool rails loops the old solver held still.

Traced with the benchmark's gene resolution: HRAS KO in Signaling by MET lands
in a 19-node RAS GTP/GDP recycling component that carries the other isoforms'
routes; the fixed point leaves them at baseline (OR redundancy), the pool's
product-of-entries with 0 absorbing zeroes all 19. AND semantics applied to
alternatives; the mechanism behind MET/SCF-KIT/DAP12.

Code: entry detection uses a 1e-9 relative tolerance (hill_sat's baseline
noise made 97% of member reactions "entries"); the rule's measured
consequences documented in pool_component!; tests tightened (81); file added
to CI and to the CLAUDE.md table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…inism, loop pool)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…dge census (findings)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rry loop signal

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…arry loop signal (specs/018)

An edge of a listed role (catalyst | assembly | depletion) whose source and
target share a strongly connected component is a recycling closure: it keeps
its feed-forward meaning (read at the component-entry value through `supply`)
but feeds nothing back around the cycle, and components are recomputed without
those edges so a component welded together by our own derived edges falls
apart into its reaction-level cycles. Motivation measured in research.md:
removing assembly + depletion edges takes TP53's component from 836 nodes to
34 and DSB's from 1,127 to 126; MP-BioPath's hand-curated networks keep the
reaction backbone inside our cycles and essentially never carry these classes.

Default (unset) is bit-identical; the legacy DS_SCC_BREAK_CATALYST is
unchanged. Closure counts per role and the component census before/after are
reported in diagnostics and the API `scc` object. test/test_scc_break_roles.jl:
26 assertions; full suite green. Pre-registration P1-P5 committed here, before
the arms.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e fails); pre-register P6 assembly-only

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…odes) and pre-registered P7

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…T cases kept, DSB-concentrated; churn clause fails

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…22, tuning +81, DSB +210, AKT 98); adopted on correctness grounds

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ixed catalog

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… to adoptable, composition reverses to clearly harmful

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…l regression; DOWN-recall; dropped subsets; sharing churn) and pre-registered P8 for the targeted fix

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…self-loops are not closures; exact census invariance test

A closure activator evaluated without `supply` read the TARGET's baseline --
on the pool path, the flat solve, the final residual and influence scores,
where under assembly-limiting it zeroed every other input's influence. Those
paths now pass the live state (the entry value). Direct self-loops are no
longer marked closures. The FR-006 test asserts the closure set and component
census exactly under relabelling and edge reversal, and iterated remnants to
solver tolerance. `assembly` covers composition edges (documented).
test_scc_break_roles.jl: 56; all assertion files green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ed, 94% DSB; Mitotic G1 -4; exact correctness)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@adamjohnwright
adamjohnwright merged commit 50839f5 into main Sep 20, 2026
4 checks passed
@adamjohnwright
adamjohnwright deleted the feat/018-derived-edge-loops branch September 20, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant