laddu_extensions/
lib.rs

1//! # laddu-extensions
2//!
3//! This is an internal crate used by `laddu`.
4#![warn(clippy::perf, clippy::style, missing_docs)]
5
6/// Experimental extensions to the `laddu` ecosystem
7///
8/// <div class="warning">
9///
10/// This module contains experimental code which may be untested or unreliable. Use at your own
11/// risk! The features contained here may eventually be moved into the standard crate modules.
12///
13/// </div>
14pub mod experimental;
15
16/// A module containing the `laddu` interface with the [`ganesh`] library
17pub mod ganesh_ext;
18
19/// Extended maximum likelihood cost functions with support for additive terms
20pub mod likelihoods;
21
22// pub use ganesh_ext::{MCMCOptions, MinimizerOptions};
23pub use likelihoods::{
24    LikelihoodEvaluator, LikelihoodExpression, LikelihoodID, LikelihoodManager, LikelihoodScalar,
25    NLL,
26};
27
28use fastrand::Rng;
29use rapidhash::{HashSetExt, RapidHashSet};
30
31/// An extension to [`Rng`] which allows for sampling from a subset of the integers `[0..n)`
32/// without replacement.
33pub trait RngSubsetExtension {
34    /// Draw a random subset of `m` indices between `0` and `n`.
35    fn subset(&mut self, m: usize, n: usize) -> Vec<usize>;
36}
37
38// Nice write-up here:
39// https://www.nowherenearithaca.com/2013/05/robert-floyds-tiny-and-beautiful.html
40fn floyd_sample(m: usize, n: usize, rng: &mut Rng) -> RapidHashSet<usize> {
41    let mut set = RapidHashSet::with_capacity(m * 2);
42    for j in (n - m)..n {
43        let t = rng.usize(..=j);
44        if !set.insert(t) {
45            set.insert(j);
46        }
47    }
48    set
49}
50
51impl RngSubsetExtension for Rng {
52    fn subset(&mut self, m: usize, n: usize) -> Vec<usize> {
53        assert!(m < n);
54        if m > n / 2 {
55            let k = n - m;
56            let exclude = floyd_sample(k, n, self);
57            let mut res = Vec::with_capacity(m);
58            for i in 0..n {
59                if !exclude.contains(&i) {
60                    res.push(i);
61                }
62            }
63            return res;
64        }
65        floyd_sample(m, n, self).into_iter().collect()
66    }
67}