Skip to main content

antecedent_validate/
validator.rs

1//! Validator contract.
2//!
3//! Effect refuters (§18.2) implement this trait. The trait is named `Validator`
4//! (not `Refuter`) per DESIGN.
5//!
6//! SPDX-License-Identifier: MIT OR Apache-2.0
7
8use antecedent_core::ExecutionContext;
9use antecedent_estimate::EstimationWorkspace;
10
11use crate::bootstrap_refute::BootstrapRefute;
12use crate::common::{RefutationProblem, RefutationReport};
13use crate::data_subset::DataSubsetRefuter;
14use crate::dummy_outcome::DummyOutcome;
15use crate::error::ValidationError;
16use crate::evalue::EValue;
17use crate::graph_refute::GraphRefuter;
18use crate::overlap::OverlapRefuter;
19use crate::overlap_rule::OverlapRuleRefuter;
20use crate::placebo::PlaceboTreatment;
21use crate::rcc::RandomCommonCause;
22use crate::riesz::RieszSensitivity;
23use crate::sensitivity::{LinearSensitivity, NonparametricSensitivity, PartialLinearSensitivity};
24use crate::unobserved_common_cause::UnobservedCommonCause;
25
26mod sealed {
27    pub trait Sealed {}
28}
29
30/// Validation / refutation algorithm over artifact type `A` .
31///
32/// `validate` also takes an [`EstimationWorkspace`] because effect refuters in
33/// this crate refit estimators; DESIGN's sketch omits it.
34///
35/// This trait is sealed: only types in this crate may implement it.
36pub trait Validator<A>: sealed::Sealed {
37    /// Prepared artifact produced by [`Self::prepare`].
38    type Prepared;
39    /// Report produced by [`Self::validate`].
40    type Report;
41
42    /// Compile `artifact` into a reusable prepared form.
43    ///
44    /// # Errors
45    ///
46    /// Incompatible artifact or missing prerequisites.
47    fn prepare(
48        &self,
49        artifact: &A,
50        ctx: &ExecutionContext,
51    ) -> Result<Self::Prepared, ValidationError>;
52
53    /// Run the check on a prepared artifact.
54    ///
55    /// # Errors
56    ///
57    /// Data, estimation, or applicability failures.
58    fn validate(
59        &self,
60        prepared: &mut Self::Prepared,
61        workspace: &mut EstimationWorkspace,
62        ctx: &ExecutionContext,
63    ) -> Result<Self::Report, ValidationError>;
64}
65
66/// Prepared effect-refutation problem (borrowed inputs + no extra state).
67#[derive(Clone, Copy, Debug)]
68pub struct PreparedRefutation<'a> {
69    /// Underlying refutation problem.
70    pub problem: RefutationProblem<'a>,
71}
72
73/// Run `validator` end-to-end (prepare → validate) for suite dispatch.
74///
75/// # Errors
76///
77/// Propagates prepare/validate failures.
78pub fn run_validator<'a, V>(
79    validator: &V,
80    problem: &RefutationProblem<'a>,
81    workspace: &mut EstimationWorkspace,
82    ctx: &ExecutionContext,
83) -> Result<RefutationReport, ValidationError>
84where
85    V: Validator<
86            RefutationProblem<'a>,
87            Prepared = PreparedRefutation<'a>,
88            Report = RefutationReport,
89        >,
90{
91    let mut prepared = validator.prepare(problem, ctx)?;
92    validator.validate(&mut prepared, workspace, ctx)
93}
94
95macro_rules! impl_effect_validator {
96    ($ty:ty, $call:expr) => {
97        impl crate::validator::sealed::Sealed for $ty {}
98        impl<'a> Validator<RefutationProblem<'a>> for $ty {
99            type Prepared = PreparedRefutation<'a>;
100            type Report = RefutationReport;
101
102            fn prepare(
103                &self,
104                artifact: &RefutationProblem<'a>,
105                _ctx: &ExecutionContext,
106            ) -> Result<Self::Prepared, ValidationError> {
107                Ok(PreparedRefutation { problem: *artifact })
108            }
109
110            fn validate(
111                &self,
112                prepared: &mut Self::Prepared,
113                workspace: &mut EstimationWorkspace,
114                ctx: &ExecutionContext,
115            ) -> Result<Self::Report, ValidationError> {
116                ($call)(self, &prepared.problem, workspace, ctx)
117            }
118        }
119    };
120}
121
122impl_effect_validator!(PlaceboTreatment, |this: &PlaceboTreatment, p, ws, ctx| {
123    this.refute(p, ws, ctx)
124});
125impl_effect_validator!(RandomCommonCause, |this: &RandomCommonCause, p, ws, ctx| {
126    this.refute(p, ws, ctx)
127});
128impl_effect_validator!(BootstrapRefute, |this: &BootstrapRefute, p, ws, ctx| {
129    this.refute(p, ws, ctx)
130});
131impl_effect_validator!(UnobservedCommonCause, |this: &UnobservedCommonCause, p, ws, ctx| {
132    this.refute(p, ws, ctx)
133});
134impl_effect_validator!(DataSubsetRefuter, |this: &DataSubsetRefuter, p, ws, ctx| {
135    this.refute(p, ws, ctx)
136});
137impl_effect_validator!(DummyOutcome, |this: &DummyOutcome, p, ws, ctx| { this.refute(p, ws, ctx) });
138impl_effect_validator!(GraphRefuter, |this: &GraphRefuter, p, ws, ctx| { this.refute(p, ws, ctx) });
139impl_effect_validator!(LinearSensitivity, |this: &LinearSensitivity, p, ws, ctx| {
140    this.refute(p, ws, ctx)
141});
142impl_effect_validator!(PartialLinearSensitivity, |this: &PartialLinearSensitivity, p, ws, ctx| {
143    this.refute(p, ws, ctx)
144});
145impl_effect_validator!(NonparametricSensitivity, |this: &NonparametricSensitivity, p, ws, ctx| {
146    this.refute(p, ws, ctx)
147});
148impl_effect_validator!(RieszSensitivity, |this: &RieszSensitivity, p, ws, ctx| {
149    this.refute(p, ws, ctx)
150});
151
152/// Validators whose `refute` does not need an estimation workspace.
153macro_rules! impl_stateless_effect_validator {
154    ($ty:ty, $call:expr) => {
155        impl crate::validator::sealed::Sealed for $ty {}
156        impl<'a> Validator<RefutationProblem<'a>> for $ty {
157            type Prepared = PreparedRefutation<'a>;
158            type Report = RefutationReport;
159
160            fn prepare(
161                &self,
162                artifact: &RefutationProblem<'a>,
163                _ctx: &ExecutionContext,
164            ) -> Result<Self::Prepared, ValidationError> {
165                Ok(PreparedRefutation { problem: *artifact })
166            }
167
168            fn validate(
169                &self,
170                prepared: &mut Self::Prepared,
171                _workspace: &mut EstimationWorkspace,
172                _ctx: &ExecutionContext,
173            ) -> Result<Self::Report, ValidationError> {
174                ($call)(self, &prepared.problem)
175            }
176        }
177    };
178}
179
180impl_stateless_effect_validator!(OverlapRefuter, |this: &OverlapRefuter, p| this.refute(p));
181impl_stateless_effect_validator!(OverlapRuleRefuter, |this: &OverlapRuleRefuter, p| {
182    this.refute(p)
183});
184impl_stateless_effect_validator!(EValue, |this: &EValue, p| this.refute(p));