Skip to main content

antecedent_core/query/
mod.rs

1//! Typed causal queries.
2//!
3//! Hot paths bind [`VariableId`]s; names are resolved only at API boundaries.
4//!
5//! SPDX-License-Identifier: MIT OR Apache-2.0
6
7mod attribution;
8mod average;
9mod counterfactual;
10mod distribution;
11mod error;
12mod mediation;
13mod population;
14mod target;
15mod temporal;
16
17pub use crate::intervention::TemporalPolicy;
18
19pub use attribution::{
20    AllocationMethod, AnomalyAttributionQuery, AttributionComponents, ChangeAttributionQuery,
21    MechanismChangeQuery, OrderedFloatBits, PopulationSelector, ShapleyConfig, ShapleyMode,
22    UnitChangeQuery,
23};
24pub use average::AverageEffectQuery;
25pub use counterfactual::CounterfactualQuery;
26pub use distribution::{InterventionalDistributionQuery, PathSpecificEffectQuery};
27pub use error::QueryError;
28pub use mediation::{ConditionalEffectQuery, MediationContrast, MediationQuery};
29pub use population::{PopulationRegistry, PopulationSelection};
30pub use target::{PredicateExpr, TargetPopulation};
31pub use temporal::TemporalEffectQuery;
32
33/// Top-level causal query enum.
34#[derive(Clone, Debug, PartialEq)]
35#[non_exhaustive]
36pub enum CausalQuery {
37    /// Average / population effect (static).
38    AverageEffect(AverageEffectQuery),
39    /// Temporal effect over a discrete horizon.
40    TemporalEffect(TemporalEffectQuery),
41    /// Counterfactual / unit-level what-if query .
42    Counterfactual(CounterfactualQuery),
43    /// Anomaly attribution for one or more units .
44    AnomalyAttribution(AnomalyAttributionQuery),
45    /// Distribution / population change attribution .
46    ChangeAttribution(ChangeAttributionQuery),
47    /// Mechanism-change detection — not attribution .
48    MechanismChange(MechanismChangeQuery),
49    /// Per-unit change attribution .
50    UnitChange(UnitChangeQuery),
51    /// Mediation (direct / mediated / natural effects).
52    Mediation(MediationQuery),
53    /// Conditional average effect given modifiers.
54    ConditionalEffect(ConditionalEffectQuery),
55    /// Interventional distribution P(Y | do(...)).
56    Distribution(InterventionalDistributionQuery),
57    /// Path-specific effect / contribution.
58    PathSpecific(PathSpecificEffectQuery),
59}
60
61impl CausalQuery {
62    /// Construct an average-effect query.
63    #[must_use]
64    pub fn average_effect(query: AverageEffectQuery) -> Self {
65        Self::AverageEffect(query)
66    }
67
68    /// Construct a temporal-effect query.
69    #[must_use]
70    pub fn temporal_effect(query: TemporalEffectQuery) -> Self {
71        Self::TemporalEffect(query)
72    }
73
74    /// Construct a counterfactual query.
75    #[must_use]
76    pub fn counterfactual(query: CounterfactualQuery) -> Self {
77        Self::Counterfactual(query)
78    }
79
80    /// Construct an anomaly attribution query.
81    #[must_use]
82    pub fn anomaly_attribution(query: AnomalyAttributionQuery) -> Self {
83        Self::AnomalyAttribution(query)
84    }
85
86    /// Construct a change attribution query.
87    #[must_use]
88    pub fn change_attribution(query: ChangeAttributionQuery) -> Self {
89        Self::ChangeAttribution(query)
90    }
91
92    /// Construct a mechanism-change detection query.
93    #[must_use]
94    pub fn mechanism_change(query: MechanismChangeQuery) -> Self {
95        Self::MechanismChange(query)
96    }
97
98    /// Construct a unit-change attribution query.
99    #[must_use]
100    pub fn unit_change(query: UnitChangeQuery) -> Self {
101        Self::UnitChange(query)
102    }
103
104    /// Construct a mediation query.
105    #[must_use]
106    pub fn mediation(query: MediationQuery) -> Self {
107        Self::Mediation(query)
108    }
109
110    /// Construct a conditional-effect query.
111    #[must_use]
112    pub fn conditional_effect(query: ConditionalEffectQuery) -> Self {
113        Self::ConditionalEffect(query)
114    }
115
116    /// Construct an interventional-distribution query.
117    #[must_use]
118    pub fn distribution(query: InterventionalDistributionQuery) -> Self {
119        Self::Distribution(query)
120    }
121
122    /// Construct a path-specific effect query.
123    #[must_use]
124    pub fn path_specific(query: PathSpecificEffectQuery) -> Self {
125        Self::PathSpecific(query)
126    }
127}
128
129impl From<AverageEffectQuery> for CausalQuery {
130    fn from(query: AverageEffectQuery) -> Self {
131        Self::AverageEffect(query)
132    }
133}
134
135impl From<TemporalEffectQuery> for CausalQuery {
136    fn from(query: TemporalEffectQuery) -> Self {
137        Self::TemporalEffect(query)
138    }
139}
140
141impl From<CounterfactualQuery> for CausalQuery {
142    fn from(query: CounterfactualQuery) -> Self {
143        Self::Counterfactual(query)
144    }
145}
146
147impl From<MediationQuery> for CausalQuery {
148    fn from(query: MediationQuery) -> Self {
149        Self::Mediation(query)
150    }
151}
152
153impl From<ConditionalEffectQuery> for CausalQuery {
154    fn from(query: ConditionalEffectQuery) -> Self {
155        Self::ConditionalEffect(query)
156    }
157}
158
159impl From<InterventionalDistributionQuery> for CausalQuery {
160    fn from(query: InterventionalDistributionQuery) -> Self {
161        Self::Distribution(query)
162    }
163}
164
165impl From<PathSpecificEffectQuery> for CausalQuery {
166    fn from(query: PathSpecificEffectQuery) -> Self {
167        Self::PathSpecific(query)
168    }
169}
170
171impl From<AnomalyAttributionQuery> for CausalQuery {
172    fn from(query: AnomalyAttributionQuery) -> Self {
173        Self::AnomalyAttribution(query)
174    }
175}
176
177impl From<ChangeAttributionQuery> for CausalQuery {
178    fn from(query: ChangeAttributionQuery) -> Self {
179        Self::ChangeAttribution(query)
180    }
181}
182
183impl From<MechanismChangeQuery> for CausalQuery {
184    fn from(query: MechanismChangeQuery) -> Self {
185        Self::MechanismChange(query)
186    }
187}
188
189impl From<UnitChangeQuery> for CausalQuery {
190    fn from(query: UnitChangeQuery) -> Self {
191        Self::UnitChange(query)
192    }
193}
194
195impl CausalQuery {
196    /// Whether this query is the static ATE path.
197    #[must_use]
198    pub const fn is_static_ate(&self) -> bool {
199        matches!(self, Self::AverageEffect(_))
200    }
201
202    /// Whether this query is a temporal effect.
203    #[must_use]
204    pub const fn is_temporal_effect(&self) -> bool {
205        matches!(self, Self::TemporalEffect(_))
206    }
207
208    /// Whether this query is counterfactual.
209    #[must_use]
210    pub const fn is_counterfactual(&self) -> bool {
211        matches!(self, Self::Counterfactual(_))
212    }
213
214    /// Whether this query is mediation.
215    #[must_use]
216    pub const fn is_mediation(&self) -> bool {
217        matches!(self, Self::Mediation(_))
218    }
219
220    /// Whether this query is a conditional effect.
221    #[must_use]
222    pub const fn is_conditional_effect(&self) -> bool {
223        matches!(self, Self::ConditionalEffect(_))
224    }
225
226    /// Whether this query is an interventional distribution.
227    #[must_use]
228    pub const fn is_distribution(&self) -> bool {
229        matches!(self, Self::Distribution(_))
230    }
231
232    /// Whether this query is path-specific.
233    #[must_use]
234    pub const fn is_path_specific(&self) -> bool {
235        matches!(self, Self::PathSpecific(_))
236    }
237
238    /// Validate the inner query.
239    ///
240    /// # Errors
241    ///
242    /// Propagates inner [`QueryError`].
243    pub fn validate(&self) -> Result<(), QueryError> {
244        match self {
245            Self::AverageEffect(q) => q.validate(),
246            Self::TemporalEffect(q) => q.validate(),
247            Self::Counterfactual(q) => q.validate(),
248            Self::AnomalyAttribution(q) => q.validate(),
249            Self::ChangeAttribution(q) => q.validate(),
250            Self::MechanismChange(q) => q.validate(),
251            Self::UnitChange(q) => q.validate(),
252            Self::Mediation(q) => q.validate(),
253            Self::ConditionalEffect(q) => q.validate(),
254            Self::Distribution(q) => q.validate(),
255            Self::PathSpecific(q) => q.validate(),
256        }
257    }
258}
259
260#[cfg(test)]
261#[path = "tests.rs"]
262mod tests;