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(usize),
46}
47
48impl OverlapClusterBuildMode {
49    pub fn group_size(&self) -> usize {
50        match self {
51            Self::Cmr(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" => Ok(Self::Houdayer),
62            "jorg" => Ok(Self::Jorg),
63            "cmr" | "cmr2" => Ok(Self::Cmr(2)),
64            _ if s.starts_with("cmr") => {
65                let n: usize = s[3..].parse().map_err(|_| {
66                    format!("invalid CMR group size in '{s}', expected 'cmrN' with integer N >= 2")
67                })?;
68                if n < 2 {
69                    return Err(format!("CMR group size must be >= 2, got {n}"));
70                }
71                Ok(Self::Cmr(n))
72            }
73            _ => Err(format!(
74                "unknown overlap_cluster_build_mode '{s}', expected 'houdayer', 'jorg', or 'cmrN'"
75            )),
76        }
77    }
78}
79
80#[derive(Debug)]
81pub struct ClusterConfig {
82    pub interval: usize,
83    pub mode: ClusterMode,
84    pub collect_csd: bool,
85}
86
87#[derive(Debug)]
88pub struct OverlapClusterConfig {
89    pub interval: usize,
90    pub mode: OverlapClusterBuildMode,
91    pub cluster_mode: ClusterMode,
92    pub collect_csd: bool,
93    pub collect_top_clusters: bool,
94}
95
96fn validate_sim_config(cfg: &SimConfig) -> Result<(), ValidationError> {
97    if cfg.n_sweeps < 1 {
98        return Err(ValidationError::new("n_sweeps must be >= 1"));
99    }
100    if cfg.warmup_sweeps > cfg.n_sweeps {
101        return Err(ValidationError::new("warmup_sweeps must be <= n_sweeps"));
102    }
103    if let Some(ref c) = cfg.cluster_update {
104        if c.interval < 1 {
105            return Err(ValidationError::new("cluster_update interval must be >= 1"));
106        }
107    }
108    if let Some(ref h) = cfg.overlap_cluster {
109        if h.interval < 1 {
110            return Err(ValidationError::new(
111                "overlap_cluster interval must be >= 1",
112            ));
113        }
114    }
115    Ok(())
116}
117
118#[derive(Debug, Validate)]
119#[validate(schema(function = "validate_sim_config"))]
120pub struct SimConfig {
121    pub n_sweeps: usize,
122    pub warmup_sweeps: usize,
123    pub sweep_mode: SweepMode,
124    pub cluster_update: Option<ClusterConfig>,
125    pub pt_interval: Option<usize>,
126    pub overlap_cluster: Option<OverlapClusterConfig>,
127    pub autocorrelation_max_lag: Option<usize>,
128    pub sequential: bool,
129    pub equilibration_diagnostic: bool,
130}