Add @poissonians macro #4262
Conversation
Implement the @poissonians macro for defining Poisson counting process differentials with associated rate expressions. This mirrors the @brownians pattern but stores rate metadata for later conversion to jumps. Phase 1 - Macro and Metadata: - Add POISSONIAN to VariableType enum - Add PoissonianRateCtx metadata type for storing rate expressions - Implement topoissonian, ispoissonian, getpoissonianrate helpers - Create @poissonians macro supporting single, inline, and block syntax - Export @poissonians from ModelingToolkitBase Phase 2 - System Storage: - Add poissonians field to System struct - Update System constructors to accept poissonians kwarg - Two-argument constructor auto-detects poissonians from equations - Extract variables/parameters from rate expressions - Add poissonians(sys) accessor with hierarchical namespacing - Add namespace_poissonians for subsystem support - Update Base.copy and equality checking Phase 3 (poissonian-to-jump conversion in mtkcompile) to follow. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement the compilation step that extracts poissonian terms from equations and converts them to Jump objects (ConstantRateJump or VariableRateJump). Core extraction functions in systems.jl: - _poissonians_to_jumps: Extract coefficients using Symbolics.linear_expansion, build affects with Pre(), classify jump types based on rate dependencies - _is_variable_rate_jump: Returns true if rate depends on t or any unknowns - extract_poissonians_to_jumps: Wrapper that updates system fields Integration: - Modified _mtkcompile to extract poissonians at the very beginning - Poissonians converted to jumps BEFORE checking for existing jumps - System then flows through standard jump/brownian pathways Behavior: - Pure-jump systems: all D(X)~0 equations are removed - Mixed systems: continuous ODE/SDE part is retained - State-dependent rates -> VariableRateJump - Parameter-only rates -> ConstantRateJump - Same poissonian in multiple equations -> single jump with multiple affects - Generated jumps merged with any explicit jumps kwarg Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Various coefficient forms (dN*a, (a+b)*dN, -dN) - Jump-diffusion with both @brownian and @poissonians - Hierarchical systems with poissonians in subsystems - Zero coefficient poissonian is dropped Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add save_positions kwarg to mtkcompile, flowing through to extract_poissonians_to_jumps and _poissonians_to_jumps - VRJs created from poissonians now use the user-specified save_positions - User-provided VRJs retain their own save_positions - Add check_no_poissonians validation in JumpProblem to error if system has unprocessed poissonians (must call mtkcompile first) - Add tests for save_positions forwarding and error on uncompiled systems Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Test D(X) ~ X * dN where jump size depends on current state - Test D(X) ~ α * X * dN where coefficient is parameter times unknown Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…-dependent coefficients - Phase 2: Add 3 testsets verifying two-argument constructor auto-discovers parameters/unknowns from poissonian coefficients (not just rate expressions) - Phase 4: Add 2 testsets for geometric decay model with state-dependent coefficient (-δ*X*dN), including analytical verification via Poisson MGF and comparison to direct JumpProcesses implementation - Refactor relative error assertions from `abs(x-e)/e < tol` to `abs(x-e) < tol*e` to avoid potential division issues Total tests: 105 (was 91) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Pass StableRNG instance to all JumpProblem constructors for consistency - Move using statements for Random, StableRNGs, Statistics, OrdinaryDiffEq, StochasticDiffEq to the top of the file per Julia convention Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The flatten function was missing poissonians when constructing the flattened system. This caused poissonians from hierarchical systems to be lost during flattening. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ssonian processing - Use `iscall(eq.lhs) && operation(eq.lhs) isa Differential` instead of `isdiffeq` to exclude Shift operators - Use `var_from_nested_derivative` from Symbolics to properly handle nested derivatives like D(D(X)) - Skip higher-order derivatives (order > 1) since jumps only work with first-order ODEs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename to _is_variable_rate_jump! (mutating) - Pre-allocate unknowns_and_iv set once containing unknowns + iv - Pre-allocate rate_vars_set for reuse across iterations - Use three-argument get_variables! to filter directly - Simplify check to just !isempty(rate_vars_set) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Verifies that @poissonians dN(rate_expr) correctly captures the symbolic value of rate_expr, not the variable name. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
One question is if we prefer @poissonians a(rate_expr)or @poissonians a [rate = expr]i.e. explicitly typing the rate metadata. The first seems cleaner to me and more inline with what we write mathematically, but the second has the advantage that we can reuse |
Replace individual membership checks (`X in unknowns`) with set equality (`unknowns == Set([X, Y])`) to catch unexpected extra items like poissonians incorrectly left in unknowns. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set((a, b)) avoids the heap allocation that Set([a, b]) requires for the intermediate vector. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Compare at all 250 time points (1:1:250) instead of just final time - Increase simulations from 500 to 2000 for better statistical convergence - Use count() instead of any() for precise jump type verification - Use solution interpolation to extract values at exact check times Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
@AayushSabharwal I think this is good to go assuming the non-broken tests pass. But let me know if you have any feedback. Note that while the amount of code is long, most of it is tests. The MTKBase changes are not too much. |
AayushSabharwal
left a comment
There was a problem hiding this comment.
Just minor stuff. Thanks for all the code comments!
- Error on higher-order derivatives in systems with poissonians (model is not well-defined with jumps and higher-order ODEs) - Use Symbolics.LinearExpander for better performance by caching the expander outside the inner equation loop - Add poissonians.jl to runtests.jl (was missing) - Add tests for higher-order derivative error Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
@AayushSabharwal I addressed your comments. I had apparently forgotten to put the new test file in runtests.jl, so I've now added it to InterfaceII right after the jump tests. It passes locally, so hopefully it will now pass on CI. Once that successfully finishes this should be good. |
|
Thanks! |
Note: this is very WIP as I've spent a good amount of time iterating with Claude on this, but haven't yet done extensive code review of what it has generated. I'm sure there are places that need updating / changes for various reasons. I'm putting this up now in case anyone has feedback on the basic design (I will stop working on it if there issues with this approach). I will go through this more carefully and drop the WIP once I have carefully reviewed all the code changes.
Longer-term we can expand from this to do better jump classification (i.e. this does the simple thing, and only has jumps with parameters as their rate become a
ConstantRateJump, with everything elseVariableRateJump). We can also expand this to support marks and/or Poisson random measures, both of which would build from this anyways.Add
@poissoniansMacro for Inline Poisson Process DifferentialsSummary
This PR introduces the
@poissoniansmacro, enabling inline Poisson process differentials in ModelingToolkitBase equations. Poissonian variables represent Poisson counting process increments and are automatically converted toConstantRateJumporVariableRateJumpobjects during system compilation.API
Declaring Poissonians
Using in Equations
Querying Poissonians
Controlling save_positions for VRJs
Implementation Details
Phase 1: Macro and Metadata
POISSONIANtoVariableTypeenumPoissonianRateCtxmetadata for storing rate expressions@poissoniansmacro withtopoissonianhelperispoissonianandgetpoissonianrateaccessorsPhase 2: System Storage
poissoniansfield toSystemstructpoissonians(sys)accessor with hierarchical subsystem supportPhase 3: Compilation to Jumps
_poissonians_to_jumpsextracts coefficients and builds jump affectsConstantRateJump(parameter-only rates) vsVariableRateJump(state/time-dependent rates)dN * dN)save_positionskwarg flows throughmtkcompileto VRJs from poissonianssave_positionssettingscheck_no_poissoniansvalidation ensuresmtkcompileis called beforeJumpProblemPhase 4: Mathematical Correctness
Example: SIR Model
Example: Jump-Diffusion