Repository navigation
Price Arrhenius rules with any connected context at BioNetGen's rate (#109) - #117
Merged
Merged
Conversation
…109) The load-time expansion prices an Arrhenius rule exactly only for a narrow class of energy patterns and a bare reactant pattern; #116 refuses the rest. They now run instead. Each runs as itself, with its own reactant and product patterns, and its dG is counted at each firing as BioNetGen's network counts it per reaction. BioNetGen weighs an energy pattern in a species by matches over automorphisms (EnergyPattern.pm), both counted by its own matcher (a `!+` lands on a `!?`, a `~?` needs a state), and dG sums G times the change in weight. For a connected pattern, the matches a bond changes are those that put a pattern component on one of the bond's two components, so a firing's dG is the change in those anchored matches. It is counted on the complexes the bond joins or parts, with a virtual bond made or broken. energy_match.hpp holds a matcher over any graph view (the pool, a pattern for its automorphisms, a toy graph in tests) and the dG helpers. The rule rides the local-rate machinery, as a Function (unbinding) or FunctionProduct (binding) rate law with per-molecule weights: - Unbinding: a molecule's weight is the largest exact rate among its bonds of the rule's kind. - Binding: dG is cut into each end's own term plus, per pattern bond of the rule's kind, a product of the two sides' matches. Where one side is the cut site alone the product is exact. A pattern with context on both sides is bounded at the far side's most matches where that raises the rate; where that bound could inflate the rate past e, the rule is split into one rule per far-side class, up to 8, each keeping only the pairs in its class, which is exact; a bound left larger is reported with a warning. The engine thins each drawn firing to rate / weight. A binding pair in one complex never fires, as BNG2 applies a two-pattern rule, under -no-bscb too. set_param re-resolves phi, Ea0 and the energies. The expansion is kept where it is exact, and such models run byte-identical. Still refused: state changes, two bonds, ring closure, homodimers, include/exclude constraints, and an energy pattern joined only by `.` that the rule's bond can reach and change. Tests: - arrhenius_context (ode) and arrhenius_context_bind (ssa) pin 29 shapes against BNG2. Main misses them by up to 61 standard errors, and #116 refuses them. - energy_match_test pins the counting against BioNetGen's weights, automorphisms, cuts and bounds. - arrhenius_context_test pins what is still refused, -no-bscb, and an AddBond written either way round. - arrhenius_set_param_test pins overrides on the exact path, including two that change the rate. Of 47 mutations, 40 are killed; the other seven are equivalent or change only the thinning rate. 251 of 259 XMLs under tests/ run byte-identical to main; the other 8 are the new models and fixtures and the PR 1 change. Measured cost: 20-60 us per event against 2.5 us expanded, on a chain-forming bivalent system.
wshlavacek
changed the base branch from
fix/arrhenius-context-refusal
to
main
October 22, 2026 19:13
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.
Closes #109. Stacked on #116, which refuses every Arrhenius rule the load-time expansion cannot price exactly. This makes those rules run at BioNetGen's rate instead. The base is
fix/arrhenius-context-refusal, so the diff shows this change alone; GitHub retargets it tomainonce #116 merges.What changes
The expansion stays wherever it is exact, and those models run byte-identical. Every other Arrhenius rule now runs as itself, with its own reactant and product patterns, and its dG is counted at each firing the way BioNetGen's network counts it per reaction.
What BNG2 counts. BNG2 weighs an energy pattern in a species by matches / automorphisms (
EnergyPattern.pmupdate; the automorphisms are the pattern matched on itself,SpeciesGraph.pm). Both are counted with BNG2's own component test, under which a!+lands on a!?and a~?needs some state, soL(r!+,r!?)has two automorphisms. A reaction's dG is Σ G × (weight in products − weight in reactants).Why the count is local. For a connected pattern, the matches a bond changes are exactly those that put some pattern component on one of the bond's two components; any other match reads only components the bond leaves alone. So a firing's dG is the change in those anchored matches, counted on the complexes the bond joins or parts, with the bond made or broken virtually. No species, no network.
The matcher.
energy_match.{hpp,cpp}is a matcher templated over a small graph view. The engine runs it on its pool, the automorphism count runs it on the pattern itself, and the unit tests run it on a hand-built graph.Thinning. The rule rides the local-rate machinery, as a
Function(unbinding) orFunctionProduct(binding) rate law with per-molecule weights. The engine draws a firing against those weights, counts the drawn instance's rate, and keeps the firing with probability rate / weight._arr_cKappended, as expanded rules take_arr_vK.-no-bscb.Still refused, with the rule and reason named: a state change, two bonds at once, ring closure, a homodimer, include/exclude constraints, and, off the expansion, an energy pattern joined only by
.that the rule's bond can reach. BNG2 counts such a pattern per complex, so joining or parting two complexes can change it. It is let through for a pattern naming a molecule type no bond can bring near the rule's, and, beside an unbinding written with.(which never parts a complex), for a pattern that asks nothing of the bond's two sites.Verification
arrhenius_context(ode) covers unidirectional unbinding, one column pair per shape. The shapes beyond the issue's are:!?counted twice;.;!+beside!?, and for~?.arrhenius_context_bind(ssa, 1000 reps) covers reversible binding, both halves on the exact path:energy_match_testchecks the counting against BNG2's weights by hand, for the issue's cases and!?C(m, k). It also checks automorphism counts, the binding cut against the direct count, cycles, molecule injectivity and the structural bound.arrhenius_context_testnow pins what is still refused, that the exact path never binds a pair inside one complex under-no-bscb, and that an XML naming its AddBond sites the other way round runs bit-identical.arrhenius_set_param_testruns both new models under no-op overrides. It also runs two overrides that change the answer: the context energy switched off, scored against the bare column, and phi = 0, scored in closed form.!+beside a!?, z = 39 against BNG2's SSA; a~?, z = 23).basicmodels.py29/29.tests/run byte-identical tomainat 2 seeds, stdout and exit code. The 8 that differ are:arrhenius_formula, extended in Refuse Arrhenius rules the load-time expansion cannot price exactly (#109) #116;bivalent,no_energy_patterns,ep_disconnected_apartandep_disconnected_ring;ep_disconnected, which this refuses.Not covered
The reverse-half ambiguity documented in #116 remains. Two unidirectional rules, X and
_reverse_X, that mirror each other and name the same parameters write the same XML as one reversible rule. Telling them apart needs BNG2'swriteXMLto mark reverse halves.