Skip to content

Factor linear/Lotka-Voltera ODE analyses through PolynomialSystem#1007

Open
georgefst wants to merge 9 commits intoToposInstitute:mainfrom
georgefst:polynomial-lv
Open

Factor linear/Lotka-Voltera ODE analyses through PolynomialSystem#1007
georgefst wants to merge 9 commits intoToposInstitute:mainfrom
georgefst:polynomial-lv

Conversation

@georgefst
Copy link

@georgefst georgefst commented Feb 5, 2026

Closes #965.

Developed alongside @patrickaldis.

model: &DiscreteDblModel,
data: LinearODEProblemData,
) -> ODEAnalysis<LinearODESystem> {
) -> ODEAnalysis<NumericalPolynomialSystem<u8>> {
Copy link
Author

@georgefst georgefst Feb 5, 2026

Choose a reason for hiding this comment

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

The exponent for linear systems is obviously always 1. So it's tempting to use or define a singleton type, but that might be overkill. Not sure how easy or conventional it is in Rust.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this accounts for constants (zero exponents) though. We'd need a type with 0 and 1

Copy link
Author

Choose a reason for hiding this comment

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

Oh yeah, good point 🤦‍♂️

Copy link
Author

Choose a reason for hiding this comment

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

We could of course define a zero-or-one type instead. But still, probably overkill.

// XXX: Lots of boilerplate to delegate the module structure of `Polynomial` to
// `Combination` because these traits cannot be derived automatically.

impl<Var, Coef, Exp> Sum for Polynomial<Var, Coef, Exp>
Copy link
Author

@georgefst georgefst Feb 5, 2026

Choose a reason for hiding this comment

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

There's nothing here that's actually specific to Polynomial. The exact same code could be used for any AdditiveMonoid. But I don't think Rust allows instances with that sort of flexibility. We could define some reusable helper, but probably not worth it.

Note that Polynomial is an AdditiveMonoid precisely when its coefficient type is, due to the instances:

  • impl<Var, Coef, Exp> Add for Polynomial<Var, Coef, Exp>
  • impl<Var, Coef, Exp> Zero for Polynomial<Var, Coef, Exp>

Copy link
Author

Choose a reason for hiding this comment

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

Oh, and also, looking at the comment above this, maybe we should actually move the main Impl code to Combination, and just delegate to that here?

where
Var: Ord,
Coef: Add<Output = Coef> + Zero,
Coef: Zero,
Copy link
Author

Choose a reason for hiding this comment

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

This slight generalisation was useful during debugging. I'm not sure if it was necessary in the end, but there's no harm.

Coming from Haskell, I am a bit surprised that the compiler doesn't warn about redundant constraints.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I've found myself wishing for such warnings but AFAIK, the Rust compiler doesn't warn about either redundant constraints or unnecessary constraints.

@georgefst georgefst marked this pull request as ready for review February 5, 2026 17:59
Copy link
Member

@epatters epatters left a comment

Choose a reason for hiding this comment

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

Thanks for making a start on this refactor! I'd suggest the following alternative design:

  • remove entirely the modules catlog::simulate::ode::[linear_ode | lotka_volterra]
  • refactor the modules catlog::stdlib::analyses::ode::[linear_ode | lotka_volterra] to first generate a polynomial system with symbolic coefficients and then from that a numerical polynomial system

The reason to do it this way is that it's useful to have a symbolic representation of the abstract system, before any specific numerical values are chosen. E.g., we will display that abstract system to the user as equations in LaTeX math.

For an example of how to this, see the mass_action module.

@georgefst
Copy link
Author

georgefst commented Feb 12, 2026

I'd suggest the following alternative design

In the last commit, we've refactored so that polynomials with symbolic coefficients are created first, and added some LaTeX tests to exercise the symbolic versions.

Hopefully this is roughly what you had in mind. If so, we'll then proceed with a similar change for the Lotka-Volterra systems.

Comment on lines 3 to 4
//! The main entry point for this module is
//! [`linear_ode_analysis`](SignedCoefficientBuilder::linear_ode_analysis).
Copy link
Author

Choose a reason for hiding this comment

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

Ah, I suppose this is no longer true now that this module also contains the new generalised linear_polynomial_system as well.

\end{align*}
$$
"#]];
expected.assert_eq(&matrix_example().to_latex_string());
Copy link
Author

Choose a reason for hiding this comment

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

Rendered, this produces:

Image

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.

Factor linear/Lotka-Voltera ODE analyses through PolynomialSystem

3 participants