1use std::fmt;
6use std::str::FromStr;
7use std::sync::Arc;
8
9use antecedent_core::VariableId;
10
11use crate::ExprId;
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
15pub enum EstimandMethod {
16 BackdoorAdjustment,
18 BackdoorEfficient,
20 FrontDoor,
22 Iv,
24 RdSharp,
26 TemporalBackdoorUnfolded,
28 TemporalMediationTotal,
30 TemporalMediationDirect,
32 TemporalMediationMediated,
34 GeneralId,
36 PathSpecificNatural,
38}
39
40impl EstimandMethod {
41 #[must_use]
43 pub const fn as_str(self) -> &'static str {
44 match self {
45 Self::BackdoorAdjustment => "backdoor.adjustment",
46 Self::BackdoorEfficient => "backdoor.efficient",
47 Self::FrontDoor => "frontdoor",
48 Self::Iv => "iv",
49 Self::RdSharp => "rd.sharp",
50 Self::TemporalBackdoorUnfolded => "temporal.backdoor.unfolded",
51 Self::TemporalMediationTotal => "temporal_mediation.total",
52 Self::TemporalMediationDirect => "temporal_mediation.direct",
53 Self::TemporalMediationMediated => "temporal_mediation.mediated",
54 Self::GeneralId => "general.id",
55 Self::PathSpecificNatural => "path_specific.natural",
56 }
57 }
58
59 #[must_use]
61 pub const fn is_temporal_mediation(self) -> bool {
62 matches!(
63 self,
64 Self::TemporalMediationTotal
65 | Self::TemporalMediationDirect
66 | Self::TemporalMediationMediated
67 )
68 }
69
70 #[must_use]
72 pub const fn is_backdoor_family(self) -> bool {
73 matches!(self, Self::BackdoorAdjustment | Self::BackdoorEfficient)
74 }
75}
76
77impl fmt::Display for EstimandMethod {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 f.write_str(self.as_str())
80 }
81}
82
83impl FromStr for EstimandMethod {
84 type Err = String;
85
86 fn from_str(s: &str) -> Result<Self, Self::Err> {
87 Ok(match s {
88 "backdoor.adjustment" => Self::BackdoorAdjustment,
89 "backdoor.efficient" => Self::BackdoorEfficient,
90 "frontdoor" => Self::FrontDoor,
91 "iv" => Self::Iv,
92 "rd.sharp" => Self::RdSharp,
93 "temporal.backdoor.unfolded" => Self::TemporalBackdoorUnfolded,
94 "temporal_mediation.total" => Self::TemporalMediationTotal,
95 "temporal_mediation.direct" => Self::TemporalMediationDirect,
96 "temporal_mediation.mediated" => Self::TemporalMediationMediated,
97 "general.id" => Self::GeneralId,
98 "path_specific.natural" => Self::PathSpecificNatural,
99 other => return Err(format!("unknown estimand method `{other}`")),
100 })
101 }
102}
103
104impl From<EstimandMethod> for Arc<str> {
105 fn from(value: EstimandMethod) -> Self {
106 Arc::from(value.as_str())
107 }
108}
109
110#[derive(Clone, Debug)]
116#[non_exhaustive]
117pub struct IdentifiedEstimand {
118 pub method: Arc<str>,
120 pub adjustment_set: Arc<[VariableId]>,
122 pub instruments: Arc<[VariableId]>,
124 pub mediators: Arc<[VariableId]>,
126 pub functional: ExprId,
128 pub rd_design: Option<RdDesignParams>,
130}
131
132#[derive(Clone, Copy, Debug, PartialEq)]
134#[non_exhaustive]
135pub struct RdDesignParams {
136 pub running_variable: VariableId,
138 pub cutoff: f64,
140 pub bandwidth: f64,
142}
143
144impl RdDesignParams {
145 #[must_use]
147 pub const fn new(running_variable: VariableId, cutoff: f64, bandwidth: f64) -> Self {
148 Self { running_variable, cutoff, bandwidth }
149 }
150}
151
152impl IdentifiedEstimand {
153 #[must_use]
155 pub fn new(
156 method: impl Into<Arc<str>>,
157 adjustment_set: Arc<[VariableId]>,
158 instruments: Arc<[VariableId]>,
159 mediators: Arc<[VariableId]>,
160 functional: ExprId,
161 rd_design: Option<RdDesignParams>,
162 ) -> Self {
163 Self {
164 method: method.into(),
165 adjustment_set,
166 instruments,
167 mediators,
168 functional,
169 rd_design,
170 }
171 }
172
173 pub fn method_kind(&self) -> Result<EstimandMethod, String> {
179 EstimandMethod::from_str(self.method.as_ref())
180 }
181
182 #[must_use]
184 pub fn backdoor(
185 method: impl Into<Arc<str>>,
186 adjustment_set: Arc<[VariableId]>,
187 functional: ExprId,
188 ) -> Self {
189 Self::new(method, adjustment_set, Arc::from([]), Arc::from([]), functional, None)
190 }
191
192 #[must_use]
194 pub fn instrumental(
195 method: impl Into<Arc<str>>,
196 instruments: Arc<[VariableId]>,
197 functional: ExprId,
198 ) -> Self {
199 Self::new(method, Arc::from([]), instruments, Arc::from([]), functional, None)
200 }
201
202 #[must_use]
204 pub fn frontdoor(
205 method: impl Into<Arc<str>>,
206 mediators: Arc<[VariableId]>,
207 functional: ExprId,
208 ) -> Self {
209 Self::new(method, Arc::from([]), Arc::from([]), mediators, functional, None)
210 }
211
212 #[must_use]
214 pub fn rd_sharp(functional: ExprId, design: RdDesignParams) -> Self {
215 Self::new(
216 Arc::from(EstimandMethod::RdSharp.as_str()),
217 Arc::from([]),
218 Arc::from([]),
219 Arc::from([]),
220 functional,
221 Some(design),
222 )
223 }
224
225 #[must_use]
227 pub fn temporal_mediation(
228 method: impl Into<Arc<str>>,
229 mediators: Arc<[VariableId]>,
230 functional: ExprId,
231 ) -> Self {
232 Self::frontdoor(method, mediators, functional)
233 }
234}