antecedent_graph/
selection.rs1use std::collections::BTreeSet;
6use std::sync::Arc;
7
8use antecedent_core::VariableId;
9
10use crate::{Admg, GraphError};
11
12#[derive(Clone, Debug)]
18pub struct SelectionDiagram {
19 causal_graph: Admg,
20 selection_targets: Arc<[VariableId]>,
21}
22
23impl SelectionDiagram {
24 pub fn try_new(
26 causal_graph: Admg,
27 selection_targets: impl Into<Arc<[VariableId]>>,
28 ) -> Result<Self, GraphError> {
29 let selection_targets = selection_targets.into();
30 let mut seen = BTreeSet::new();
31 for target in selection_targets.iter().copied() {
32 if target.as_usize() >= causal_graph.node_count() {
33 return Err(GraphError::UnknownNode { id: target.raw() });
34 }
35 if !seen.insert(target.raw()) {
36 return Err(GraphError::InvalidSelectionDiagram {
37 message: "selection targets must be unique".into(),
38 });
39 }
40 }
41 Ok(Self { causal_graph, selection_targets })
42 }
43
44 #[must_use]
46 pub const fn causal_graph(&self) -> &Admg {
47 &self.causal_graph
48 }
49
50 #[must_use]
52 pub fn selection_targets(&self) -> &[VariableId] {
53 &self.selection_targets
54 }
55
56 #[must_use]
58 pub fn mechanism_may_differ(&self, variable: VariableId) -> bool {
59 self.selection_targets.contains(&variable)
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn selection_targets_are_not_graph_nodes() {
69 let graph = Admg::with_variables(3);
70 let diagram = SelectionDiagram::try_new(graph, [VariableId::from_raw(1)]).unwrap();
71 assert_eq!(diagram.causal_graph().node_count(), 3);
72 assert!(diagram.mechanism_may_differ(VariableId::from_raw(1)));
73 }
74}