Skip to content

Add UODE Lotka-Volterra notebook#153

Merged
DanWaxman merged 11 commits into
mainfrom
mh-uode
Jun 3, 2026
Merged

Add UODE Lotka-Volterra notebook#153
DanWaxman merged 11 commits into
mainfrom
mh-uode

Conversation

@markusheinonen
Copy link
Copy Markdown
Contributor

  • Observe both prey and predator (H=I_2) at dt=0.2 (150 points)
  • Laplace(0, 0.005) prior for SINDy-like sparsity on Theta
  • SVI config: lr=1e-3, cov_rescaling=2.0, lr_decay=0.5, 2000 steps
  • Correctly recovers xy interaction coefficients (Frobenius err ~0.077)
  • Includes debug CLI script for systematic experiments

- Observe both prey and predator (H=I_2) at dt=0.2 (150 points)
- Laplace(0, 0.005) prior for SINDy-like sparsity on Theta
- SVI config: lr=1e-3, cov_rescaling=2.0, lr_decay=0.5, 2000 steps
- Correctly recovers xy interaction coefficients (Frobenius err ~0.077)
- Includes debug CLI script for systematic experiments
@mattlevine22
Copy link
Copy Markdown
Collaborator

mattlevine22 commented Mar 19, 2026

Was able to get this notebook to run "perfectly" in #155 .

To get things to work better

  1. Use ContinuousTimeEnKF instead of EKF, as EnKF is more numerically robust
  2. Revert to simple ADAM with lr=1e-3 and 4000 epochs. At 1000 epochs, things are pretty close, and by 4000 epochs, the answer has fully sparsified
  3. There are some bugs when trying to do MAP-Predictive Filter here...observe that svi_result.params['Theta_auto_loc] != Theta_inferred`

Instead of predictive_filter = Predictive(data_conditioned_model, params=svi_result.params, num_samples=1), do
predictive_filter = Predictive(data_conditioned_model, params=guide.median(svi_result.params), num_samples=1)

mattlevine22 and others added 3 commits March 21, 2026 21:50
* add MLL-truth computation and more visualizations

* working lv UODE---use EnKF + 2000 adam epochs with lr=1e-3 + fixed posterior predictive filtering plots

* reverted to original timeseries length
@DanWaxman
Copy link
Copy Markdown
Collaborator

I also just updated this in lieu of #135

@mattlevine22
Copy link
Copy Markdown
Collaborator

Now that it is working (mostly by using EnKF and running ADAM for longer), we should think about where to put this example.

  1. Should this go in Docs as a deep dive? Or should It be in the dynestyx-examples repo?
  • I like the idea of us having a reimplementation of "Universal ODEs" in our repo (to show that most of the popular methods can be set up easily in Dynestyx)
  1. Do we want to keep the debugging script / put it in dynestyx-examples?

@DanWaxman
Copy link
Copy Markdown
Collaborator

1. Should this go in Docs as a deep dive? Or should It be in the `dynestyx-examples` repo?

Hmm I think this is a good open question. I think there are two choices for how we use dynestyx-examples: (1) have every example there; (2) keep minimal examples (e.g., that implement mainstream ideas in a single notebook) in dynestyx, and delegate more complex multi-file case studies to dynestyx-examples. I lean more towards (2), in which case we may still consider this a "deep dive."

In any case, I think it's a low-cost decision, it's easy to revert this later.

2. Do we want to keep the debugging script / put it in dynestyx-examples?

I would get rid of the debugging script if we keep it in dynestyx, and keep it if we put it in dynestyx-examples. I don't want duplicated code across repos, though.

@DanWaxman
Copy link
Copy Markdown
Collaborator

Just got this to be working again and fixed some text/formatting issues. I think it makes sense to add to our deep dives for now, so I deleted the debug script and added this to our docs site nav. LGTM. Since I just made a bunch of changes, I think it's best if Matt does a quick review of this before merging. Thanks @markusheinonen!!

@DanWaxman DanWaxman requested a review from mattlevine22 June 2, 2026 20:00
@mattlevine22
Copy link
Copy Markdown
Collaborator

@copilot Do a thorough check of the Jupyter notebook and please provide a bulleted list of how to fix the exposition to match the code (there are many discrepancies). For example, we code H=jnp.eye(state_dim) but write about partial observations and H = [1 0] in the exposition (also learning rate and cov_rescaling are not used in the code anymore).

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 2, 2026

@copilot Do a thorough check of the Jupyter notebook and please provide a bulleted list of how to fix the exposition to match the code (there are many discrepancies). For example, we code `H=jnp.eye(s...

Addressed in 3bfc01a.

  • Updated the intro and configuration table to match the current setup: both species observed, H = I_2, \Delta t = 0.2, and the explicit initial-state prior/covariance.
  • Corrected the prior descriptions to match the code: Theta ~ Laplace(0, 0.005), sigma_x ~ LogNormal(log 0.05, 1.0), sigma_y ~ HalfNormal(0.5).
  • Replaced stale EKF wording with EnKF wording in the marginal-likelihood, inference, filtering, and phase-space sections, plus the filtered-trajectory plot title.
  • Removed stale exposition about cov_rescaling, learning-rate decay, and optax, and replaced it with the actual optimizer settings: ADAM with lr=1e-3 for 4000 SVI steps.
  • Updated the SVI text to reflect the current MAP readout path via guide.median(svi_result.params).

Copy link
Copy Markdown
Collaborator

@mattlevine22 mattlevine22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks! Fixed the text so it matches what was done (no LR schedule, no cov_rescaling, full observations, EnKF not EKF).

Tried to get copilot to do it, but that was a big mistake lol.

@DanWaxman
Copy link
Copy Markdown
Collaborator

DanWaxman commented Jun 2, 2026 via email

@DanWaxman DanWaxman merged commit cded6d9 into main Jun 3, 2026
3 checks passed
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.

4 participants