Skip to main content

antecedent_core/query/
mod.rs

1//! Typed causal queries.
2//!
3//! Hot paths bind [`VariableId`](crate::ids::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 interference;
13mod mediation;
14mod population;
15mod response;
16mod target;
17mod temporal;
18mod transport;
19
20pub use crate::intervention::TemporalPolicy;
21
22pub use attribution::{
23    AllocationMethod, AnomalyAttributionQuery, AttributionComponents, ChangeAttributionQuery,
24    MechanismChangeQuery, OrderedFloatBits, PopulationSelector, ShapleyConfig, ShapleyMode,
25    UnitChangeQuery,
26};
27pub use average::AverageEffectQuery;
28pub use counterfactual::CounterfactualQuery;
29pub use distribution::{InterventionalDistributionQuery, PathSpecificEffectQuery};
30pub use error::QueryError;
31pub use interference::{
32    AssignmentDesign, EXPOSURE_LEVEL_TOLERANCE, ExposureLevel, ExposureMapping,
33    InterferenceFunctional, InterferenceQuery,
34};
35pub use mediation::{ConditionalEffectQuery, MediationContrast, MediationQuery};
36pub use population::{PopulationRegistry, PopulationSelection};
37pub use response::{
38    ContinuousDomain, DerivativeScale, DerivativeWeighting, GridSpec,
39    MAX_NONPARAMETRIC_RESPONSE_DIM, MAX_TEMPORAL_RESPONSE_HORIZONS, ObservationAssumption,
40    ObservationSpec, ResponseFunctional, ResponseQuery, TemporalResponseLicense,
41    TemporalResponseSpec,
42};
43pub use target::{PredicateExpr, TargetPopulation};
44pub use temporal::TemporalEffectQuery;
45pub use transport::TransportQuery;
46
47/// Top-level causal query enum.
48#[derive(Clone, Debug, PartialEq)]
49#[non_exhaustive]
50pub enum CausalQuery {
51    /// Average / population effect (static).
52    AverageEffect(AverageEffectQuery),
53    /// Temporal effect over a discrete horizon.
54    TemporalEffect(TemporalEffectQuery),
55    /// Counterfactual / unit-level what-if query .
56    Counterfactual(CounterfactualQuery),
57    /// Anomaly attribution for one or more units .
58    AnomalyAttribution(AnomalyAttributionQuery),
59    /// Distribution / population change attribution .
60    ChangeAttribution(ChangeAttributionQuery),
61    /// Mechanism-change detection — not attribution .
62    MechanismChange(MechanismChangeQuery),
63    /// Per-unit change attribution .
64    UnitChange(UnitChangeQuery),
65    /// Mediation (direct / mediated / natural effects).
66    Mediation(MediationQuery),
67    /// Conditional average effect given modifiers.
68    ConditionalEffect(ConditionalEffectQuery),
69    /// Interventional distribution P(Y | do(...)).
70    Distribution(InterventionalDistributionQuery),
71    /// Path-specific effect / contribution.
72    PathSpecific(PathSpecificEffectQuery),
73    /// Continuous response, derivative, policy response, or Jacobian.
74    Response(ResponseQuery),
75    /// Structurally transported response between populations.
76    Transport(TransportQuery),
77    /// Randomization-based causal effect under interference.
78    Interference(InterferenceQuery),
79}
80
81impl CausalQuery {
82    /// Construct an average-effect query.
83    #[must_use]
84    pub fn average_effect(query: AverageEffectQuery) -> Self {
85        Self::AverageEffect(query)
86    }
87
88    /// Construct a temporal-effect query.
89    #[must_use]
90    pub fn temporal_effect(query: TemporalEffectQuery) -> Self {
91        Self::TemporalEffect(query)
92    }
93
94    /// Construct a counterfactual query.
95    #[must_use]
96    pub fn counterfactual(query: CounterfactualQuery) -> Self {
97        Self::Counterfactual(query)
98    }
99
100    /// Construct an anomaly attribution query.
101    #[must_use]
102    pub fn anomaly_attribution(query: AnomalyAttributionQuery) -> Self {
103        Self::AnomalyAttribution(query)
104    }
105
106    /// Construct a change attribution query.
107    #[must_use]
108    pub fn change_attribution(query: ChangeAttributionQuery) -> Self {
109        Self::ChangeAttribution(query)
110    }
111
112    /// Construct a mechanism-change detection query.
113    #[must_use]
114    pub fn mechanism_change(query: MechanismChangeQuery) -> Self {
115        Self::MechanismChange(query)
116    }
117
118    /// Construct a unit-change attribution query.
119    #[must_use]
120    pub fn unit_change(query: UnitChangeQuery) -> Self {
121        Self::UnitChange(query)
122    }
123
124    /// Construct a mediation query.
125    #[must_use]
126    pub fn mediation(query: MediationQuery) -> Self {
127        Self::Mediation(query)
128    }
129
130    /// Construct a conditional-effect query.
131    #[must_use]
132    pub fn conditional_effect(query: ConditionalEffectQuery) -> Self {
133        Self::ConditionalEffect(query)
134    }
135
136    /// Construct an interventional-distribution query.
137    #[must_use]
138    pub fn distribution(query: InterventionalDistributionQuery) -> Self {
139        Self::Distribution(query)
140    }
141
142    /// Construct a path-specific effect query.
143    #[must_use]
144    pub fn path_specific(query: PathSpecificEffectQuery) -> Self {
145        Self::PathSpecific(query)
146    }
147
148    /// Construct a continuous-response query.
149    #[must_use]
150    pub fn response(query: ResponseQuery) -> Self {
151        Self::Response(query)
152    }
153
154    /// Construct a transportability query.
155    #[must_use]
156    pub fn transport(query: TransportQuery) -> Self {
157        Self::Transport(query)
158    }
159
160    /// Construct an interference query.
161    #[must_use]
162    pub fn interference(query: InterferenceQuery) -> Self {
163        Self::Interference(query)
164    }
165}
166
167impl From<AverageEffectQuery> for CausalQuery {
168    fn from(query: AverageEffectQuery) -> Self {
169        Self::AverageEffect(query)
170    }
171}
172
173impl From<TemporalEffectQuery> for CausalQuery {
174    fn from(query: TemporalEffectQuery) -> Self {
175        Self::TemporalEffect(query)
176    }
177}
178
179impl From<CounterfactualQuery> for CausalQuery {
180    fn from(query: CounterfactualQuery) -> Self {
181        Self::Counterfactual(query)
182    }
183}
184
185impl From<MediationQuery> for CausalQuery {
186    fn from(query: MediationQuery) -> Self {
187        Self::Mediation(query)
188    }
189}
190
191impl From<ConditionalEffectQuery> for CausalQuery {
192    fn from(query: ConditionalEffectQuery) -> Self {
193        Self::ConditionalEffect(query)
194    }
195}
196
197impl From<InterventionalDistributionQuery> for CausalQuery {
198    fn from(query: InterventionalDistributionQuery) -> Self {
199        Self::Distribution(query)
200    }
201}
202
203impl From<PathSpecificEffectQuery> for CausalQuery {
204    fn from(query: PathSpecificEffectQuery) -> Self {
205        Self::PathSpecific(query)
206    }
207}
208
209impl From<AnomalyAttributionQuery> for CausalQuery {
210    fn from(query: AnomalyAttributionQuery) -> Self {
211        Self::AnomalyAttribution(query)
212    }
213}
214
215impl From<ChangeAttributionQuery> for CausalQuery {
216    fn from(query: ChangeAttributionQuery) -> Self {
217        Self::ChangeAttribution(query)
218    }
219}
220
221impl From<MechanismChangeQuery> for CausalQuery {
222    fn from(query: MechanismChangeQuery) -> Self {
223        Self::MechanismChange(query)
224    }
225}
226
227impl From<UnitChangeQuery> for CausalQuery {
228    fn from(query: UnitChangeQuery) -> Self {
229        Self::UnitChange(query)
230    }
231}
232
233impl From<ResponseQuery> for CausalQuery {
234    fn from(query: ResponseQuery) -> Self {
235        Self::Response(query)
236    }
237}
238
239impl From<TransportQuery> for CausalQuery {
240    fn from(query: TransportQuery) -> Self {
241        Self::Transport(query)
242    }
243}
244
245impl From<InterferenceQuery> for CausalQuery {
246    fn from(query: InterferenceQuery) -> Self {
247        Self::Interference(query)
248    }
249}
250
251impl CausalQuery {
252    /// Whether this query is the static ATE path.
253    #[must_use]
254    pub const fn is_static_ate(&self) -> bool {
255        matches!(self, Self::AverageEffect(_))
256    }
257
258    /// Whether this query is a temporal effect.
259    #[must_use]
260    pub const fn is_temporal_effect(&self) -> bool {
261        matches!(self, Self::TemporalEffect(_))
262    }
263
264    /// Whether this query is counterfactual.
265    #[must_use]
266    pub const fn is_counterfactual(&self) -> bool {
267        matches!(self, Self::Counterfactual(_))
268    }
269
270    /// Whether this query is mediation.
271    #[must_use]
272    pub const fn is_mediation(&self) -> bool {
273        matches!(self, Self::Mediation(_))
274    }
275
276    /// Whether this query is a conditional effect.
277    #[must_use]
278    pub const fn is_conditional_effect(&self) -> bool {
279        matches!(self, Self::ConditionalEffect(_))
280    }
281
282    /// Whether this query is an interventional distribution.
283    #[must_use]
284    pub const fn is_distribution(&self) -> bool {
285        matches!(self, Self::Distribution(_))
286    }
287
288    /// Whether this query is path-specific.
289    #[must_use]
290    pub const fn is_path_specific(&self) -> bool {
291        matches!(self, Self::PathSpecific(_))
292    }
293
294    /// Validate the inner query.
295    ///
296    /// # Errors
297    ///
298    /// Propagates inner [`QueryError`].
299    pub fn validate(&self) -> Result<(), QueryError> {
300        match self {
301            Self::AverageEffect(q) => q.validate(),
302            Self::TemporalEffect(q) => q.validate(),
303            Self::Counterfactual(q) => q.validate(),
304            Self::AnomalyAttribution(q) => q.validate(),
305            Self::ChangeAttribution(q) => q.validate(),
306            Self::MechanismChange(q) => q.validate(),
307            Self::UnitChange(q) => q.validate(),
308            Self::Mediation(q) => q.validate(),
309            Self::ConditionalEffect(q) => q.validate(),
310            Self::Distribution(q) => q.validate(),
311            Self::PathSpecific(q) => q.validate(),
312            Self::Response(q) => q.validate(),
313            Self::Transport(q) => q.validate(),
314            Self::Interference(q) => q.validate(),
315        }
316    }
317}
318
319#[cfg(test)]
320#[path = "tests.rs"]
321mod tests;