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}