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