Skip to main content

causal_hub/random/models/bayesian_network/gaussian/
model.rs

1use rand::prelude::*;
2
3use crate::{
4    labels,
5    models::{BN, GaussBN},
6    random::{Random, RngDag, RngGaussCPD},
7    set,
8    types::{Error, Labels, Result},
9};
10
11/// A struct for random Gaussian Bayesian network generation.
12pub struct RngGaussBN<'a, R>
13where
14    R: Rng,
15{
16    rng: &'a mut R,
17    labels: &'a Labels,
18    s_a: f64,
19    s_b: f64,
20    evidence: f64,
21    probability: f64,
22}
23
24impl<'a, R> RngGaussBN<'a, R>
25where
26    R: Rng,
27{
28    /// Creates a new `RngGaussBN` instance.
29    ///
30    /// # Arguments
31    ///
32    /// * `rng` - A mutable reference to a random number generator.
33    /// * `labels` - The labels of the variables.
34    /// * `s_a` - The standard deviation of the regression coefficients.
35    /// * `s_b` - The standard deviation of the intercept.
36    /// * `e` - A small positive constant for covariance regularization.
37    /// * `p` - The probability of generating an edge.
38    ///
39    /// # Errors
40    ///
41    /// * If `s_a` is not positive.
42    /// * If `s_b` is not positive.
43    /// * If `e` is not positive.
44    /// * If `p` is not in [0, 1].
45    ///
46    /// # Returns
47    ///
48    /// A new `RngGaussBN` instance.
49    ///
50    pub fn new(
51        rng: &'a mut R,
52        labels: &'a Labels,
53        s_a: f64,
54        s_b: f64,
55        evidence: f64,
56        probability: f64,
57    ) -> Result<Self> {
58        // Check parameters.
59        if s_a <= 0.0 {
60            return Err(Error::InvalidParameter("s_a", "must be positive"));
61        }
62        if s_b <= 0.0 {
63            return Err(Error::InvalidParameter("s_b", "must be positive"));
64        }
65        if evidence <= 0.0 {
66            return Err(Error::InvalidParameter("e", "must be positive"));
67        }
68        // Check if the probability is in [0, 1].
69        if !(0.0..=1.0).contains(&probability) {
70            return Err(Error::InvalidParameter("p", "must be in [0, 1]"));
71        }
72
73        Ok(Self {
74            rng,
75            labels,
76            s_a,
77            s_b,
78            evidence,
79            probability,
80        })
81    }
82}
83
84impl<R> Random for RngGaussBN<'_, R>
85where
86    R: Rng,
87{
88    type Output = Result<GaussBN>;
89
90    fn random(&mut self) -> Self::Output {
91        // Generate a random DAG.
92        let graph = RngDag::new(self.rng, self.labels, self.probability)?.random()?;
93
94        // Generate the CPDs.
95        let cpds = self
96            .labels
97            .iter()
98            .enumerate()
99            .map(|(i, x)| {
100                // Get the parents of the variable.
101                let pa_i = graph.parents(&set![i])?;
102                // Get the labels of the variable.
103                let labels = labels![x.clone()];
104                // Get the labels of the conditioning variables.
105                let conditioning_labels =
106                    pa_i.into_iter().map(|j| self.labels[j].clone()).collect();
107                // Generate the random CPD.
108                RngGaussCPD::new(
109                    self.rng,
110                    &labels,
111                    &conditioning_labels,
112                    self.s_a,
113                    self.s_b,
114                    self.evidence,
115                )?
116                .random()
117            })
118            .collect::<Result<Vec<_>>>()?;
119
120        // Return the Gaussian Bayesian network.
121        GaussBN::new(graph, cpds)
122    }
123}