problemreductions/
random.rs1use crate::registry::ConstructionError;
4use crate::topology::SimpleGraph;
5use serde::Deserialize;
6
7#[derive(Debug, Deserialize, crate::CreateSpec)]
9pub struct SimpleGraphRandomSpec {
10 pub num_vertices: usize,
12 pub edge_prob: Option<f64>,
14 pub seed: Option<i64>,
16}
17
18#[derive(Debug, Deserialize, crate::CreateSpec)]
20pub struct IntegerGeometryRandomSpec {
21 pub num_vertices: usize,
23 pub seed: Option<i64>,
25}
26
27#[derive(Debug, Deserialize, crate::CreateSpec)]
29pub struct UnitDiskRandomSpec {
30 pub num_vertices: usize,
32 pub radius: Option<f64>,
34 pub seed: Option<i64>,
36}
37
38#[derive(Debug, Deserialize, crate::CreateSpec)]
40pub struct CliqueRandomSpec {
41 pub num_vertices: usize,
43 pub edge_prob: Option<f64>,
45 pub seed: Option<i64>,
47 pub k: usize,
49}
50
51impl CliqueRandomSpec {
52 pub fn graph(&self) -> Result<SimpleGraph, ConstructionError> {
54 SimpleGraphRandomSpec {
55 num_vertices: self.num_vertices,
56 edge_prob: self.edge_prob,
57 seed: self.seed,
58 }
59 .graph()
60 }
61}
62
63#[derive(Debug, Deserialize, crate::CreateSpec)]
65pub struct EndpointRandomSpec {
66 pub num_vertices: usize,
68 pub edge_prob: Option<f64>,
70 pub seed: Option<i64>,
72 pub source: Option<usize>,
74 pub sink: Option<usize>,
76}
77
78#[derive(Debug, Deserialize, crate::CreateSpec)]
80pub struct ColoringRandomSpec {
81 pub num_vertices: usize,
83 pub edge_prob: Option<f64>,
85 pub seed: Option<i64>,
87 pub k: Option<usize>,
89}
90
91impl ColoringRandomSpec {
92 pub fn graph(&self) -> Result<SimpleGraph, ConstructionError> {
94 SimpleGraphRandomSpec {
95 num_vertices: self.num_vertices,
96 edge_prob: self.edge_prob,
97 seed: self.seed,
98 }
99 .graph()
100 }
101}
102
103impl EndpointRandomSpec {
104 pub fn graph(&self) -> Result<SimpleGraph, ConstructionError> {
106 SimpleGraphRandomSpec {
107 num_vertices: self.num_vertices,
108 edge_prob: self.edge_prob,
109 seed: self.seed,
110 }
111 .graph()
112 }
113
114 pub fn endpoints(&self) -> Result<(usize, usize), ConstructionError> {
116 if self.num_vertices < 2 {
117 return Err("num_vertices must be at least 2".into());
118 }
119 let source = self.source.unwrap_or(0);
120 let sink = self.sink.unwrap_or(self.num_vertices - 1);
121 if source >= self.num_vertices || sink >= self.num_vertices {
122 return Err(format!(
123 "source and sink must be below num_vertices ({})",
124 self.num_vertices
125 )
126 .into());
127 }
128 if source == sink {
129 return Err("source and sink must be distinct".into());
130 }
131 Ok((source, sink))
132 }
133}
134
135impl SimpleGraphRandomSpec {
136 pub fn graph(&self) -> Result<SimpleGraph, ConstructionError> {
138 let edge_prob = self.edge_prob.unwrap_or(0.5);
139 if !(0.0..=1.0).contains(&edge_prob) {
140 return Err(format!("edge_prob must be between 0 and 1, got {edge_prob}").into());
141 }
142 Ok(create_random_graph(
143 self.num_vertices,
144 edge_prob,
145 seed_to_u64(self.seed)?,
146 ))
147 }
148}
149
150#[macro_export]
152macro_rules! impl_random_generate {
153 ($target:ty, $spec:ty, |$input:ident| $body:block) => {
154 impl $crate::registry::RandomGenerate for $target {
155 fn inputs() -> Vec<$crate::registry::CreateInputInfo> {
156 <$spec as $crate::registry::CreateSpec>::inputs()
157 }
158
159 fn generate(
160 data: serde_json::Value,
161 ) -> Result<Self, $crate::registry::ConstructionError> {
162 $crate::registry::validate_create_inputs(&Self::inputs(), &data)?;
163 let $input: $spec = <$spec as $crate::registry::CreateSpec>::deserialize_inputs(
164 data,
165 )
166 .map_err(|error| {
167 $crate::registry::ConstructionError::InvalidInput(error.to_string())
168 })?;
169 let generate = || -> Result<Self, $crate::registry::ConstructionError> { $body };
170 generate()
171 }
172 }
173 };
174}
175
176pub(crate) fn lcg_step(state: &mut u64) -> f64 {
178 *state = state
179 .wrapping_mul(6364136223846793005)
180 .wrapping_add(1442695040888963407);
181 (*state >> 33) as f64 / (1u64 << 31) as f64
182}
183
184pub(crate) fn lcg_init(seed: Option<u64>) -> u64 {
186 seed.unwrap_or_else(|| {
187 let duration = std::time::SystemTime::now()
188 .duration_since(std::time::UNIX_EPOCH)
189 .expect("system clock must be after the Unix epoch");
190 duration.as_secs() ^ u64::from(duration.subsec_nanos())
191 })
192}
193
194pub(crate) fn create_random_graph(
196 num_vertices: usize,
197 edge_prob: f64,
198 seed: Option<u64>,
199) -> SimpleGraph {
200 let mut state = lcg_init(seed);
201 let edges = (0..num_vertices)
202 .flat_map(|u| ((u + 1)..num_vertices).map(move |v| (u, v)))
203 .filter(|_| lcg_step(&mut state) < edge_prob)
204 .collect();
205 SimpleGraph::new(num_vertices, edges)
206}
207
208pub(crate) fn create_random_int_positions(
210 num_vertices: usize,
211 seed: Option<u64>,
212) -> Vec<(i64, i64)> {
213 let mut state = lcg_init(seed);
214 let grid_size = (num_vertices as f64).sqrt().ceil() as i64 + 1;
215 let capacity = (grid_size * grid_size) as usize;
216 lcg_choose(&mut state, capacity, num_vertices)
217 .expect("grid capacity exceeds the requested position count")
218 .into_iter()
219 .map(|index| {
220 let index = i64::try_from(index).expect("random position index exceeds i64");
221 (index / grid_size, index % grid_size)
222 })
223 .collect()
224}
225
226pub(crate) fn create_random_float_positions(
228 num_vertices: usize,
229 seed: Option<u64>,
230) -> Vec<(f64, f64)> {
231 let mut state = lcg_init(seed);
232 let side = (num_vertices as f64).sqrt();
233 (0..num_vertices)
234 .map(|_| (lcg_step(&mut state) * side, lcg_step(&mut state) * side))
235 .collect()
236}
237
238pub(crate) fn lcg_choose(
240 state: &mut u64,
241 n: usize,
242 k: usize,
243) -> Result<Vec<usize>, ConstructionError> {
244 if k > n {
245 return Err(ConstructionError::Conversion(format!(
246 "cannot choose {k} elements from {n}"
247 )));
248 }
249 let mut indices = (0..n).collect::<Vec<_>>();
250 for i in 0..k {
251 let j = i + (lcg_step(state) * (n - i) as f64) as usize % (n - i);
252 indices.swap(i, j);
253 }
254 let mut chosen = indices[..k].to_vec();
255 chosen.sort_unstable();
256 Ok(chosen)
257}
258
259pub(crate) fn seed_to_u64(seed: Option<i64>) -> Result<Option<u64>, ConstructionError> {
260 seed.map(|value| u64::try_from(value).map_err(|_| "seed must be a nonnegative i64".into()))
261 .transpose()
262}