use std::fmt;
use std::str::FromStr;
use std::sync::Arc;
use antecedent_core::VariableId;
use crate::ExprId;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum EstimandMethod {
BackdoorAdjustment,
BackdoorEfficient,
FrontDoor,
Iv,
RdSharp,
TemporalBackdoorUnfolded,
TemporalMediationTotal,
TemporalMediationDirect,
TemporalMediationMediated,
GeneralId,
PathSpecificNatural,
}
impl EstimandMethod {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::BackdoorAdjustment => "backdoor.adjustment",
Self::BackdoorEfficient => "backdoor.efficient",
Self::FrontDoor => "frontdoor",
Self::Iv => "iv",
Self::RdSharp => "rd.sharp",
Self::TemporalBackdoorUnfolded => "temporal.backdoor.unfolded",
Self::TemporalMediationTotal => "temporal_mediation.total",
Self::TemporalMediationDirect => "temporal_mediation.direct",
Self::TemporalMediationMediated => "temporal_mediation.mediated",
Self::GeneralId => "general.id",
Self::PathSpecificNatural => "path_specific.natural",
}
}
#[must_use]
pub const fn is_temporal_mediation(self) -> bool {
matches!(
self,
Self::TemporalMediationTotal
| Self::TemporalMediationDirect
| Self::TemporalMediationMediated
)
}
#[must_use]
pub const fn is_backdoor_family(self) -> bool {
matches!(self, Self::BackdoorAdjustment | Self::BackdoorEfficient)
}
}
impl fmt::Display for EstimandMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for EstimandMethod {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"backdoor.adjustment" => Self::BackdoorAdjustment,
"backdoor.efficient" => Self::BackdoorEfficient,
"frontdoor" => Self::FrontDoor,
"iv" => Self::Iv,
"rd.sharp" => Self::RdSharp,
"temporal.backdoor.unfolded" => Self::TemporalBackdoorUnfolded,
"temporal_mediation.total" => Self::TemporalMediationTotal,
"temporal_mediation.direct" => Self::TemporalMediationDirect,
"temporal_mediation.mediated" => Self::TemporalMediationMediated,
"general.id" => Self::GeneralId,
"path_specific.natural" => Self::PathSpecificNatural,
other => return Err(format!("unknown estimand method `{other}`")),
})
}
}
impl From<EstimandMethod> for Arc<str> {
fn from(value: EstimandMethod) -> Self {
Arc::from(value.as_str())
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct IdentifiedEstimand {
pub method: Arc<str>,
pub adjustment_set: Arc<[VariableId]>,
pub instruments: Arc<[VariableId]>,
pub mediators: Arc<[VariableId]>,
pub functional: ExprId,
pub rd_design: Option<RdDesignParams>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct RdDesignParams {
pub running_variable: VariableId,
pub cutoff: f64,
pub bandwidth: f64,
}
impl RdDesignParams {
#[must_use]
pub const fn new(running_variable: VariableId, cutoff: f64, bandwidth: f64) -> Self {
Self { running_variable, cutoff, bandwidth }
}
}
impl IdentifiedEstimand {
#[must_use]
pub fn new(
method: impl Into<Arc<str>>,
adjustment_set: Arc<[VariableId]>,
instruments: Arc<[VariableId]>,
mediators: Arc<[VariableId]>,
functional: ExprId,
rd_design: Option<RdDesignParams>,
) -> Self {
Self {
method: method.into(),
adjustment_set,
instruments,
mediators,
functional,
rd_design,
}
}
pub fn method_kind(&self) -> Result<EstimandMethod, String> {
EstimandMethod::from_str(self.method.as_ref())
}
#[must_use]
pub fn backdoor(
method: impl Into<Arc<str>>,
adjustment_set: Arc<[VariableId]>,
functional: ExprId,
) -> Self {
Self::new(method, adjustment_set, Arc::from([]), Arc::from([]), functional, None)
}
#[must_use]
pub fn instrumental(
method: impl Into<Arc<str>>,
instruments: Arc<[VariableId]>,
functional: ExprId,
) -> Self {
Self::new(method, Arc::from([]), instruments, Arc::from([]), functional, None)
}
#[must_use]
pub fn frontdoor(
method: impl Into<Arc<str>>,
mediators: Arc<[VariableId]>,
functional: ExprId,
) -> Self {
Self::new(method, Arc::from([]), Arc::from([]), mediators, functional, None)
}
#[must_use]
pub fn rd_sharp(functional: ExprId, design: RdDesignParams) -> Self {
Self::new(
Arc::from(EstimandMethod::RdSharp.as_str()),
Arc::from([]),
Arc::from([]),
Arc::from([]),
functional,
Some(design),
)
}
#[must_use]
pub fn temporal_mediation(
method: impl Into<Arc<str>>,
mediators: Arc<[VariableId]>,
functional: ExprId,
) -> Self {
Self::frontdoor(method, mediators, functional)
}
}