Repository navigation
events: refuse a fixed fire and a moving one on one instant across two batches (#1277) - #1353
Conversation
…o batches (lanl#1277) An event on the clock at a fixed time and one on a state a requested column moves cross on one instant and fire as two batches, a root's slack apart. lanl#49's refusal of two events on one instant whose times move differently was asked of one batch, so it saw neither with the other: S(3) went from 3.75 to 4.25 across k = 1, and the run returned dS/dk = 1.25, one side's slope. Before a fire is recorded, it is now asked against each earlier fire in event_fires: one moved and the other not, within 2·kEventRootSlack· kSwitchInstantUlps·ε·max(|t|,1), and the run is refused, naming both.
|
Thanks. I checked this at 3237402 against our #1326 (#947, head 0e791e3), which also changes how events on one instant are drained. The two don't conflict. There's one thing in the new message, below, that is independent of #1326. Measured on macOS arm64, with three builds: this PR alone, #1326 alone, and the two merged with No conflict with #1326#1326 holds a strict trigger, On the merged build:
1. The refusal names the batch's first event, not the one that movesThis is #1277's model with a strict event beside With
A fix: ask #49's condition before the new block: |
|
A heads-up from #1356, merged as 3df252c. #1356 reads a sensitivity run's row ahead from up to 1e-12 short of a sample, and fires what crosses there. With it on main, one case here fails: The run is still refused, but by #49's same-instant message ("events 'E0' and 'E1' fire at the same instant t=2.500000 but their crossing times move differently … (issue #49)") rather than this PR's. E1's root lies within the instant of E0's clock stop, so the read-ahead fires the two at the row as one batch, and the cross-batch check never runs for that case. Two ways to settle it:
We built main + #1356 + this PR (it merges clean in text): on this PR's test file and #1356's, 1 failed, 72 passed, and every other case passes. One more thing from that build: with this PR in, a clock trigger |
…ving-fire-one-instant
…anl#1277 review) The cross-batch refusal skipped a pair whenever both fires moved, so it caught a fixed fire beside a moving one and nothing else. lanl#1277's model with E0 at `time >= T` and sensitivity_params ["T", "k"] moves E0 by dt/dT = 1 and E1 by dt/dk = -2.5: the order still flips across k = 1, and the run returned dS/dk = 1.25 with no error where ["k"] alone was refused. Within one batch, adopt_tau refuses exactly that. Each kept fire now records its shift per column and the event that moves it. Two fires within the slack are refused unless both are fixed or every column's shifts agree to same_shift's tolerance, hoisted for this: E1 at `q >= T` with q' = 1 beside E0 at T moves by a difference that is 1 to rounding, and still runs (dS/dT = -0.5). The message names the event that moves rather than the first that fired, and says "both move, and not together" for that case. The slack is clock_crossings_at's reach, now one helper. The tests drop the row at 2.5: since lanl#1276 a row there gathers a fire just short of it into its own batch, so "E1 first" at -1e-13 was answered by lanl#49's refusal and the test failed on current main.
Closes #1277.
What was wrong
An event on the clock and an event on the state that cross on one instant fire as two batches, a root's slack apart, when the first is at a fixed time and a requested column moves the second. #49's refusal, of two events on one instant whose times move differently (
fixed_event >= 0 && tau_event >= 0inapply_event_sensitivity_jump), is asked of one batch. It never saw the two together.#1277's model:
S' = 1,q' = k;E0: at (time >= 2.5): S = S + 1;E1: at (q >= 2.5): S = S/2.On main 9a26f5b:
sensitivity_params=["k"]at k = 1 returned dS/dk(3) = 1.25 with no error. That is one side's slope at a jump, not a derivative.The fix
src/cvode_simulator.cpp,apply_event_sensitivity_jump. Before a fire is recorded inevent_fires, it is asked against every earlier fire kept there. If exactly one of the two is moved by a requested column (EventFire::movesagainst this fire'sfire_moves), and the two are within2·kEventRootSlack·kSwitchInstantUlps·ε·max(|t|, 1)of each other, the run is refused. That is the same reachclock_crossings_atgives an event root beside a switch, 2.8e-13 at t = 2.5. The message names both events and which one moves:This is #1277's proposal:
event_firesalready holds each earlier fire and whether it moves.The check matches #49's refusal within one batch: it does not test whether the two events commute.
Tests (
test_event_batch_order_sensitivity.py)test_a_fixed_fire_and_a_moving_one_in_two_batches_are_refused, with the trigger at 2.5 − 1e-13 (E1 first), at 2.5, and at 2.5 + 1e-13 (E0 first). Probing showed 2.5 − 1e-14 lands in one batch, where Forward sensitivity w.r.t. a switch time encoded as an SBML event (event-time sensitivity dt*/dp != 0) #49's own refusal already applies, so the "E1 first" case sits at −1e-13 to reach the two-batch path.test_the_pair_a_millionth_apart_runs, with the trigger at 2.5 ± 1e-6. The order holds across k, and the run matches the closed form dS/dk = trigger/2.test_the_pair_on_one_instant_runs_where_no_column_moves_either. Withsensitivity_params=["a"]on the coincident pair, dS(3)/da = 1.75, the same in either order.Mutations, each caught:
E1 firstcaseonandE0 firstE1 firstandE0 first(1e-13 apart)Measured
fd_sens: the nightly sweep (106 jobs on 53.netmodels, sensitivities against central differences), run on main's core and on this one. Both give 106 PASS, with identical verdicts and no new refusal.