statrs-dev / statrs

Statistical computation library for Rust
https://docs.rs/statrs/latest/statrs/
MIT License
581 stars 83 forks source link

Mutable/Movable parameters for multivariate normal #180

Open limads opened 1 year ago

limads commented 1 year ago

Hello, I'm studying the possibility of using this crate for Markov Chain Monte Carlo (MCMC) based inference. In this use case, the log-density of a distribution is evaluated repeatedly at different parameter values. To do that currently, the crate requires re-creating the distributions at each iteration. This isn't much of a problem for scalar distributions, but for the multivariate normal, I have to re-allocate the mean vector and covariance matrix at each iteration (since distributions are immutable), which impacts performance.

Allowing the user to re-set the parameters separately would work:

pub fn set_mean(&mut self, mu : &[f64]);
pub fn set_variance(&mut self, var : &[f64]);

But a solution that moves the parameters out of the struct would also work (therefore preserving the intended immutable API):

pub fn take_parameters(self) -> (DVector<f64>, DMatrix<f64>);

Are there any plans to offer something like that?

YeungOnion commented 5 months ago

Hi, are you still considering this for MCMC? If so, I think your solution that preserves immutability would be good. I'd be open to a PR for this, please consider making this atop #209 as we plan to merge those changes and those change the multivariate API

Aside as a possibility for an optimization if the math is nice: If the transformations you make on the covariance can be expressed as operations on the decomposition and its inverse, then consider a take_parameters_and_precomputed as well, which would motivate a new from new_with_precomputed_unchecked (does not verify that LL* = Σ or LP = 1 for provided (μ, Σ), (L, P, |Σ|)) and new_with_precomputed

YeungOnion commented 2 weeks ago

@FreezyLemon did some profiling demonstrating the allocation is much less time consuming than the linalg operations and shared it in #278.

It does seem like struct instantiation could have been a bottleneck for MCMC iterations as it could happen up to every step.

If we do anything in this space to support low-dimensional MCMC we should probably look at

I think high dimensionality should rely on a different dependency.