antecedent_core/query/
mediation.rs1use std::sync::Arc;
6
7use crate::ids::VariableId;
8use crate::intervention::Intervention;
9use crate::value::Value;
10
11use super::AverageEffectQuery;
12use super::TargetPopulation;
13use super::error::QueryError;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
16pub enum MediationContrast {
18 Total,
20 Direct,
22 Mediated,
24 NaturalDirect,
26 NaturalIndirect,
28}
29
30#[derive(Clone, Debug, Eq, PartialEq, Hash)]
32pub struct MediationQuery {
33 pub treatment: VariableId,
35 pub outcome: VariableId,
37 pub mediators: Arc<[VariableId]>,
39 pub contrast: MediationContrast,
41 pub control: Intervention,
43 pub active: Intervention,
45 pub target_population: TargetPopulation,
47}
48
49impl MediationQuery {
50 #[must_use]
52 pub fn binary(
53 treatment: VariableId,
54 outcome: VariableId,
55 mediators: impl Into<Arc<[VariableId]>>,
56 contrast: MediationContrast,
57 ) -> Self {
58 Self {
59 treatment,
60 outcome,
61 mediators: mediators.into(),
62 contrast,
63 control: Intervention::set(treatment, Value::f64(0.0)),
64 active: Intervention::set(treatment, Value::f64(1.0)),
65 target_population: TargetPopulation::AllObserved,
66 }
67 }
68
69 pub fn validate(&self) -> Result<(), QueryError> {
75 if self.treatment == self.outcome {
76 return Err(QueryError::TreatmentEqualsOutcome { id: self.treatment });
77 }
78 if self.mediators.is_empty() {
79 return Err(QueryError::EmptyMediators);
80 }
81 if self.mediators.iter().any(|&m| m == self.treatment || m == self.outcome) {
82 return Err(QueryError::MediatorOverlapsTreatmentOrOutcome);
83 }
84 let control_var =
85 self.control.primary_variable().ok_or(QueryError::AmbiguousInterventionTarget)?;
86 if control_var != self.treatment {
87 return Err(QueryError::InterventionVariableMismatch {
88 expected: self.treatment,
89 got: control_var,
90 });
91 }
92 let active_var =
93 self.active.primary_variable().ok_or(QueryError::AmbiguousInterventionTarget)?;
94 if active_var != self.treatment {
95 return Err(QueryError::InterventionVariableMismatch {
96 expected: self.treatment,
97 got: active_var,
98 });
99 }
100 self.target_population.validate()?;
101 Ok(())
102 }
103}
104
105#[derive(Clone, Debug, Eq, PartialEq, Hash)]
107pub struct ConditionalEffectQuery {
108 pub inner: AverageEffectQuery,
110}
111
112impl ConditionalEffectQuery {
113 pub fn try_new(inner: AverageEffectQuery) -> Result<Self, QueryError> {
119 if inner.effect_modifiers.is_empty() {
120 return Err(QueryError::EmptyEffectModifiers);
121 }
122 inner.validate()?;
123 Ok(Self { inner })
124 }
125
126 pub fn validate(&self) -> Result<(), QueryError> {
132 if self.inner.effect_modifiers.is_empty() {
133 return Err(QueryError::EmptyEffectModifiers);
134 }
135 self.inner.validate()
136 }
137}