Open
Conversation
Remove parse-time restriction that blocked state variable references in f(cmt), rate(cmt), and dur(cmt) expressions. These expressions now fall through to the default branch which emits correct state variable references. Absorption lag-time (alag) restriction is kept since state-dependent lag is not supported (sort-phase ordering cannot handle it). Addresses RxODE#216 and RxODE#222. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add double *__zzStateVar__ parameter to generated RxRate and RxDur function signatures, and populate state variables from __zzStateVar__ instead of NA_REAL for ode_rate and ode_dur modes. This allows state-dependent rate(cmt) and dur(cmt) expressions to access actual compartment values at runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update t_RATE and t_DUR function pointer typedefs to include a double *y parameter for passing ODE state to rate/duration functions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update getRate and getDur signatures to accept double *y and forward it to the RATE/DUR function pointers. Update updateDur and updateRate to pass yp through to getDur/getRate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace rx->ypNA with op->inits in sort-phase calls to updateDur and updateRate (handleTurnOnModeledDuration, handleTurnOnModeledRate). Using NA state caused state-dependent rate/dur expressions to return NA, triggering badSolve=1. Using initial conditions provides valid non-NA estimates for event ordering. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In handle_evid for EVIDF_MODEL_RATE_ON and EVIDF_MODEL_DUR_ON events, call updateRate/updateDur with the actual solver state (yp) before reading getDoseIndexPlus1. This ensures InfusionRate and infusion end-time use the correct state-dependent values at dose time. Add needSortDefines.h include and forward declarations for updateRate/updateDur to resolve the forward-reference dependency with rxode2parseGetTime.h. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ignature and direct-index bug - Allow alag(cmt) expressions referencing d/dt(state) syntactically by removing the ALAG parse-time restriction in handleDdtRhs - Add double *__zzStateVar__ parameter to generated Lag function signature (matching Rate/Dur/F functions); state vars remain NA_REAL in Lag so state-dep alag expressions produce a clean runtime 'NA lag' error - Update t_LAG typedef from 3-arg to 4-arg (adds double *y) - Update getLag() to accept and forward double *y state pointer - All sort-phase getLag calls pass rx->ypNA so state-dep lag always produces a runtime error (not a crash) - Fix direct-index bug in handle_evid: ind->idx is an ix-array position but updateRate/updateDur expect a direct all_times index; compute _directIdx = (ind->idx >= 0) ? ind->ix[ind->idx] : ind->idx before calling updateRate/updateDur (fixes the li=0.5 regression) - Update getRate in handleSS and getLag in handleSS to pass yp Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
State-dependent f(cmt) is now allowed when combined with modeled rate (rate=-1) or modeled duration (rate=-2) events; update the two expect_error() calls that tested these cases to expect_no_error(). Also update the test description to reflect the current behavior: - rate(cmt)/dur(cmt) depending on state still error (unsupported) - f(cmt) depending on state with fixed rate still errors - f(cmt) depending on state with modeled rate/dur now succeeds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document the new capabilities added in this feature branch: - State-dep f(cmt) with modeled-rate/duration events now allowed - Runtime recomputation of modeled rate/duration with actual ODE state - Sort-phase uses initial conditions instead of NA for better ordering - alag(cmt) expressions using d/dt(state) now syntactically accepted Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Member
Author
|
Needs more work;
|
- Remove TMTIME restriction in parseDdt.h that blocked state variable references in mtime(var) <- expr definitions - Add double *__zzStateVar__ parameter to generated mtime C function so state values are available during model-time computation - Update t_calc_mtime typedef to accept state pointer (double *y) - Move calc_mtime() call in iniSubject to after u_inis/memcpy so initial conditions are populated in ind->solve before use - Pre-copy op->inits to ind->solve when no u_inis but nMtime > 0, ensuring state-dep mtime expressions get valid non-NA initial values - Add test: mtime(t1) <- intestine * 0.01 parses and solves correctly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In handleTurnOnModeledDuration and handleTurnOnModeledRate, use ind->solve (per-subject initial conditions from u_inis) instead of op->inits (global all-subject estimates) when op->neq > 0. When op->neq == 0 there is no ODE state so fall back to op->inits. iniSubject populates ind->solve via memcpy(op->inits) + u_inis before sortInd is called, so ind->solve has correct per-subject values at sort time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Sorted=0 - Add reSortMainTimeline(ind, startI): re-sorts ind->ix[startI..n-1] using ind->timeThread as sort key (wraps timsort SORT macro). - Add recomputeMtimeIfNeeded(rx, ind, yp, nextI): recomputes ind->mtime with current state yp and updates timeThread for changed mtime events; returns 1 if any mtime value changed. - Refactor sortInd to call reSortMainTimeline instead of inline SORT. - In handle_evid at EVIDF_MODEL_RATE_ON / DUR_ON events, set ind->mainSorted = 0 (guarded by ind->idx >= 0 to skip extra doses). - In all 6 solve loops (ind_indLin0, ind_liblsoda0, ind_lsoda0, ind_linCmt0H, ind_linCmt0, ind_dop0): add re-sort trigger at top of event loop and mtime recomputation after handleEvid1/handleSS. The re-sort refreshes timeThread for remaining stop events (using getTime_ to include lag correction) and mtime events before calling reSortMainTimeline(ind, i). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests verify that: 1. State-dependent modeled rate uses per-subject initial state at sort phase, producing different trajectories for subjects with different initial conditions. 2. Runtime re-sort after rate recompute places stop event correctly (no NA output, no solver errors). NEWS entries document the three bug fixes: per-subject initial conditions at sort time, main timeline re-sort after runtime rate/dur update, and mtime recomputation at dose events. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…branch) The `ode_lag` branch in codegen.c previously set all state variables to NA_REAL, causing runtime NA errors whenever a state-dependent alag() expression was evaluated. Merge it with the general else-branch so that Rate/Dur/F/Lag functions all populate state vars from `__zzStateVar__`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Five related fixes for state-dependent rate/duration/alag interaction: 1. updateDur/updateRate: store absolute lagged stop time (lagged_start + duration) in all_times[stop_raw] instead of unlagged (t + duration). This ensures that when alag() and dur()/rate() are both state-dependent, the stop event lands at the correct absolute time. 2. getTimeCalculateInfusionTimes: RATE_OFF/DUR_OFF cases now return getAllTimes() directly (already the absolute stop time) instead of re-applying lag via getLag — which would have double-lagged stop events. 3. getTime__: add RATE_OFF/DUR_OFF guard in update=0 path to likewise return getAllTimes() directly. 4. getTimeCalculateInfusionTimes catch-all: replace rx->ypNA with ind->solve so sort-time lag uses per-subject initial state. 5. handleInfusionItem: replace rx->ypNA with ind->solve for amt>0 and tB computations so constant-alag infusion start/stop times are computed with the per-subject initial state rather than a sentinel NA array. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…igger Three changes across all 6 solve loops and one new helper: 1. refreshLagTimesIfNeeded(): new helper that, when needSortAlag is set, recomputes timeThread entries for remaining bolus/ON dose events using the current state yp via getLag(). Skips RATE_OFF/DUR_OFF (store absolute times) and all EVIDF_INF_RATE events (stop key = lagged_start + f*dur, not raw_stop + lag — handled at sort time via handleInfusionItem). Returns 1 if any time changed; callers set mainSorted=0 to trigger re-sort. 2. xout computation: changed from getTime_(ind->ix[i], ind) (which called getLag with rx->ypNA) to ind->timeThread[ind->ix[i]] (precomputed correct lag-adjusted value from sortInd / runtime refresh). 3. Re-sort trigger: stop event refresh now uses getAllTimes(ind, _raw) instead of getTime_(_raw, ind), since stop events already store absolute lagged times after commit fix(getTime). 4. All 6 loops call refreshLagTimesIfNeeded after recomputeMtimeIfNeeded, setting mainSorted=0 when lag-adjusted times change. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three tests covering the new state-dependent lag functionality: - Sort phase uses actual initial state (not NA) for alag(cmt) <- state expr - dur(cmt) + constant alag: stop event placed at absolute lagged time - Multiple doses: runtime lag refresh produces no NAs across dose events Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document four fixes: - State-dep alag() now uses actual state values (not NA) at sort phase - dur(cmt)/rate(cmt) stop events store absolute lagged times correctly when combined with alag() - xout uses precomputed timeThread values (correct lag at each event) - Runtime lag refresh via refreshLagTimesIfNeeded re-sorts timeline when state-dep lag values change between events Note cache invalidation: cached compiled models must be rebuilt after installing this version. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation 1. Skip evid==3 (reset events): getWh(evid=3) gives cmt=-1, which causes _alag[-1] array underrun (UB) inside RxLag. 2. Zero ind->curShift before the scan loop, matching the sortInd convention. After a reset event curShift = -maxShift; without zeroing, getLag returns rawTime + lag + maxShift instead of rawTime + lag, producing a spurious 'changed' flag and a corrupted event timeline. 3. Set ind->idx = j (the ix[] position) inside the loop before calling getLag/_update_par_ptr. getValue() in approx.cpp reads y[ind->ix[ind->idx]], so the covariate (e.g. lagt) is looked up at the sorted position of the dose event being processed — not at the position of the preceding observation event (which has lagt=0). All three changes save and restore the affected fields after the loop so no side-effects leak into subsequent event handling. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Member
Author
|
@john-harrold the last commit made me laugh -- it said I fixed everything that I broke, but it had just forgot the context. It was "justifying" itself for only fixing some of the bugs it was introducing. |
Member
Author
|
This has been running in Clade code for about 1 week, I'm assuming it doesn't know how to fix the errors it generated. Unfortunately, that means this particular feature request is done for now. |
This comment was marked as outdated.
This comment was marked as outdated.
Both liblsoda (C) and dlsoda (Fortran) refuse to integrate when |tout - t| < 2*UROUND*max(|tout|,|t|) with the error "tout too close to t to start integration". The prior isSameTime threshold was only 1*DBL_EPSILON, creating a gap where the check would pass the guard but the solver would reject the step. For id=509 in nmtest (SS infusion with lag and modeled rate), the EVIDF_INF_RATE stop-event time computed via handleInfusionItem differed from the solver's current xp by ~1.5 ULPs at t≈12 and t≈31.28, which fell in the 1x–2x epsilon gap. Widening isSameTime to 2*DBL_EPSILON eliminates these spurious "too close" failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
State-dependent lag/F/dur/rate/mtime expressions (including those using
d/dt(state) terms) now parse successfully and compile, so the
corresponding tests should expect success rather than failure.
Changes:
- First group (rxode2): mtime(z)=d/dt(x)+3 now compiles fine
- Second group (rxode2parse): {f,F,alag,lag,rate,dur}(depot)=d/dt(central)+3
and mtime(z)=d/dt(x)+3 now parse successfully
Jacobian-dependent expressions (df(x)/dt(ETA[1])) remain invalid and
their badParse tests are unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…loop sort order getSolve(i) uses sorted-position i as buffer index. The solve loop's updateSolve propagates state through positions 0..n-1; runtime re-sorts via refreshLagTimesIfNeeded change which raw event is at each position, but the state chain stays consistent. Previously, rxode2_df called iniSubject(inLhs=1) which ran sortInd using initial-state lag values, producing a different sorted order than the solve loop's final order (which may include runtime lag updates). Reading getSolve(i) in rxode2_df then retrieved the wrong ODE state for some observations — manifesting as depot appearing at the wrong time when alag() depends on a state variable. Fix: skip sortInd when inLhs==1. rxode2_df then iterates through the same sorted positions as the solve loop, so getSolve(i) maps correctly. Also remove debug REprintf calls added during investigation, and add a numeric correctness test: with constant state_val and alag(depot)<-state_val, dose at raw_t fires at raw_t+state_val and depot follows 100*exp(-ka*(t-t_fire)). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
No description provided.