causal-hub 0.0.4

A library for causal models, inference and discovery.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
use std::ops::Deref;

use itertools::Itertools;
use ndarray::{Zip, prelude::*};
use rand::{Rng, SeedableRng, seq::SliceRandom};
use rand_distr::{Distribution, weighted::WeightedIndex};
use rayon::prelude::*;

use crate::{
    datasets::{CatTrj, CatTrjEv, CatTrjEvT, CatTrjs, CatTrjsEv, CatType},
    estimators::{BE, CIMEstimator, ParCIMEstimator},
    models::{CatCIM, Labelled},
    types::{Labels, Set},
};

// TODO: This must be refactored to be stateless.

/// A struct representing a raw estimator.
///
/// This estimator is used to find an initial guess of the parameters with the given evidence.
/// Its purpose is to provide a starting point for the other estimators, like EM.
///
#[derive(Debug)]
pub struct RAWE<'a, R, E, D> {
    rng: &'a mut R,
    evidence: &'a E,
    dataset: Option<D>,
}

impl<R, E, D> Deref for RAWE<'_, R, E, D> {
    type Target = D;

    fn deref(&self) -> &Self::Target {
        self.dataset.as_ref().unwrap()
    }
}

impl<R, E, D> Labelled for RAWE<'_, R, E, D>
where
    D: Labelled,
{
    #[inline]
    fn labels(&self) -> &Labels {
        self.dataset.as_ref().unwrap().labels()
    }
}

impl<'a, R: Rng + SeedableRng> RAWE<'a, R, CatTrjEv, CatTrj> {
    /// Constructs a new raw estimator from the evidence.
    ///
    /// # Arguments
    ///
    /// * `evidence` - A reference to the evidence to fill.
    ///
    /// # Returns
    ///
    /// A new `RAWE` instance.
    ///
    pub fn par_new(rng: &'a mut R, evidence: &'a CatTrjEv) -> Self {
        // Initialize the estimator.
        let mut estimator = Self {
            rng,
            evidence,
            dataset: None,
        };

        // Fill the evidence with the raw estimator.
        estimator.dataset = Some(estimator.par_fill());

        estimator
    }

    /// Sample uncertain evidence.
    /// TODO: Taken from importance sampling, deduplicate.
    fn sample_evidence(&mut self) -> CatTrjEv {
        // Get shortened variable type.
        use CatTrjEvT as E;

        // Sample the evidence for each variable.
        let certain_evidence = self
            .evidence
            // Flatten the evidence.
            .evidences()
            .iter()
            // Map (label, [evidence]) to (label, evidence) pairs.
            .flatten()
            .flat_map(|e| {
                // Get the variable index, starting time, and ending time.
                let (event, start_time, end_time) = (e.event(), e.start_time(), e.end_time());
                // Sample the evidence.
                let e = match e {
                    E::UncertainPositiveInterval { p_states, .. } => {
                        // Construct the sampler.
                        let state = WeightedIndex::new(p_states).unwrap();
                        // Sample the state.
                        let state = state.sample(self.rng);
                        // Return the sample.
                        E::CertainPositiveInterval {
                            event,
                            state,
                            start_time,
                            end_time,
                        }
                    }
                    E::UncertainNegativeInterval { p_not_states, .. } => {
                        // Allocate the not states.
                        let mut not_states: Set<_> = (0..p_not_states.len()).collect();
                        // Repeat until only a subset of the not states are sampled.
                        while not_states.len() == p_not_states.len() {
                            // Sample the not states.
                            not_states = p_not_states
                                .indexed_iter()
                                // For each (state, p_not_state) pair ...
                                .filter_map(|(i, &p_i)| {
                                    // ... with p_i probability, retain the state.
                                    Some(i).filter(|_| self.rng.random_bool(p_i))
                                })
                                .collect();
                        }
                        // Return the sample and weight.
                        E::CertainNegativeInterval {
                            event,
                            not_states,
                            start_time,
                            end_time,
                        }
                    }
                    _ => e.clone(), // Due to evidence sampling.
                };

                // Return the certain evidence.
                Some(e)
            });

        // Collect the certain evidence.
        CatTrjEv::new(self.evidence.states().clone(), certain_evidence)
    }

    /// Fills the evidence with the raw estimator.
    ///
    /// # Arguments
    ///
    /// * `evidence` - A reference to the evidence to fill.
    ///
    /// # Returns
    ///
    /// A new `CatTrj` instance.
    ///
    fn par_fill(&mut self) -> CatTrj {
        // Short the evidence name.
        use CatTrjEvT as E;
        // Set missing placeholder.
        const M: CatType = CatType::MAX;

        // Get labels and states.
        let states = self.evidence.states().clone();

        // Get the ending time of the last event.
        let end_time = self
            .evidence
            .evidences()
            .iter()
            // Get the ending time of each event.
            .flatten()
            .map(|e| e.end_time())
            // Get the maximum time.
            .max_by(|a, b| a.partial_cmp(b).unwrap())
            // Unwrap the maximum time.
            .unwrap_or(0.);

        // Sort the evidence by starting time, adding initial and ending time.
        let times: Array1<_> = self
            .evidence
            .evidences()
            .iter()
            // Get the starting time of each event.
            .flatten()
            .map(|e| e.start_time())
            // Add initial and ending time.
            .chain([0., end_time])
            // Sort the times.
            .sorted_by(|a, b| a.partial_cmp(b).unwrap())
            // Deduplicate the times to aggregate the events.
            .dedup()
            .collect();

        // Allocate the matrix of events with unknown states.
        let mut events = Array2::from_elem((times.len(), states.len()), M);

        // Reduce the uncertain evidences to certain evidences.
        let evidence = self.sample_evidence();

        // Set the states of the events given the evidence.
        Zip::from(&times)
            .and(events.axis_iter_mut(Axis(0)))
            .par_for_each(|time, mut event| {
                // For each event, set the state of the variable at that time, if any.
                event.iter_mut().enumerate().for_each(|(i, e)| {
                    // Get the evidence vector for that variable.
                    let e_i = &evidence.evidences()[i];
                    // Get the evidence for that time.
                    let e_i_t = e_i.iter().find(|e| e.contains(time));
                    // If the evidence is present, set the state.
                    if let Some(e_i_t) = e_i_t {
                        match e_i_t {
                            E::CertainPositiveInterval { state, .. } => *e = *state as CatType,
                            E::CertainNegativeInterval { .. } => todo!(), // FIXME:
                            _ => unreachable!(), // Due to the previous assertions, this should never happen.
                        }
                    }
                });
            });

        // Get the events with no evidence at all.
        let no_evidence: Vec<_> = events
            .axis_iter(Axis(1))
            .into_par_iter()
            .enumerate()
            .filter_map(|(i, e)| {
                if e.iter().all(|&x| x == M) {
                    Some(i)
                } else {
                    None
                }
            })
            .collect();
        // If no evidence is present, fill it randomly.
        for i in no_evidence {
            // Sample a state uniformly at random.
            let random_state = Array::from_iter({
                let random_state = || self.rng.random_range(0..(states[i].len() as CatType));
                std::iter::repeat_with(random_state).take(events.nrows())
            });
            // Fill the event with the sampled state.
            events.column_mut(i).assign(&random_state);
        }

        // Fill the unknown states by propagating the known states.
        events
            .axis_iter_mut(Axis(1))
            .into_par_iter()
            .for_each(|mut event| {
                // Set the first known state position.
                let mut first_known = 0;
                // Check if the first state is known.
                if event[first_known] == M {
                    // If the first state is unknown, get the first known state.
                    // NOTE: Safe unwrap since we know at least one state is present.
                    first_known = event.iter().position(|e| *e != M).unwrap();
                    // Get the event to fill with.
                    let e = event[first_known];
                    // Backward fill the unknown states.
                    event.slice_mut(s![..first_known]).fill(e);
                }
                // Set the first known state position as the last known state position.
                let mut last_known = first_known;
                // Get the first unknown state.
                while let Some(first_unknown) = event.iter().skip(last_known).position(|e| *e == M)
                {
                    // Add displacement to the first known state position because we skipped some elements.
                    let first_unknown = first_unknown + last_known;
                    // Get the last known state.
                    // NOTE: Safe because we know at least one state is present.
                    let e = event[first_unknown - 1];
                    // Get the last unknown state after the first unknown state.
                    // NOTE: We get the "first known state after the first unknown state",
                    // but we fill with an excluding range, so we can use the same position.
                    let last_unknown = event.iter().skip(first_unknown).position(|e| *e != M);
                    // Add displacement to the first unknown state position because we skipped some elements.
                    let last_unknown =
                        last_unknown.map(|last_unknown| last_unknown + first_unknown);
                    // If no last unknown state, set the end.
                    let last_unknown = last_unknown.unwrap_or(event.len());
                    // Fill the unknown states with the last known state, or till the end if none.
                    event.slice_mut(s![first_unknown..last_unknown]).fill(e);
                    // Set the last known state position as the last unknown state position.
                    last_known = last_unknown;
                }
            });

        // Initialize the events and times with first event and time, if any.
        let mut new_events: Vec<_> = events
            .rows()
            .into_iter()
            .map(|x| x.to_owned())
            .take(1)
            .collect();
        let mut new_times: Vec<_> = times.iter().cloned().take(1).collect();

        // Check if there is at max one state change per transition.
        events
            .rows()
            .into_iter()
            .zip(&times)
            .tuple_windows()
            .for_each(|((e_i, t_i), (e_j, t_j))| {
                // Count the number of state changes.
                let mut diff: Vec<_> = e_i
                    .indexed_iter()
                    .zip(e_j.indexed_iter())
                    .filter_map(|(i, j)| if i != j { Some(j) } else { None })
                    .collect();
                // Check if there is at most one state change.
                if diff.len() <= 1 {
                    // Add the event and time to the new events.
                    new_events.push(e_j.to_owned());
                    new_times.push(*t_j);
                    // Nothing to fix, just return.
                    return;
                }
                // Otherwise, we have multiple state changes.
                // Shuffle them to generate a transition order.
                diff.shuffle(self.rng);
                // Ignore the last state change to avoid overlap with the next event.
                diff.pop();
                // Get the first state change.
                let (mut e_k, mut t_k) = (e_i.to_owned(), *t_i);
                // Compute uniform time delta.
                let t_delta = (t_j - t_i) / (diff.len() + 1) as f64;
                // Generate the events to add to fill the gaps between e_i and e_j.
                diff.into_iter().for_each(|(i, x)| {
                    // Set the state to the event.
                    e_k[i] = *x;
                    // Set the time to the event.
                    t_k += t_delta;
                    // Add the event and time to the new events.
                    new_events.push(e_k.clone());
                    new_times.push(t_k);
                });
                // Add the last event and time to the new events.
                new_events.push(e_j.to_owned());
                new_times.push(*t_j);
            });

        // Reshape the events to the number of events and states.
        let events = Array::from_iter(new_events.into_iter().flatten())
            .into_shape_with_order((new_times.len(), states.len()))
            .expect("Failed to reshape events.");
        // Reshape the times to the number of events.
        let times = Array::from_iter(new_times);

        // Construct the fully observed trajectory.
        CatTrj::new(states, events, times)
    }
}

impl<'a, R: Rng + SeedableRng> RAWE<'a, R, CatTrjsEv, CatTrjs> {
    /// Constructs a new raw estimator from the evidence.
    ///
    /// # Arguments
    ///
    /// * `evidence` - A reference to the evidence to fill.
    ///
    /// # Returns
    ///
    /// A new `RAWE` instance.
    ///
    pub fn par_new(rng: &'a mut R, evidence: &'a CatTrjsEv) -> Self {
        // Get evidence.
        let _evidence = evidence.evidences();
        // Sample seed for parallel sampling.
        let seeds: Vec<_> = (0.._evidence.len()).map(|_| rng.next_u64()).collect();
        // Fill the evidence with the raw estimator.
        let dataset: Option<CatTrjs> = Some(
            seeds
                .into_par_iter()
                .zip(_evidence)
                .map(|(seed, e)| {
                    // Create a new random number generator with the seed.
                    let mut rng = R::seed_from_u64(seed);
                    // Fill the evidence with the raw estimator.
                    RAWE::<'_, R, CatTrjEv, CatTrj>::par_new(&mut rng, e)
                        .dataset
                        .unwrap()
                })
                .collect(),
        );

        Self {
            rng,
            evidence,
            dataset,
        }
    }
}

impl<R: Rng + SeedableRng> CIMEstimator<CatCIM> for RAWE<'_, R, CatTrjEv, CatTrj> {
    fn fit(&self, x: &Set<usize>, z: &Set<usize>) -> CatCIM {
        // Estimate the CIM with a uniform prior.
        BE::new(self.dataset.as_ref().unwrap())
            .with_prior((1, 1.))
            .fit(x, z)
    }
}

impl<R: Rng + SeedableRng> CIMEstimator<CatCIM> for RAWE<'_, R, CatTrjsEv, CatTrjs> {
    fn fit(&self, x: &Set<usize>, z: &Set<usize>) -> CatCIM {
        // Estimate the CIM with a uniform prior.
        BE::new(self.dataset.as_ref().unwrap())
            .with_prior((1, 1.))
            .fit(x, z)
    }
}

impl<R: Rng + SeedableRng> ParCIMEstimator<CatCIM> for RAWE<'_, R, CatTrjsEv, CatTrjs> {
    fn par_fit(&self, x: &Set<usize>, z: &Set<usize>) -> CatCIM {
        // Estimate the CIM with a uniform prior.
        BE::new(self.dataset.as_ref().unwrap())
            .with_prior((1, 1.))
            .par_fit(x, z)
    }
}