Skip to content

rm: run the Arrhenius rules RuleMonkey 3.11.0 misread (#1058) - #1221

Merged
wshlavacek merged 1 commit into
mainfrom
fix/1058-lift-rulemonkey-energy-refusal
Oct 23, 2026
Merged

wshlavacek merged 1 commit into
mainfrom
fix/1058-lift-rulemonkey-energy-refusal

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

Closes #1058.

RuleMonkey 3.12.0, vendored in #1220, gives every Arrhenius rule it runs BNG2's rate. Where expanding a rule at load is exact, it expands it. Everywhere else it counts dG at each firing, the way BNG2's network counts it per reaction (richardposner/RuleMonkey#109). The shapes it cannot run, it refuses itself, naming the rule and the reason. That leaves bngsim's own gate with nothing to catch, so this PR removes it.

What changes

  • Removed from python/bngsim/_network_free_checks.py:

    • refuse_rulemonkey_energy_misreads and rulemonkey_energy_misreads;
    • the helpers only they used: _arrhenius_centers, _expanded_exactly, _bond_partners, _component_names, _plain_bond_pattern, _graph, _components and the Site alias;
    • the module docstring's paragraph on them.

    Their calls in Simulator and RuleMonkeySession.__init__ are gone too. _local and _children stay, because other checks use them.

  • Tests (python/tests/test_rulemonkey_arrhenius.py): test_rm_refuses_an_energy_rule_it_would_misread is replaced by value tests on all fifteen misread/ models. Each replicate gets a fresh Model or session: 40 for rm and the session, 200 for SSA.

    • test_energy_context_fires_at_bng2s_closed_form: method='rm', a RuleMonkeySession, and network SSA on the .net, each checked against the closed form in the model's .bngl header. Each observable is binomial(1000, exp(-k t)). Two stay at 1000 because BNG2 never fires the rule there: rule_context's s~0 complexes and ringopen's open chain. The SSA case checks the closed form itself.
    • test_energy_context_matches_network_ssa: rm against network SSA, a two-sample z test at each time point.
  • New model tests/data/nfsim/arrhenius/state_pattern, generated with BNG2 2.9.3:

    • A reversible binding rule with the energy pattern A(b!1).B(a!1,s~1), and 100 each of A, B(s0) and B(s1).
    • Detailed balance gives the exact stationary means: AB0 = 22.33 and AB1 = 68.20. If the state pattern were dropped, both would be 39.33.
    • rm settles on those means at t = 5 and matches network SSA along the way. Network SSA settles on them too.
  • Refusal test: test_rulemonkey_names_an_energy_rule_shape_it_refuses_itself now matches RuleMonkey's own message, which names the rule: rule '_R1' is outside the shapes RM runs: it changes a state.

  • Docs: the "Arrhenius (energy) rules" section of docs/user-guide/network-free.md now says:

    • what rm runs;
    • what RuleMonkey refuses;
    • one case RuleMonkey reads differently from BNG2. Two unidirectional mirror-image rules, X and _reverse_X, with the same phi and Ea0 parameters write the same XML as one reversible rule, and RuleMonkey gives the second rule 1-phi.
  • changelog.d/1058.fixed.md.

Shapes RuleMonkey still refuses

None of the fifteen misread models is one of them: all fifteen load and run on 3.12.0. The shapes are:

  • a state change;
  • a homodimer;
  • a ring closure;
  • two bonds;
  • include/exclude constraints;
  • a bond that can reach a .-joined energy pattern.

RuleMonkey refuses each of these by name, and state_change covers that path.

Verification

  • Sensitivity. On 3.12.0 I swapped each closed-form rate for the misread one, the rate with the context dropped or misapplied. The closed-form check then fails on every observable the misread changes, at |z| from 50 to 515 on rm's runs.
  • RuleMonkey tests. The 18 files that drive RuleMonkey: 496 passed, 3 skipped (448 before). This is after rebasing on ff817e95 (ssa: carry what each firing rounds away, and read samples at its exact time (#1059) #1075's SSA change) and rebuilding.
  • Test floors. The full suite grows by 48, well inside ci/test_floors.env's bounds, so no floor changes.

RuleMonkey 3.12.0 (vendored in #1220) gives every Arrhenius rule it runs
BNG2's rate: it expands a rule at load where that is exact, and
elsewhere counts dG at each firing as BNG2's network does per reaction
(richardposner/RuleMonkey#109).  Shapes it cannot run, it refuses
itself, naming the rule and the reason.  So bngsim's own gate,
refuse_rulemonkey_energy_misreads, has nothing left to catch.

Deleted from _network_free_checks.py: refuse_rulemonkey_energy_misreads,
rulemonkey_energy_misreads, and the helpers only they used
(_arrhenius_centers, _expanded_exactly, _bond_partners,
_component_names, _plain_bond_pattern, _graph, _components, Site), with
the module docstring's paragraph on them.  Their calls in Simulator and
RuleMonkeySession.__init__ are gone too.  _local and _children stay;
other checks use them.

All fifteen misread/ models load on 3.12.0.  None is a shape RuleMonkey
still refuses (a state change, a homodimer, a ring closure, two bonds,
constraints, a bond that can reach a '.'-joined energy pattern).  So
test_rm_refuses_an_energy_rule_it_would_misread is now three value
tests per model:
- method='rm', a RuleMonkeySession, and network SSA on the .net,
  each against the closed form the model's .bngl header gives: each
  observable is binomial(1000, exp(-k t)), or fixed at 1000 where BNG2
  never fires the rule (rule_context's s~0, ringopen's open chain);
- method='rm' against network SSA, two-sample z per time point.
Each replicate gets a fresh Model or session, 40 for rm, 200 for SSA.
On 3.12.0, swapping each closed-form rate for the misread one, the rate
with the context dropped or misapplied, fails every observable the
misread changes, at |z| from 50 to 515 on rm's runs.

New model state_pattern: a reversible binding rule with the energy
pattern A(b!1).B(a!1,s~1), 100 each of A, B(s~0) and B(s~1),
generated with BNG2 2.9.3.  Detailed balance gives the exact
stationary means, AB0 = 22.33 and AB1 = 68.20; dropping the state
would put both at 39.33.  rm settles on them at t = 5 and matches
network SSA along the way, and network SSA settles on them too.

test_rulemonkey_names_an_energy_rule_shape_it_refuses_itself now
matches RuleMonkey's own message naming the rule ("rule '_R1' is
outside the shapes RM runs: it changes a state").

The network-free guide's "Arrhenius (energy) rules" section says what
rm runs, what RuleMonkey refuses, and one case RuleMonkey reads
differently from BNG2: two unidirectional mirror-image rules X and
_reverse_X with the same phi and Ea0 parameters write the XML of one
reversible rule, and the second gets 1-phi.

The 18 files that drive RuleMonkey: 496 passed, 3 skipped (448 before).
@wshlavacek
wshlavacek merged commit 4bac972 into main Oct 23, 2026
22 checks passed
@wshlavacek
wshlavacek deleted the fix/1058-lift-rulemonkey-energy-refusal branch October 23, 2026 05:01
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.

Lift method='rm''s refusal of Arrhenius rules RuleMonkey misreads, once richardposner/RuleMonkey#109 is fixed and vendored

1 participant