feat: add dynamic regression (time-varying coefficients)#7
Merged
Conversation
Formatting-only changes applied by ruff format. No behavioral changes.
Implement multivariate FFBS (Durbin-Koopman simulation smoother) for
dynamic regression coefficients β_t that follow a random walk:
β_t = β_{t-1} + η_t, η_t ~ N(0, diag(σ²_β))
Key components:
- kalman.rs: dynamic_beta_smoother() with Cholesky-solve backward pass
for numerical stability (avoids explicit matrix inversion)
- sampler.rs: run_single_chain_dynamic() with full Block Gibbs loop:
Step 1: y_adj = y - x'β_t → sample μ_t (existing smoother)
Step 2: y_adj = y - μ_t → sample β_t (new FFBS)
Step 3-4: sample σ²_obs, σ²_level (existing)
Step 5: sample σ²_β per covariate (new inv-gamma)
Predictions: random walk β propagation in post-period
- lib.rs: dynamic_regression=false PyO3 argument
- Spike-and-slab disabled when dynamic_regression=true
Includes 6 new Rust tests (3 kalman + 3 sampler).
- ModelOptions: add dynamic_regression: bool = False with validation - DEFAULT_MODEL_ARGS: add "dynamic_regression": False - _run_sampler: pass dynamic_regression to Rust run_gibbs_sampler
16 tests across 5 categories: - Options validation (2): default=False, True accepted - Basic behavior (3): predictions shape, gamma empty, False=existing - Boundary (5): k=0, k=1, k=2, T_pre=2, k=T_pre-1 - Statistical (3): constant beta, structural break, no NaN - Integration (3): end-to-end, summary/inferences, plot Also update test_options.py expected_keys for dynamic_regression.
- summary.py: adopt origin/main's R-compatible summary format - test_analysis.py: adopt origin/main's docstring removal
YuminosukeSato
added a commit
that referenced
this pull request
Mar 23, 2026
feat: add dynamic regression (time-varying coefficients)
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.
Summary
Add Dynamic Regression (time-varying coefficients β_t), equivalent to R CausalImpact's
AddDynamicRegression.β_t = β_{t-1} + η_t, η_t ~ N(0, diag(σ²_β))
Regression coefficients follow a random walk over time. This provides better counterfactual predictions than static regression when the pre-intervention relationship undergoes structural changes.
Changes
Rust (src/)
kalman.rs: Multivariate FFBS (Durbin-Koopman simulation smoother). Cholesky-solve based backward pass for numerical stability (avoids explicit matrix inversion)sampler.rs: Extended Block Gibbs loop viarun_single_chain_dynamic()— samples β_t, σ²_β and propagates random walk predictions in post-periodlib.rs: Addeddynamic_regression=falsePyO3 argumentPython (python/)
options.py: AddedModelOptions.dynamic_regression: bool = Falsefieldmain.py: Added default toDEFAULT_MODEL_ARGS, passthrough in_run_samplerTests
test_dynamic_regression.py: 16 tests (option validation 2, basic behavior 3, boundary 5, statistical quality 3, integration 3)Design Decisions
dynamic_regression=True(variable selection is incompatible with time-varying coefficients)run_single_chain_static/run_single_chain_dynamic— zero risk of regressionUsage
Test plan
cargo fmt --check && cargo clippy -- -D warningspassescargo test— 26 tests passruff check && ruff format --checkpasses.venv/bin/pytest tests/ -v— 193 tests pass