1use crate::ids::VariableId;
6
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub enum QueryError {
10 TreatmentEqualsOutcome {
12 id: VariableId,
14 },
15 InterventionVariableMismatch {
17 expected: VariableId,
19 got: VariableId,
21 },
22 AmbiguousInterventionTarget,
24 ModifierOverlapsTreatmentOrOutcome,
26 InvalidTemporalWindow {
28 from: i32,
30 until: i32,
32 },
33 NonPositiveHorizon,
35 InvalidIntervention(String),
37 EmptyCounterfactualOutcomes,
39 EmptyAnomalyTargets,
41 NonPositiveAnomalyLimit,
43 EmptyMediators,
45 MediatorOverlapsTreatmentOrOutcome,
47 EmptyEffectModifiers,
49 EmptyPopulationRows,
51 EmptyPredicateName,
53 DynamicPolicyHasNoTreatmentOffset,
55 InvalidPopulationTimeRange {
57 start: usize,
59 end: usize,
61 },
62 EmptyAllocationOrder,
64 NonPositiveShapleyLimit,
66 NonPositiveShapleySamples,
68 NonPositiveComponentLimit,
70 EmptyMechanismChangeTargets,
72 InvalidSignificanceLevel,
74 EmptyDistributionOutcomes,
76 NonPositivePathLimit,
78 PathNodeOverlapsTreatmentOrOutcome,
80 ConditioningOverlapsOutcomeOrIntervention,
82 PopulationRegistryRequired,
84 UnknownPredicateName {
86 name: std::sync::Arc<str>,
88 },
89 UnknownDistributionRef {
91 id: u32,
93 },
94 PopulationNeedsTreatment,
96 PopulationNonBinaryTreatment,
98 PopulationLengthMismatch {
100 expected: usize,
102 actual: usize,
104 },
105 PopulationRowOutOfRange {
107 row: usize,
109 n: usize,
111 },
112 PopulationEnvironmentUnsupported,
114 InvalidPopulationWeights,
116}
117
118impl core::fmt::Display for QueryError {
119 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120 match self {
121 Self::TreatmentEqualsOutcome { id } => {
122 write!(f, "treatment and outcome are the same variable {id}")
123 }
124 Self::InterventionVariableMismatch { expected, got } => {
125 write!(f, "intervention targets {got}, expected treatment {expected}")
126 }
127 Self::AmbiguousInterventionTarget => {
128 write!(f, "intervention does not have a unique target variable")
129 }
130 Self::ModifierOverlapsTreatmentOrOutcome => {
131 write!(f, "effect modifier overlaps treatment or outcome")
132 }
133 Self::InvalidTemporalWindow { from, until } => {
134 write!(f, "invalid temporal window [{from}, {until}]")
135 }
136 Self::NonPositiveHorizon => write!(f, "horizon_steps must be >= 1"),
137 Self::InvalidIntervention(msg) => write!(f, "invalid intervention: {msg}"),
138 Self::EmptyCounterfactualOutcomes => {
139 write!(f, "counterfactual query requires at least one outcome")
140 }
141 Self::EmptyAnomalyTargets => write!(f, "anomaly attribution requires targets"),
142 Self::NonPositiveAnomalyLimit => write!(f, "anomaly max_units must be >= 1"),
143 Self::EmptyMediators => write!(f, "mediation query requires mediators"),
144 Self::MediatorOverlapsTreatmentOrOutcome => {
145 write!(f, "mediator overlaps treatment or outcome")
146 }
147 Self::EmptyEffectModifiers => {
148 write!(f, "conditional effect requires non-empty effect modifiers")
149 }
150 Self::EmptyPopulationRows => write!(f, "population selector has no rows"),
151 Self::EmptyPredicateName => write!(f, "predicate name must be non-empty"),
152 Self::DynamicPolicyHasNoTreatmentOffset => {
153 write!(f, "TemporalPolicy::Dynamic has no single treatment offset")
154 }
155 Self::InvalidPopulationTimeRange { start, end } => {
156 write!(f, "invalid population time range [{start}, {end})")
157 }
158 Self::EmptyAllocationOrder => write!(f, "sequential allocation order is empty"),
159 Self::NonPositiveShapleyLimit => {
160 write!(f, "Shapley max_exact_components must be >= 1")
161 }
162 Self::NonPositiveShapleySamples => {
163 write!(f, "Shapley sample / permutation count must be >= 1")
164 }
165 Self::NonPositiveComponentLimit => {
166 write!(f, "max_components / max_targets must be >= 1")
167 }
168 Self::EmptyMechanismChangeTargets => {
169 write!(f, "mechanism-change detection requires targets")
170 }
171 Self::InvalidSignificanceLevel => {
172 write!(f, "significance level must be in (0, 1)")
173 }
174 Self::EmptyDistributionOutcomes => {
175 write!(f, "interventional distribution requires at least one outcome")
176 }
177 Self::NonPositivePathLimit => {
178 write!(f, "path max_paths / max_len must be >= 1")
179 }
180 Self::PathNodeOverlapsTreatmentOrOutcome => {
181 write!(f, "path node overlaps treatment or outcome")
182 }
183 Self::ConditioningOverlapsOutcomeOrIntervention => {
184 write!(f, "distribution conditioning overlaps outcome or intervention")
185 }
186 Self::PopulationRegistryRequired => {
187 write!(f, "named predicate / custom distribution requires a PopulationRegistry")
188 }
189 Self::UnknownPredicateName { name } => {
190 write!(f, "unknown predicate name `{name}`")
191 }
192 Self::UnknownDistributionRef { id } => {
193 write!(f, "unknown DistributionRef({id})")
194 }
195 Self::PopulationNeedsTreatment => {
196 write!(f, "Treated/Untreated population requires a treatment column")
197 }
198 Self::PopulationNonBinaryTreatment => {
199 write!(f, "Treated/Untreated population requires binary 0/1 treatment")
200 }
201 Self::PopulationLengthMismatch { expected, actual } => {
202 write!(f, "population length mismatch: expected {expected}, got {actual}")
203 }
204 Self::PopulationRowOutOfRange { row, n } => {
205 write!(f, "population row {row} out of range for n={n}")
206 }
207 Self::PopulationEnvironmentUnsupported => {
208 write!(f, "Environment target population is not resolved by PopulationRegistry")
209 }
210 Self::InvalidPopulationWeights => {
211 write!(f, "custom distribution weights must be finite and non-negative")
212 }
213 }
214 }
215}
216
217impl std::error::Error for QueryError {}