pub mod difference_in_differences;
pub mod instrumental_variables;
pub mod propensity_score;
pub mod regression_discontinuity;
pub use instrumental_variables::{
HausmanResult, HausmanTest, IVEstimator, IVResult, WeakInstrumentResult, WeakInstrumentTest,
LIML,
};
pub use difference_in_differences::{
AttGt, DiD, DiDResult, EventCoefficient, EventStudy, EventStudyResult, StaggeredDiD,
StaggeredDiDResult, SyntheticControl,
};
pub use regression_discontinuity::{
BandwidthMethod, BandwidthSelector, FuzzyRDD, RDDPlot, RDDResult, RDD,
};
pub use propensity_score::{
MatchingMethod, MatchingResult, OverlapCheck, OverlapResult, PSMatching, PSResult,
PropensityScoreModel, TrimMethod, IPW,
};
pub use propensity_score::ps_estimate;
pub mod sem;
pub use sem::{satisfies_backdoor, IdentificationResult, LinearEquation, SEMWithIntercepts, SEM};
pub mod conditional_independence;
pub mod fci_algorithm;
pub mod hedge;
pub mod id_algorithm;
pub mod linear_sem;
pub mod pc_algorithm;
pub mod semi_markov_graph;
pub mod symbolic_prob;
pub use linear_sem::{LinearSEM, LinearSEMWithIntercepts};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EdgeMark {
Tail,
Arrow,
Circle,
}
#[derive(Debug, Clone)]
pub struct CausalGraph {
pub var_names: Vec<String>,
edges: Vec<Vec<Option<(EdgeMark, EdgeMark)>>>,
pub sep_sets: Vec<Vec<Option<Vec<usize>>>>,
}
impl CausalGraph {
pub fn new(var_names: &[&str]) -> Self {
let p = var_names.len();
Self {
var_names: var_names.iter().map(|s| s.to_string()).collect(),
edges: vec![vec![None; p]; p],
sep_sets: vec![vec![None; p]; p],
}
}
pub fn num_vars(&self) -> usize {
self.var_names.len()
}
pub fn set_edge(&mut self, i: usize, j: usize, mark_at_i: EdgeMark, mark_at_j: EdgeMark) {
self.edges[i][j] = Some((mark_at_i, mark_at_j));
self.edges[j][i] = Some((mark_at_j, mark_at_i));
}
pub fn remove_edge(&mut self, i: usize, j: usize) {
self.edges[i][j] = None;
self.edges[j][i] = None;
}
pub fn is_adjacent(&self, i: usize, j: usize) -> bool {
self.edges[i][j].is_some()
}
pub fn get_mark_at(&self, from: usize, to: usize) -> Option<EdgeMark> {
self.edges[from][to].map(|(_, mark_at_to)| mark_at_to)
}
pub fn get_mark_from(&self, from: usize, to: usize) -> Option<EdgeMark> {
self.edges[from][to].map(|(mark_at_from, _)| mark_at_from)
}
pub fn is_directed(&self, i: usize, j: usize) -> bool {
matches!(self.edges[i][j], Some((EdgeMark::Tail, EdgeMark::Arrow)))
}
pub fn is_undirected(&self, i: usize, j: usize) -> bool {
matches!(self.edges[i][j], Some((EdgeMark::Tail, EdgeMark::Tail)))
}
pub fn is_bidirected(&self, i: usize, j: usize) -> bool {
matches!(self.edges[i][j], Some((EdgeMark::Arrow, EdgeMark::Arrow)))
}
pub fn neighbors(&self, i: usize) -> impl Iterator<Item = usize> + '_ {
(0..self.num_vars()).filter(move |&j| j != i && self.is_adjacent(i, j))
}
pub fn get_sep_set(&self, i: usize, j: usize) -> Option<&Vec<usize>> {
self.sep_sets[i][j].as_ref()
}
pub fn set_sep_set(&mut self, i: usize, j: usize, sep: Vec<usize>) {
self.sep_sets[i][j] = Some(sep.clone());
self.sep_sets[j][i] = Some(sep);
}
pub fn make_complete(&mut self) {
let p = self.num_vars();
for i in 0..p {
for j in 0..p {
if i != j {
self.edges[i][j] = Some((EdgeMark::Tail, EdgeMark::Tail));
}
}
}
}
}