1use crate::graph_types::GraphMarker;
4use crate::types::{EnergyMode, LocalConstraint, LocalSolutionSize, NumericWeight, ProblemSize, SolutionSize};
5use num_traits::{Num, Zero};
6use std::ops::AddAssign;
7
8pub trait Problem: Clone {
13 const NAME: &'static str;
15
16 type GraphType: GraphMarker;
18
19 type Weight: NumericWeight;
21
22 type Size: Clone + PartialOrd + Num + Zero + AddAssign;
24
25 fn num_variables(&self) -> usize;
27
28 fn num_flavors(&self) -> usize;
31
32 fn problem_size(&self) -> ProblemSize;
34
35 fn energy_mode(&self) -> EnergyMode;
37
38 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size>;
46
47 fn variables(&self) -> std::ops::Range<usize> {
49 0..self.num_variables()
50 }
51
52 fn flavors(&self) -> Vec<usize> {
54 (0..self.num_flavors()).collect()
55 }
56
57 fn is_valid_config(&self, config: &[usize]) -> bool {
59 if config.len() != self.num_variables() {
60 return false;
61 }
62 let num_flavors = self.num_flavors();
63 config.iter().all(|&v| v < num_flavors)
64 }
65
66 fn solution_size_multiple(&self, configs: &[Vec<usize>]) -> Vec<SolutionSize<Self::Size>> {
68 configs.iter().map(|c| self.solution_size(c)).collect()
69 }
70}
71
72pub trait ConstraintSatisfactionProblem: Problem {
77 fn constraints(&self) -> Vec<LocalConstraint>;
79
80 fn objectives(&self) -> Vec<LocalSolutionSize<Self::Size>>;
82
83 fn weights(&self) -> Vec<Self::Size>;
85
86 fn set_weights(&mut self, weights: Vec<Self::Size>);
88
89 fn is_weighted(&self) -> bool;
91
92 fn is_satisfied(&self, config: &[usize]) -> bool {
94 self.constraints().iter().all(|c| c.is_satisfied(config))
95 }
96
97 fn compute_objective(&self, config: &[usize]) -> Self::Size {
99 let mut total = Self::Size::zero();
100 for obj in self.objectives() {
101 total += obj.evaluate(config);
102 }
103 total
104 }
105}
106
107pub fn csp_solution_size<P: ConstraintSatisfactionProblem>(
110 problem: &P,
111 config: &[usize],
112) -> SolutionSize<P::Size> {
113 let is_valid = problem.is_satisfied(config);
114 let size = problem.compute_objective(config);
115 SolutionSize::new(size, is_valid)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::graph_types::SimpleGraph;
122
123 #[derive(Clone)]
125 struct SimpleWeightedProblem {
126 weights: Vec<i32>,
127 }
128
129 impl Problem for SimpleWeightedProblem {
130 const NAME: &'static str = "SimpleWeightedProblem";
131 type GraphType = SimpleGraph;
132 type Weight = i32;
133 type Size = i32;
134
135 fn num_variables(&self) -> usize {
136 self.weights.len()
137 }
138
139 fn num_flavors(&self) -> usize {
140 2
141 }
142
143 fn problem_size(&self) -> ProblemSize {
144 ProblemSize::new(vec![("variables", self.weights.len())])
145 }
146
147 fn energy_mode(&self) -> EnergyMode {
148 EnergyMode::LargerSizeIsBetter
149 }
150
151 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
152 let sum: i32 = config
153 .iter()
154 .zip(&self.weights)
155 .map(|(&c, &w)| if c == 1 { w } else { 0 })
156 .sum();
157 SolutionSize::valid(sum)
158 }
159 }
160
161 #[derive(Clone)]
163 struct SimpleCsp {
164 num_vars: usize,
165 }
166
167 impl Problem for SimpleCsp {
168 const NAME: &'static str = "SimpleCsp";
169 type GraphType = SimpleGraph;
170 type Weight = i32;
171 type Size = i32;
172
173 fn num_variables(&self) -> usize {
174 self.num_vars
175 }
176
177 fn num_flavors(&self) -> usize {
178 2
179 }
180
181 fn problem_size(&self) -> ProblemSize {
182 ProblemSize::new(vec![("variables", self.num_vars)])
183 }
184
185 fn energy_mode(&self) -> EnergyMode {
186 EnergyMode::LargerSizeIsBetter
187 }
188
189 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
190 csp_solution_size(self, config)
191 }
192 }
193
194 impl ConstraintSatisfactionProblem for SimpleCsp {
195 fn constraints(&self) -> Vec<LocalConstraint> {
196 if self.num_vars >= 2 {
198 vec![LocalConstraint::new(
199 2,
200 vec![0, 1],
201 vec![true, true, true, false], )]
203 } else {
204 vec![]
205 }
206 }
207
208 fn objectives(&self) -> Vec<LocalSolutionSize<Self::Size>> {
209 (0..self.num_vars)
211 .map(|i| LocalSolutionSize::new(2, vec![i], vec![0, 1]))
212 .collect()
213 }
214
215 fn weights(&self) -> Vec<Self::Size> {
216 vec![1; self.num_vars]
217 }
218
219 fn set_weights(&mut self, _weights: Vec<Self::Size>) {}
220
221 fn is_weighted(&self) -> bool {
222 false
223 }
224 }
225
226 #[test]
227 fn test_simple_problem() {
228 let problem = SimpleWeightedProblem {
229 weights: vec![1, 2, 3],
230 };
231
232 assert_eq!(problem.num_variables(), 3);
233 assert_eq!(problem.num_flavors(), 2);
234 assert_eq!(problem.variables(), 0..3);
235 assert_eq!(problem.flavors(), vec![0, 1]);
236
237 let sol = problem.solution_size(&[0, 0, 0]);
238 assert_eq!(sol.size, 0);
239 assert!(sol.is_valid);
240
241 let sol = problem.solution_size(&[1, 1, 1]);
242 assert_eq!(sol.size, 6);
243 assert!(sol.is_valid);
244
245 let sol = problem.solution_size(&[1, 0, 1]);
246 assert_eq!(sol.size, 4);
247 assert!(sol.is_valid);
248 }
249
250 #[test]
251 fn test_valid_config() {
252 let problem = SimpleWeightedProblem {
253 weights: vec![1, 2, 3],
254 };
255
256 assert!(problem.is_valid_config(&[0, 1, 0]));
257 assert!(problem.is_valid_config(&[1, 1, 1]));
258 assert!(!problem.is_valid_config(&[0, 2, 0])); assert!(!problem.is_valid_config(&[0, 1])); assert!(!problem.is_valid_config(&[0, 1, 0, 1])); }
262
263 #[test]
264 fn test_batch_evaluation() {
265 let problem = SimpleWeightedProblem {
266 weights: vec![1, 2, 3],
267 };
268
269 let configs = vec![vec![0, 0, 0], vec![1, 1, 1], vec![1, 0, 1]];
270
271 let results = problem.solution_size_multiple(&configs);
272 assert_eq!(results.len(), 3);
273 assert_eq!(results[0].size, 0);
274 assert_eq!(results[1].size, 6);
275 assert_eq!(results[2].size, 4);
276 }
277
278 #[test]
279 fn test_csp_solution_size() {
280 let problem = SimpleCsp { num_vars: 3 };
281
282 let sol = problem.solution_size(&[0, 0, 0]);
284 assert!(sol.is_valid);
285 assert_eq!(sol.size, 0);
286
287 let sol = problem.solution_size(&[1, 0, 0]);
288 assert!(sol.is_valid);
289 assert_eq!(sol.size, 1);
290
291 let sol = problem.solution_size(&[0, 1, 0]);
292 assert!(sol.is_valid);
293 assert_eq!(sol.size, 1);
294
295 let sol = problem.solution_size(&[1, 1, 0]);
297 assert!(!sol.is_valid);
298 assert_eq!(sol.size, 2);
299 }
300
301 #[test]
302 fn test_csp_is_satisfied() {
303 let problem = SimpleCsp { num_vars: 3 };
304
305 assert!(problem.is_satisfied(&[0, 0, 0]));
306 assert!(problem.is_satisfied(&[1, 0, 0]));
307 assert!(problem.is_satisfied(&[0, 1, 0]));
308 assert!(!problem.is_satisfied(&[1, 1, 0]));
309 }
310
311 #[test]
312 fn test_csp_compute_objective() {
313 let problem = SimpleCsp { num_vars: 3 };
314
315 assert_eq!(problem.compute_objective(&[0, 0, 0]), 0);
316 assert_eq!(problem.compute_objective(&[1, 0, 0]), 1);
317 assert_eq!(problem.compute_objective(&[1, 1, 0]), 2);
318 assert_eq!(problem.compute_objective(&[1, 1, 1]), 3);
319 }
320
321 #[test]
322 fn test_csp_single_variable() {
323 let problem = SimpleCsp { num_vars: 1 };
325
326 assert!(problem.constraints().is_empty());
327 assert!(problem.is_satisfied(&[0])); assert!(problem.is_satisfied(&[1]));
329
330 let sol = problem.solution_size(&[0]);
331 assert!(sol.is_valid);
332 assert_eq!(sol.size, 0);
333
334 let sol = problem.solution_size(&[1]);
335 assert!(sol.is_valid);
336 assert_eq!(sol.size, 1);
337 }
338
339 #[test]
340 fn test_csp_weights_and_weighted() {
341 let problem = SimpleCsp { num_vars: 3 };
342 assert_eq!(problem.weights(), vec![1, 1, 1]);
343 assert!(!problem.is_weighted());
344 }
345
346 #[test]
347 fn test_csp_set_weights() {
348 let mut problem = SimpleCsp { num_vars: 3 };
349 problem.set_weights(vec![10, 20, 30]);
350 assert!(!problem.is_weighted());
352 }
353
354 #[test]
355 fn test_problem_size_metadata() {
356 let problem = SimpleWeightedProblem {
357 weights: vec![1, 2, 3, 4, 5],
358 };
359
360 let size = problem.problem_size();
361 assert_eq!(size.get("variables"), Some(5));
362 }
363
364 #[test]
365 fn test_energy_mode() {
366 let problem = SimpleWeightedProblem {
367 weights: vec![1, 2, 3],
368 };
369 assert!(problem.energy_mode().is_maximization());
370 }
371
372 #[test]
373 fn test_batch_evaluation_empty() {
374 let problem = SimpleWeightedProblem {
375 weights: vec![1, 2, 3],
376 };
377
378 let configs: Vec<Vec<usize>> = vec![];
379 let results = problem.solution_size_multiple(&configs);
380 assert!(results.is_empty());
381 }
382
383 #[test]
384 fn test_is_valid_config_empty_problem() {
385 let problem = SimpleWeightedProblem { weights: vec![] };
386
387 assert_eq!(problem.num_variables(), 0);
388 assert!(problem.is_valid_config(&[])); assert!(!problem.is_valid_config(&[0])); }
391
392 #[test]
393 fn test_variables_range() {
394 let problem = SimpleWeightedProblem {
395 weights: vec![1, 2, 3, 4, 5],
396 };
397
398 let vars: Vec<usize> = problem.variables().collect();
399 assert_eq!(vars, vec![0, 1, 2, 3, 4]);
400 }
401
402 #[test]
403 fn test_flavors_list() {
404 let problem = SimpleWeightedProblem {
405 weights: vec![1, 2],
406 };
407
408 assert_eq!(problem.flavors(), vec![0, 1]);
409 }
410
411 #[test]
412 fn test_csp_objectives() {
413 let problem = SimpleCsp { num_vars: 3 };
414 let objectives = problem.objectives();
415
416 assert_eq!(objectives.len(), 3);
417 assert_eq!(objectives[0].evaluate(&[0, 0, 0]), 0);
419 assert_eq!(objectives[0].evaluate(&[1, 0, 0]), 1);
420 assert_eq!(objectives[1].evaluate(&[0, 1, 0]), 1);
421 assert_eq!(objectives[2].evaluate(&[0, 0, 1]), 1);
422 }
423
424 #[test]
425 fn test_csp_solution_size_helper_function() {
426 let problem = SimpleCsp { num_vars: 2 };
427
428 let sol = csp_solution_size(&problem, &[0, 0]);
430 assert!(sol.is_valid);
431 assert_eq!(sol.size, 0);
432
433 let sol = csp_solution_size(&problem, &[1, 0]);
434 assert!(sol.is_valid);
435 assert_eq!(sol.size, 1);
436
437 let sol = csp_solution_size(&problem, &[1, 1]);
438 assert!(!sol.is_valid);
439 assert_eq!(sol.size, 2);
440 }
441
442 #[derive(Clone)]
444 struct MultiFlavorProblem {
445 num_vars: usize,
446 num_flavors: usize,
447 }
448
449 impl Problem for MultiFlavorProblem {
450 const NAME: &'static str = "MultiFlavorProblem";
451 type GraphType = SimpleGraph;
452 type Weight = i32;
453 type Size = i32;
454
455 fn num_variables(&self) -> usize {
456 self.num_vars
457 }
458
459 fn num_flavors(&self) -> usize {
460 self.num_flavors
461 }
462
463 fn problem_size(&self) -> ProblemSize {
464 ProblemSize::new(vec![
465 ("variables", self.num_vars),
466 ("flavors", self.num_flavors),
467 ])
468 }
469
470 fn energy_mode(&self) -> EnergyMode {
471 EnergyMode::SmallerSizeIsBetter
472 }
473
474 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
475 let sum: i32 = config.iter().map(|&c| c as i32).sum();
476 SolutionSize::valid(sum)
477 }
478 }
479
480 #[test]
481 fn test_multi_flavor_problem() {
482 let problem = MultiFlavorProblem {
483 num_vars: 3,
484 num_flavors: 4,
485 };
486
487 assert_eq!(problem.num_flavors(), 4);
488 assert_eq!(problem.flavors(), vec![0, 1, 2, 3]);
489 assert!(problem.energy_mode().is_minimization());
490
491 assert!(problem.is_valid_config(&[0, 1, 2]));
493 assert!(problem.is_valid_config(&[3, 3, 3]));
494
495 assert!(!problem.is_valid_config(&[0, 4, 0]));
497 assert!(!problem.is_valid_config(&[5, 0, 0]));
498
499 let sol = problem.solution_size(&[0, 1, 2]);
500 assert_eq!(sol.size, 3);
501
502 let sol = problem.solution_size(&[3, 3, 3]);
503 assert_eq!(sol.size, 9);
504 }
505
506 #[test]
507 fn test_batch_evaluation_with_multi_flavor() {
508 let problem = MultiFlavorProblem {
509 num_vars: 2,
510 num_flavors: 3,
511 };
512
513 let configs = vec![vec![0, 0], vec![1, 1], vec![2, 2], vec![0, 2]];
514 let results = problem.solution_size_multiple(&configs);
515
516 assert_eq!(results.len(), 4);
517 assert_eq!(results[0].size, 0);
518 assert_eq!(results[1].size, 2);
519 assert_eq!(results[2].size, 4);
520 assert_eq!(results[3].size, 2);
521 }
522}