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,
44    Jorg,
45    Cmr,
46    Cmr3,
47}
48
49impl OverlapClusterBuildMode {
50    pub fn group_size(&self) -> usize {
51        match self {
52            Self::Cmr3 => 3,
53            _ => 2,
54        }
55    }
56}
57
58impl TryFrom<&str> for OverlapClusterBuildMode {
59    type Error = String;
60    fn try_from(s: &str) -> Result<Self, Self::Error> {
61        match s {
62            "houdayer" => Ok(Self::Houdayer),
63            "jorg" => Ok(Self::Jorg),
64            "cmr" => Ok(Self::Cmr),
65            "cmr3" => Ok(Self::Cmr3),
66            _ => Err(format!(
67                "unknown overlap_cluster_build_mode '{s}', expected 'houdayer', 'jorg', 'cmr', or 'cmr3'"
68            )),
69        }
70    }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq)]
74pub enum OverlapUpdateMode {
75    Swap,
76    Free,
77}
78
79impl TryFrom<&str> for OverlapUpdateMode {
80    type Error = String;
81    fn try_from(s: &str) -> Result<Self, Self::Error> {
82        match s {
83            "swap" => Ok(Self::Swap),
84            "free" => Ok(Self::Free),
85            _ => Err(format!(
86                "unknown overlap_update_mode '{s}', expected 'swap' or 'free'"
87            )),
88        }
89    }
90}
91
92#[derive(Debug)]
93pub struct ClusterConfig {
94    pub interval: usize,
95    pub mode: ClusterMode,
96    pub collect_csd: bool,
97}
98
99fn validate_overlap_cluster_config(cfg: &OverlapClusterConfig) -> Result<(), ValidationError> {
100    if cfg.update_mode == OverlapUpdateMode::Free
101        && !matches!(
102            cfg.mode,
103            OverlapClusterBuildMode::Cmr | OverlapClusterBuildMode::Cmr3
104        )
105    {
106        return Err(ValidationError::new(
107            "overlap_update_mode 'free' requires overlap_cluster_build_mode 'cmr' or 'cmr3'",
108        ));
109    }
110    if cfg.mode == OverlapClusterBuildMode::Cmr3 && cfg.update_mode != OverlapUpdateMode::Free {
111        return Err(ValidationError::new(
112            "overlap_cluster_build_mode 'cmr3' requires overlap_update_mode 'free'",
113        ));
114    }
115    Ok(())
116}
117
118#[derive(Debug, Validate)]
119#[validate(schema(function = "validate_overlap_cluster_config"))]
120pub struct OverlapClusterConfig {
121    pub interval: usize,
122    pub mode: OverlapClusterBuildMode,
123    pub cluster_mode: ClusterMode,
124    pub update_mode: OverlapUpdateMode,
125    pub collect_csd: bool,
126    pub collect_top_clusters: bool,
127}
128
129fn validate_sim_config(cfg: &SimConfig) -> Result<(), ValidationError> {
130    if cfg.n_sweeps < 1 {
131        return Err(ValidationError::new("n_sweeps must be >= 1"));
132    }
133    if cfg.warmup_sweeps > cfg.n_sweeps {
134        return Err(ValidationError::new("warmup_sweeps must be <= n_sweeps"));
135    }
136    if let Some(ref c) = cfg.cluster_update {
137        if c.interval < 1 {
138            return Err(ValidationError::new("cluster_update interval must be >= 1"));
139        }
140    }
141    if let Some(ref h) = cfg.overlap_cluster {
142        if h.interval < 1 {
143            return Err(ValidationError::new(
144                "overlap_cluster interval must be >= 1",
145            ));
146        }
147    }
148    Ok(())
149}
150
151#[derive(Debug, Validate)]
152#[validate(schema(function = "validate_sim_config"))]
153pub struct SimConfig {
154    pub n_sweeps: usize,
155    pub warmup_sweeps: usize,
156    pub sweep_mode: SweepMode,
157    pub cluster_update: Option<ClusterConfig>,
158    pub pt_interval: Option<usize>,
159    #[validate]
160    pub overlap_cluster: Option<OverlapClusterConfig>,
161    pub autocorrelation_max_lag: Option<usize>,
162    pub sequential: bool,
163    pub equilibration_diagnostic: bool,
164}