Skip to content

Strict complex-as-node: fix the catalyst/regulator UUID silo#47

Merged
adamjohnwright merged 1 commit into
mainfrom
fix/complex-as-node-silo
Jul 16, 2026
Merged

Strict complex-as-node: fix the catalyst/regulator UUID silo#47
adamjohnwright merged 1 commit into
mainfrom
fix/complex-as-node-silo

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

The bug

A complex that is produced by one reaction and catalyses/regulates another was stored as separate un-unified UUID nodes, severing the curated "complex produced → then catalyses/regulates" chain. Traced concretely: ATM → active p53 → p53:FAS-gene complex (R-HSA-6799810) → regulates FAS expression → FAS was broken (ATM reached ~2000 nodes but not FAS).

Two root causes, both specific to complexes-that-contain-an-internal-EntitySet in the produce→catalyse topology (simple complexes already unify via plain stId):

  1. Phase-2 union-find only merges output@A ↔ input@B; catalyst/regulator roles are never registered/unified.
  2. A complex-with-set got three different node-id encodings (produced = ::variant::{terminal leaves}, bundled-catalyst = plain stId, negative-regulator = ::variant::{direct components}) — so the strings never matched to unify.

The fix

LNG_COMPLEX_AS_NODE (default on): a Complex is one node = its plain stId regardless of internal EntitySets, in both emission encoders. All roles now agree on the plain stId, so produced and regulator occurrences unify. Purely an emission change — the matching-layer cartesian is untouched. Bare EntitySets still expand to OR members. The ::variant:: machinery stays reachable via LNG_COMPLEX_AS_NODE=0 for A/B.

Members remain perturbable through their production reactions, so no real distinction is lost. Connected regulators then combine via the existing multiplicative/hill_log activator aggregation and divide/devspec inhibition.

Verified

  • vs Neo4j: R-HSA-6799810 collapses 3 nodes → 1; ATM/BRCA1/CHEK2/MDM2 all reach FAS.
  • Benchmarks (tuned cutoffs 0.58/1.20, DS_KO_REMAP): experimental 66.43% → 69.85% (RAF +36, WNT +20, Mitotic_Prophase +15, S_Phase +8, ERBB2 +2 — nothing regressed); curator 81.18% → 83.82%. Best of the series, zero regressions.
  • TestComplexAsNode added; full non-DB suite green, ruff + mypy clean, coverage 46.5%.

🤖 Generated with Claude Code

A biological complex that is PRODUCED by one reaction and CATALYSES/REGULATES
another was stored as separate, un-unified UUID nodes, severing the curated
"complex produced -> then it catalyses/regulates" chain (traced: ATM -> active
p53 -> p53:FAS-gene complex R-HSA-6799810 -> regulates FAS expression -> FAS was
broken; ATM reached ~2000 nodes but not FAS). Two root causes, both specific to
complexes-that-contain-an-internal-EntitySet: (1) the Phase-2 union-find only
merges output@A<->input@B, never catalyst/regulator roles; (2) a complex-with-set
got three DIFFERENT node-id encodings across produced / bundled-catalyst /
negative-regulator paths, so the strings never matched to unify.

Fix (LNG_COMPLEX_AS_NODE, default on): a Complex is ONE node = its plain stId
regardless of internal EntitySets, in both emission encoders
(_map_annotated_entity_to_nodes and _decompose_regulator_entity for all three
regulator roles). All roles now agree on the plain stId, so produced and
regulator occurrences unify. Purely an EMISSION change — the matching layer
(reaction_generator cartesian) is untouched. Bare EntitySets still expand to OR
members. The ::variant:: machinery is left in place, reachable via
LNG_COMPLEX_AS_NODE=0 for A/B.

Members stay perturbable via their production reactions (a family member is an
input to the binding reaction that makes the complex), so no distinction is lost
where it is a real reaction participant. The connected regulators then combine
via the existing multiplicative/hill_log activator aggregation.

Verified vs Neo4j: R-HSA-6799810 collapses 3 nodes -> 1; ATM/BRCA1/CHEK2/MDM2
all reach FAS. Benchmarks (tuned cutoffs, DS_KO_REMAP): experimental
66.43% -> 69.85% (RAF +36, WNT +20, Mitotic_Prophase +15, S_Phase +8, ERBB2 +2,
nothing regressed); curator 81.18% -> 83.82%. Best result of the series, zero
regressions. TestComplexAsNode added; suite green, ruff + mypy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@adamjohnwright
adamjohnwright merged commit 288f701 into main Jul 16, 2026
4 checks passed
@adamjohnwright
adamjohnwright deleted the fix/complex-as-node-silo branch July 16, 2026 01:43
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