use crate::ids::VariableId;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum QueryError {
TreatmentEqualsOutcome {
id: VariableId,
},
InterventionVariableMismatch {
expected: VariableId,
got: VariableId,
},
AmbiguousInterventionTarget,
ModifierOverlapsTreatmentOrOutcome,
InvalidTemporalWindow {
from: i32,
until: i32,
},
NonPositiveHorizon,
InvalidIntervention(String),
EmptyCounterfactualOutcomes,
EmptyAnomalyTargets,
NonPositiveAnomalyLimit,
EmptyMediators,
MediatorOverlapsTreatmentOrOutcome,
EmptyEffectModifiers,
EmptyPopulationRows,
EmptyPredicateName,
DynamicPolicyHasNoTreatmentOffset,
InvalidPopulationTimeRange {
start: usize,
end: usize,
},
EmptyAllocationOrder,
DuplicateAllocationComponent,
NonPositiveShapleyLimit,
NonPositiveShapleySamples,
NonPositiveComponentLimit,
EmptyMechanismChangeTargets,
InvalidSignificanceLevel,
EmptyDistributionOutcomes,
NonPositivePathLimit,
PathNodeOverlapsTreatmentOrOutcome,
ConditioningOverlapsOutcomeOrIntervention,
PopulationRegistryRequired,
UnknownPredicateName {
name: std::sync::Arc<str>,
},
UnknownDistributionRef {
id: u32,
},
PopulationNeedsTreatment,
PopulationNonBinaryTreatment,
PopulationLengthMismatch {
expected: usize,
actual: usize,
},
PopulationRowOutOfRange {
row: usize,
n: usize,
},
PopulationEnvironmentUnsupported,
InvalidPopulationWeights,
}
impl core::fmt::Display for QueryError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::TreatmentEqualsOutcome { id } => {
write!(f, "treatment and outcome are the same variable {id}")
}
Self::InterventionVariableMismatch { expected, got } => {
write!(f, "intervention targets {got}, expected treatment {expected}")
}
Self::AmbiguousInterventionTarget => {
write!(f, "intervention does not have a unique target variable")
}
Self::ModifierOverlapsTreatmentOrOutcome => {
write!(f, "effect modifier overlaps treatment or outcome")
}
Self::InvalidTemporalWindow { from, until } => {
write!(f, "invalid temporal window [{from}, {until}]")
}
Self::NonPositiveHorizon => write!(f, "horizon_steps must be >= 1"),
Self::InvalidIntervention(msg) => write!(f, "invalid intervention: {msg}"),
Self::EmptyCounterfactualOutcomes => {
write!(f, "counterfactual query requires at least one outcome")
}
Self::EmptyAnomalyTargets => write!(f, "anomaly attribution requires targets"),
Self::NonPositiveAnomalyLimit => write!(f, "anomaly max_units must be >= 1"),
Self::EmptyMediators => write!(f, "mediation query requires mediators"),
Self::MediatorOverlapsTreatmentOrOutcome => {
write!(f, "mediator overlaps treatment or outcome")
}
Self::EmptyEffectModifiers => {
write!(f, "conditional effect requires non-empty effect modifiers")
}
Self::EmptyPopulationRows => write!(f, "population selector has no rows"),
Self::EmptyPredicateName => write!(f, "predicate name must be non-empty"),
Self::DynamicPolicyHasNoTreatmentOffset => {
write!(f, "TemporalPolicy::Dynamic has no single treatment offset")
}
Self::InvalidPopulationTimeRange { start, end } => {
write!(f, "invalid population time range [{start}, {end})")
}
Self::EmptyAllocationOrder => write!(f, "sequential allocation order is empty"),
Self::DuplicateAllocationComponent => {
write!(f, "sequential allocation order contains duplicate components")
}
Self::NonPositiveShapleyLimit => {
write!(f, "Shapley max_exact_components must be >= 1")
}
Self::NonPositiveShapleySamples => {
write!(f, "Shapley sample / permutation count must be >= 1")
}
Self::NonPositiveComponentLimit => {
write!(f, "max_components / max_targets must be >= 1")
}
Self::EmptyMechanismChangeTargets => {
write!(f, "mechanism-change detection requires targets")
}
Self::InvalidSignificanceLevel => {
write!(f, "significance level must be in (0, 1)")
}
Self::EmptyDistributionOutcomes => {
write!(f, "interventional distribution requires at least one outcome")
}
Self::NonPositivePathLimit => {
write!(f, "path max_paths / max_len must be >= 1")
}
Self::PathNodeOverlapsTreatmentOrOutcome => {
write!(f, "path node overlaps treatment or outcome")
}
Self::ConditioningOverlapsOutcomeOrIntervention => {
write!(f, "distribution conditioning overlaps outcome or intervention")
}
Self::PopulationRegistryRequired => {
write!(f, "named predicate / custom distribution requires a PopulationRegistry")
}
Self::UnknownPredicateName { name } => {
write!(f, "unknown predicate name `{name}`")
}
Self::UnknownDistributionRef { id } => {
write!(f, "unknown DistributionRef({id})")
}
Self::PopulationNeedsTreatment => {
write!(f, "Treated/Untreated population requires a treatment column")
}
Self::PopulationNonBinaryTreatment => {
write!(f, "Treated/Untreated population requires binary 0/1 treatment")
}
Self::PopulationLengthMismatch { expected, actual } => {
write!(f, "population length mismatch: expected {expected}, got {actual}")
}
Self::PopulationRowOutOfRange { row, n } => {
write!(f, "population row {row} out of range for n={n}")
}
Self::PopulationEnvironmentUnsupported => {
write!(f, "Environment target population is not resolved by PopulationRegistry")
}
Self::InvalidPopulationWeights => {
write!(f, "custom distribution weights must be finite and non-negative")
}
}
}
}
impl std::error::Error for QueryError {}