Skip to main content

spin_sim/
config.rs

1use validator::{Validate, ValidationError};
2
3#[derive(Debug, Clone, Copy, PartialEq)]
4pub enum SweepMode {
5    Metropolis,
6    Gibbs,
7}
8
9impl TryFrom<&str> for SweepMode {
10    type Error = String;
11    fn try_from(s: &str) -> Result<Self, Self::Error> {
12        match s {
13            "metropolis" => Ok(Self::Metropolis),
14            "gibbs" => Ok(Self::Gibbs),
15            _ => Err(format!(
16                "unknown sweep_mode '{s}', expected 'metropolis' or 'gibbs'"
17            )),
18        }
19    }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq)]
23pub enum ClusterMode {
24    Wolff,
25    Sw,
26}
27
28impl TryFrom<&str> for ClusterMode {
29    type Error = String;
30    fn try_from(s: &str) -> Result<Self, Self::Error> {
31        match s {
32            "wolff" => Ok(Self::Wolff),
33            "sw" => Ok(Self::Sw),
34            _ => Err(format!(
35                "unknown cluster_mode '{s}', expected 'wolff' or 'sw'"
36            )),
37        }
38    }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq)]
42pub enum OverlapClusterBuildMode {
43    Houdayer(usize),
44    Jorg,
45    Cmr,
46}
47
48impl OverlapClusterBuildMode {
49    pub fn group_size(&self) -> usize {
50        match self {
51            Self::Houdayer(n) => *n,
52            _ => 2,
53        }
54    }
55}
56
57impl TryFrom<&str> for OverlapClusterBuildMode {
58    type Error = String;
59    fn try_from(s: &str) -> Result<Self, Self::Error> {
60        match s {
61            "houdayer" | "houd2" => Ok(Self::Houdayer(2)),
62            "jorg" => Ok(Self::Jorg),
63            "cmr" | "cmr2" => Ok(Self::Cmr),
64            _ if s.starts_with("houd") => {
65                let n: usize = s[4..].parse().map_err(|_| {
66                    format!(
67                        "invalid Houdayer group size in '{s}', expected 'houdN' with even integer N >= 2"
68                    )
69                })?;
70                if n < 2 || !n.is_multiple_of(2) {
71                    return Err(format!(
72                        "Houdayer group size must be even and >= 2, got {n}"
73                    ));
74                }
75                Ok(Self::Houdayer(n))
76            }
77            _ => Err(format!(
78                "unknown overlap_cluster_build_mode '{s}', expected 'houdayer', 'houdN', 'jorg', or 'cmr'"
79            )),
80        }
81    }
82}
83
84#[derive(Debug)]
85pub struct ClusterConfig {
86    pub interval: usize,
87    pub mode: ClusterMode,
88    pub collect_stats: bool,
89}
90
91#[derive(Debug)]
92pub struct OverlapClusterConfig {
93    pub interval: usize,
94    pub modes: Vec<OverlapClusterBuildMode>,
95    pub cluster_mode: ClusterMode,
96    pub collect_stats: bool,
97}
98
99impl OverlapClusterConfig {
100    pub fn max_group_size(&self) -> usize {
101        self.modes.iter().map(|m| m.group_size()).max().unwrap_or(2)
102    }
103}
104
105pub fn parse_overlap_modes(s: &str) -> Result<Vec<OverlapClusterBuildMode>, String> {
106    s.split('+')
107        .map(|part| OverlapClusterBuildMode::try_from(part.trim()))
108        .collect()
109}
110
111fn validate_sim_config(cfg: &SimConfig) -> Result<(), ValidationError> {
112    if cfg.n_sweeps < 1 {
113        return Err(ValidationError::new("n_sweeps must be >= 1"));
114    }
115    if cfg.warmup_sweeps > cfg.n_sweeps {
116        return Err(ValidationError::new("warmup_sweeps must be <= n_sweeps"));
117    }
118    if let Some(ref c) = cfg.cluster_update {
119        if c.interval < 1 {
120            return Err(ValidationError::new("cluster_update interval must be >= 1"));
121        }
122    }
123    if let Some(ref h) = cfg.overlap_cluster {
124        if h.interval < 1 {
125            return Err(ValidationError::new(
126                "overlap_cluster interval must be >= 1",
127            ));
128        }
129    }
130    Ok(())
131}
132
133#[derive(Debug, Validate)]
134#[validate(schema(function = "validate_sim_config"))]
135pub struct SimConfig {
136    pub n_sweeps: usize,
137    pub warmup_sweeps: usize,
138    pub sweep_mode: SweepMode,
139    pub cluster_update: Option<ClusterConfig>,
140    pub pt_interval: Option<usize>,
141    pub overlap_cluster: Option<OverlapClusterConfig>,
142    pub autocorrelation_max_lag: Option<usize>,
143    pub sequential: bool,
144    pub equilibration_diagnostic: bool,
145}