-
Notifications
You must be signed in to change notification settings - Fork 0
Updates to the package during JOSS Review #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
djpasseyjr
wants to merge
21
commits into
main
Choose a base branch
from
JOSS-Review
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
7292fef
Updates to deps so tests are automatically runnable.
djpasseyjr 2d341af
Adds black settings and build system.
djpasseyjr cd8b639
Black reformatting
djpasseyjr 6e0a3fe
Adds flake8 settings.
djpasseyjr 62f812a
Autoformatter (black) and flake8 compliance updates. Cosmetic only.
djpasseyjr 1d3ace2
Updates developer instructions with linter, formatting and testing.
djpasseyjr a3431d0
Adds mkdocs instructions for building docs.
djpasseyjr 3c9100f
Pin pysindy version bc of breaking changes.
djpasseyjr 00407f3
Updates docs in line with JOSS review.
djpasseyjr 5edad8b
Adds benchmark to docs
djpasseyjr 56c0089
Adds setuptools for pysindy on python >= 3.11
djpasseyjr 10993a9
Enhance hyperparameter optimization explanation
djpasseyjr 55167b4
(auto) Paper PDF Draft
djpasseyjr 6b7b8bb
Update figure caption for clarity
djpasseyjr f8d13d1
(auto) Paper PDF Draft
djpasseyjr 305c1d5
Add new references to paper.bib
djpasseyjr 8ae9388
(auto) Paper PDF Draft
djpasseyjr 4f2c1dc
Update BibTeX entries for articles
djpasseyjr e453e76
(auto) Paper PDF Draft
djpasseyjr 5fa2a8d
Added Gilpin and Kaptanoglu citations.
djpasseyjr 8bb7a5c
(auto) Paper PDF Draft
djpasseyjr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| [flake8] | ||
| max-line-length = 79 | ||
| # E203: Black uses space before ':' in slices (e.g. x[a : b]). | ||
| # E501: Long lines (docstrings, type hints) deferred; wrap in follow-up. | ||
| extend-ignore = E203, E501 | ||
| # Re-exports in __init__.py are intentional; suppress "imported but unused" | ||
| per-file-ignores = | ||
| __init__.py:F401 | ||
| */__init__.py:F401 | ||
| */*/__init__.py:F401 | ||
| */*/*/__init__.py:F401 |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| # VS Code | ||
| *.code-workspace | ||
| .vscode | ||
| .cursor/ | ||
|
|
||
| # iOS | ||
| .DS_Store | ||
|
|
||
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| # Working with the benchmark | ||
|
|
||
| The [Interfere Benchmark](https://drive.google.com/file/d/19_Ha-D8Kb1fFJ_iECU62eawbeuCpeV_g/view?usp=sharing) is a set of JSON files. Each file encodes one **observational vs intervention** problem: you get training data, an **observational** trajectory (continuation with the same exogenous driver), and an **intervention** trajectory (the same system under an intervention). The goal is to fit a forecasting method on the training data and compare how well it predicts the observational continuation versus the intervention response. | ||
|
|
||
| ## What's in the JSON | ||
|
|
||
| Each benchmark JSON includes (among other keys) the following at the top level: | ||
|
|
||
| | Key | Description | | ||
| | ---------------------------- | ------------------------------------------------------------------------------- | | ||
| | `metadata` | Variable descriptions and schema info | | ||
| | `model_description` | Text description of the underlying dynamics | | ||
| | `initial_condition_times` | Times for the short history before training | | ||
| | `initial_condition_states` | States at those times (prior to `train_*`) | | ||
| | `train_times` | 1D array of training time points | | ||
| | `train_states` | 2D array `(n_train, n_vars)` of training observations | | ||
| | `train_exog_idxs` | Column indices of exogenous variables during training (same as forecast) | | ||
| | `forecast_times` | Time points for the forecast period | | ||
| | `forecast_states` | **Observational** trajectory: system continued with same exogenous driver | | ||
| | `forecast_exog_idxs` | Same as `train_exog_idxs` | | ||
| | `causal_resp_states` | **Intervention** trajectory: response under the "do" intervention | | ||
| | `causal_resp_times` | Same length as `forecast_times` (same time grid) | | ||
| | `causal_resp_exog_idxs` | Exogenous indices under the intervention (includes any newly driven variables) | | ||
| | `target_idx` | (Optional) column index of the prediction target | | ||
|
|
||
| The **intervention is not stored as a separate object**. It is implied by the exogenous columns: during the observational period they follow `train_states` / `forecast_states` at `forecast_exog_idxs`; during the intervention period they follow `train_states` / `causal_resp_states` at `causal_resp_exog_idxs`. You reconstruct callable interventions by interpolating those time series (see below). | ||
|
|
||
| ## The three stages | ||
|
|
||
| 1. **Training**Fit a forecasting method on `train_states` at `train_times`, with exogenous variables at `train_exog_idxs`. | ||
| 2. **Observational forecast**Using the **same** exogenous driver as in training (values from the observational run: training then `forecast_states`), simulate over `forecast_times` and compare to `forecast_states`. | ||
| 3. **Intervention forecast** | ||
| Using the **intervention** exogenous driver (values from the intervention run: training then `causal_resp_states` at `causal_resp_exog_idxs`), simulate over the same `forecast_times` and compare to `causal_resp_states`. | ||
|
|
||
| So: same training data; one continuation is "observational", the other is "under intervention"; the benchmark compares how well a method predicts both. | ||
|
|
||
| ## Loading the JSON and building interventions | ||
|
|
||
| The package expects interventions as **callables** of time (e.g. `SignalIntervention` with scalar functions). The JSON only gives (time, value) arrays. So we load the arrays and turn the exogenous columns into callables via interpolation (e.g. `scipy.interpolate.interp1d`): | ||
|
|
||
| ```python | ||
| import json | ||
| import numpy as np | ||
| import interfere | ||
| from scipy.interpolate import interp1d | ||
|
|
||
| def load_benchmark(path): | ||
| """Load benchmark JSON and build interventions from exogenous columns.""" | ||
| with open(path) as f: | ||
| data = json.load(f) | ||
|
|
||
| train_t = np.array(data["train_times"]) | ||
| forecast_t = np.array(data["forecast_times"]) | ||
| train_states = np.array(data["train_states"]) | ||
| forecast_states = np.array(data["forecast_states"]) | ||
| causal_resp_states = np.array(data["causal_resp_states"]) | ||
| obs_exog_idxs = data["forecast_exog_idxs"] | ||
| do_exog_idxs = data["causal_resp_exog_idxs"] | ||
|
|
||
| # Time grid for interpolation: end of train (excl. last) + full forecast | ||
| obs_t = np.hstack([train_t[:-1], forecast_t]) | ||
|
|
||
| # Observational intervention: exog follows observational trajectory (train -> forecast_states) | ||
| obs_intervention = interfere.SignalIntervention( | ||
| obs_exog_idxs, | ||
| [ | ||
| interp1d(obs_t, np.hstack([train_states[:-1, i], forecast_states[:, i]])) | ||
| for i in obs_exog_idxs | ||
| ], | ||
| ) | ||
|
|
||
| # Do-intervention: exog follows intervention trajectory (train -> causal_resp_states) | ||
| do_intervention = interfere.SignalIntervention( | ||
| do_exog_idxs, | ||
| [ | ||
| interp1d(obs_t, np.hstack([train_states[:-1, i], causal_resp_states[:, i]])) | ||
| for i in do_exog_idxs | ||
| ], | ||
| ) | ||
|
|
||
| return { | ||
| "train_t": train_t, | ||
| "forecast_t": forecast_t, | ||
| "train_states": train_states, | ||
| "forecast_states": forecast_states, | ||
| "causal_resp_states": causal_resp_states, | ||
| "obs_intervention": obs_intervention, | ||
| "do_intervention": do_intervention, | ||
| "model_description": data.get("model_description", ""), | ||
| } | ||
| ``` | ||
|
|
||
| This uses only `interfere`, `numpy`, and `scipy`; no separate experiments package. | ||
|
|
||
| ## Minimal end-to-end example | ||
|
|
||
| After loading with `load_benchmark`, fit on training data (with observational exog), run an observational forecast, then fit again with the do-intervention exog and run the intervention forecast. Compare both to the benchmark trajectories: | ||
|
|
||
| ```python | ||
| # Load (path to your benchmark JSON) | ||
| data = load_benchmark("examples/AttractingFixedPoint4D.json") | ||
| train_t = data["train_t"] | ||
| forecast_t = data["forecast_t"] | ||
| train_states = data["train_states"] | ||
| obs_intervention = data["obs_intervention"] | ||
| do_intervention = data["do_intervention"] | ||
|
|
||
| # Fit on training data | ||
| endog, exog = obs_intervention.split_exog(train_states) | ||
| method = interfere.SINDy() | ||
| method.fit(train_t, endog, exog) | ||
|
|
||
| # Observational forecast: same exog as training continuation | ||
| observational_pred = method.simulate( | ||
| forecast_t, | ||
| prior_states=train_states, | ||
| prior_t=train_t, | ||
| intervention=obs_intervention, | ||
| ) | ||
|
|
||
| # Intervention forecast: fit with do-intervention exog, then simulate | ||
| method2 = interfere.SINDy() | ||
| endog2, exog2 = do_intervention.split_exog(train_states) | ||
| method2.fit(train_t, endog2, exog2) | ||
| intervention_pred = method2.simulate( | ||
| forecast_t, | ||
| prior_states=train_states, | ||
| prior_t=train_t, | ||
| intervention=do_intervention, | ||
| ) | ||
|
|
||
| # Compare to ground truth | ||
| forecast_rmse = np.sqrt(np.mean((observational_pred - data["forecast_states"]) ** 2)) | ||
| intervention_rmse = np.sqrt(np.mean((intervention_pred - data["causal_resp_states"]) ** 2)) | ||
| print("RMSE observational forecast:", forecast_rmse) | ||
| print("RMSE intervention forecast:", intervention_rmse) | ||
| ``` |
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you also specify a minimum python version in pyproject.toml?