rust-random / rand

A Rust library for random number generation.
https://crates.io/crates/rand
Other
1.68k stars 430 forks source link

const generic and dynamic distributions (Dirichlet, Multinomial, ...) #1322

Closed benjamin-lieser closed 1 year ago

benjamin-lieser commented 1 year ago

I am in the process of writing an implementation for the Multinomial distribution. The structure is similar to the one of the Dirichlet distribution. But I want to support const generic (number of categories known at compile time) and dynamic number of categories.

I think having Multinomial as an enum is a good idea, because depending on the number of draws different sampling strategies might be optimal and we can be generic over dynamic and static number of categories.

For the dynamic allocation the K parameter would be 0.

pub enum Multinomial<F : Float, I : PrimInt, const K : usize = 0> {
    FromBinomial(MultinomialFromBinomial<F,I,K>),
    FromBinomialDynamic(MultinomialFromBinomialDynamic<F,I>),
    //Maybe more implementations like naive for small number of draws
}

impl<F : Float + DivAssign, I : PrimInt, const K: usize> Multinomial<F, I, K> {
    pub fn new_static(n : I, weights : [F;K]) -> Result<Multinomial<F, I, K>, WeightedError> {
        assert!(K != 0);
        Ok(Self::FromBinomial(MultinomialFromBinomial::new(n, weights)?))
    }
    pub fn new_dynamic(weights : Vec<F>) -> Multinomial<F, I, K> {
        assert!(K == 0);
        Ok(Self::FromBinomialDynamic(MultinomialFromBinomialDynamic::new(n, weights)?))
    }
}

What do you think? In the future we could maybe do with one new function which selects depending if K==0, but as far as I know current stable Rust does not support this.

Also how to take the weight parameter? Especially for the dynamic case something like W : IntoIterator<Item=F> seems fitting.

dhardy commented 1 year ago

That design could work, though there is probably very little reason for the enum: users will already need to choose between static or dynamic constructors. If the point is to be able to return a generic object sampling from [I; K] or [I] — you still can't do this without a unified output (sample) type (e.g. by converting [I; K] to Vec<I> — was this how you planned to implement Distribution<_>)?

Honestly, the fact I took so long to answer reflects the currently rather limited interest in maintaining, let alone expanding, rand_distr. I'll close this issue because I don't want to track it, but feel free to ask further questions or open a PR (I'll try at least to answer questions more promptly).