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}