Skip to content

Conversation

@fderuiter
Copy link
Owner

🔧 The Violation

The gillespie_step_time function in math_explorer/src/epidemiology/stochastic.rs violated several "Systems Core" principles:

  1. Dependency Inversion Violation: It relied on rand::thread_rng(), making it impossible to seed for deterministic testing.
  2. Open/Closed Violation: It hardcoded the logic for a 2-event system (Infection/Recovery), requiring modification to support other models (e.g., SEIR).
  3. Side Effects: Implicit randomness made the function impure and hard to verify.

🛠️ The Fix

I applied the Strategy Pattern and Dependency Inversion:

  1. Defined a StochasticSystem<State> trait that allows any model to define its propensities (rates) and reaction logic.
  2. Implemented a generic GillespieSolver<R: Rng> that accepts any RNG strategy (e.g., StdRng, ThreadRng).
  3. Implemented StochasticSystem for the existing SIRModel.
  4. Deprecated the old gillespie_step_time function.

📉 Debt Reduction

  • Removed implicit dependency on thread_rng.
  • Decoupled the simulation algorithm (Gillespie SSA) from the specific biological model.
  • Enabled future extension to other stochastic models (SEIR, Lotka-Volterra) without modifying the solver code.

🧪 Integrity

  • Ran cargo test successfully.
  • Added test_gillespie_solver_deterministic which verifies the solver behavior using a fixed seed, ensuring logic is preserved and correct.
  • Verified SIRModel implementation matches the expected reaction dynamics.

PR created automatically by Jules for task 14656697529283794320 started by @fderuiter

- Replaced ad-hoc `gillespie_step_time` with a generic `GillespieSolver` struct.
- Implemented `StochasticSystem` trait to abstract reaction networks (e.g., SIR, SEIR).
- Injected RNG via generics (`R: Rng`) to enable deterministic testing (Dependency Inversion).
- Implemented `StochasticSystem` for `SIRModel`.
- Added deterministic unit tests for the solver.
- Deprecated `gillespie_step_time` to preserve backward compatibility.
- Updated EDR log.

Co-authored-by: fderuiter <127706008+fderuiter@users.noreply.github.com>
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@fderuiter
Copy link
Owner Author

Run cargo fmt -- --check
Diff in /home/runner/work/math/math/math_explorer/src/epidemiology/stochastic.rs:1:
-use rand::Rng;
use crate::epidemiology::compartmental::{SIRModel, SIRState};
+use rand::Rng;

/// A trait for systems that can be simulated stochastically.
///
Diff in /home/runner/work/math/math/math_explorer/src/epidemiology/stochastic.rs:142:
/// Calculates time to next event for SIR system (Gillespie).
///
/// $\tau = - \ln(U) / (\text{rate}{infect} + \text{rate}{recover})$
-#[deprecated(since = "0.1.0", note = "Use GillespieSolver instead for full simulation")]
+#[deprecated(

  • since = "0.1.0",
  • note = "Use GillespieSolver instead for full simulation"
    +)]
    pub fn gillespie_step_time(rate_infect: f64, rate_recover: f64) -> f64 {
    let mut rng = rand::thread_rng();
    let u: f64 = rng.r#gen(); // Uniform (0, 1)
    Diff in /home/runner/work/math/math/math_explorer/src/epidemiology/stochastic.rs:161:
    #[cfg(test)]
    mod tests {
    use super::*;
  • use rand::rngs::StdRng;
    use rand::SeedableRng;
  • use rand::rngs::StdRng;

    #[test]
    fn test_extinction_probability() {
    Error: Process completed with exit code 1.

@google-labs-jules
Copy link
Contributor

Run cargo fmt -- --check
Diff in /home/runner/work/math/math/math_explorer/src/epidemiology/stochastic.rs:1:
-use rand::Rng;
use crate::epidemiology::compartmental::{SIRModel, SIRState};
+use rand::Rng;

/// A trait for systems that can be simulated stochastically.
///
Diff in /home/runner/work/math/math/math_explorer/src/epidemiology/stochastic.rs:142:
/// Calculates time to next event for SIR system (Gillespie).
///
/// $\tau = - \ln(U) / (\text{rate}{infect} + \text{rate}{recover})$
-#[deprecated(since = "0.1.0", note = "Use GillespieSolver instead for full simulation")]
+#[deprecated(

  • since = "0.1.0",
  • note = "Use GillespieSolver instead for full simulation"
    +)]
    pub fn gillespie_step_time(rate_infect: f64, rate_recover: f64) -> f64 {
    let mut rng = rand::thread_rng();
    let u: f64 = rng.r#gen(); // Uniform (0, 1)
    Diff in /home/runner/work/math/math/math_explorer/src/epidemiology/stochastic.rs:161:
    #[cfg(test)]
    mod tests {
    use super::*;
  • use rand::rngs::StdRng;
    use rand::SeedableRng;
  • use rand::rngs::StdRng;

    #[test]
    fn test_extinction_probability() {
    Error: Process completed with exit code 1.

I have run cargo fmt and pushed the changes.

- Replaced ad-hoc `gillespie_step_time` with a generic `GillespieSolver` struct.
- Implemented `StochasticSystem` trait to abstract reaction networks (e.g., SIR, SEIR).
- Injected RNG via generics (`R: Rng`) to enable deterministic testing (Dependency Inversion).
- Implemented `StochasticSystem` for `SIRModel`.
- Added deterministic unit tests for the solver.
- Deprecated `gillespie_step_time` to preserve backward compatibility.
- Updated EDR log.
- Applied cargo fmt.

Co-authored-by: fderuiter <127706008+fderuiter@users.noreply.github.com>
@fderuiter fderuiter merged commit dd45254 into main Jan 27, 2026
1 check passed
@fderuiter fderuiter deleted the core/refactor-gillespie-strategy-14656697529283794320 branch January 27, 2026 20:08
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.

2 participants