1use crate::graph_types::SimpleGraph;
7use crate::traits::{ConstraintSatisfactionProblem, Problem};
8use crate::types::{EnergyMode, LocalConstraint, LocalSolutionSize, ProblemSize, SolutionSize};
9use petgraph::graph::{NodeIndex, UnGraph};
10use petgraph::visit::EdgeRef;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Coloring {
37 num_colors: usize,
39 graph: UnGraph<(), ()>,
41}
42
43impl Coloring {
44 pub fn new(num_vertices: usize, num_colors: usize, edges: Vec<(usize, usize)>) -> Self {
51 let mut graph = UnGraph::new_undirected();
52 for _ in 0..num_vertices {
53 graph.add_node(());
54 }
55 for (u, v) in edges {
56 graph.add_edge(NodeIndex::new(u), NodeIndex::new(v), ());
57 }
58 Self { num_colors, graph }
59 }
60
61 pub fn num_vertices(&self) -> usize {
63 self.graph.node_count()
64 }
65
66 pub fn num_edges(&self) -> usize {
68 self.graph.edge_count()
69 }
70
71 pub fn num_colors(&self) -> usize {
73 self.num_colors
74 }
75
76 pub fn edges(&self) -> Vec<(usize, usize)> {
78 self.graph
79 .edge_references()
80 .map(|e| (e.source().index(), e.target().index()))
81 .collect()
82 }
83
84 fn is_valid_coloring(&self, config: &[usize]) -> bool {
86 for edge in self.graph.edge_references() {
87 let u = edge.source().index();
88 let v = edge.target().index();
89 let color_u = config.get(u).copied().unwrap_or(0);
90 let color_v = config.get(v).copied().unwrap_or(0);
91 if color_u == color_v {
92 return false;
93 }
94 }
95 true
96 }
97}
98
99impl Problem for Coloring {
100 const NAME: &'static str = "Coloring";
101 type GraphType = SimpleGraph;
102 type Weight = i32;
103 type Size = i32;
104
105 fn num_variables(&self) -> usize {
106 self.graph.node_count()
107 }
108
109 fn num_flavors(&self) -> usize {
110 self.num_colors
111 }
112
113 fn problem_size(&self) -> ProblemSize {
114 ProblemSize::new(vec![
115 ("num_vertices", self.graph.node_count()),
116 ("num_edges", self.graph.edge_count()),
117 ("num_colors", self.num_colors),
118 ])
119 }
120
121 fn energy_mode(&self) -> EnergyMode {
122 EnergyMode::SmallerSizeIsBetter
125 }
126
127 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
128 let is_valid = self.is_valid_coloring(config);
129 let mut conflicts = 0;
131 for edge in self.graph.edge_references() {
132 let u = edge.source().index();
133 let v = edge.target().index();
134 let color_u = config.get(u).copied().unwrap_or(0);
135 let color_v = config.get(v).copied().unwrap_or(0);
136 if color_u == color_v {
137 conflicts += 1;
138 }
139 }
140 SolutionSize::new(conflicts, is_valid)
141 }
142}
143
144impl ConstraintSatisfactionProblem for Coloring {
145 fn constraints(&self) -> Vec<LocalConstraint> {
146 self.graph
148 .edge_references()
149 .map(|e| {
150 let u = e.source().index();
151 let v = e.target().index();
152 let k = self.num_colors;
153
154 let mut spec = vec![false; k * k];
156 for c1 in 0..k {
157 for c2 in 0..k {
158 spec[c1 * k + c2] = c1 != c2;
159 }
160 }
161
162 LocalConstraint::new(k, vec![u, v], spec)
163 })
164 .collect()
165 }
166
167 fn objectives(&self) -> Vec<LocalSolutionSize<Self::Size>> {
168 vec![]
170 }
171
172 fn weights(&self) -> Vec<Self::Size> {
173 vec![]
174 }
175
176 fn set_weights(&mut self, _weights: Vec<Self::Size>) {}
177
178 fn is_weighted(&self) -> bool {
179 false
180 }
181}
182
183pub fn is_valid_coloring(
185 num_vertices: usize,
186 edges: &[(usize, usize)],
187 coloring: &[usize],
188 num_colors: usize,
189) -> bool {
190 if coloring.len() != num_vertices {
191 return false;
192 }
193 if coloring.iter().any(|&c| c >= num_colors) {
195 return false;
196 }
197 for &(u, v) in edges {
199 if u < coloring.len() && v < coloring.len() && coloring[u] == coloring[v] {
200 return false;
201 }
202 }
203 true
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::solvers::{BruteForce, Solver};
210
211 #[test]
212 fn test_coloring_creation() {
213 let problem = Coloring::new(4, 3, vec![(0, 1), (1, 2), (2, 3)]);
214 assert_eq!(problem.num_vertices(), 4);
215 assert_eq!(problem.num_edges(), 3);
216 assert_eq!(problem.num_colors(), 3);
217 assert_eq!(problem.num_variables(), 4);
218 assert_eq!(problem.num_flavors(), 3);
219 }
220
221 #[test]
222 fn test_solution_size_valid() {
223 let problem = Coloring::new(3, 3, vec![(0, 1), (1, 2)]);
224
225 let sol = problem.solution_size(&[0, 1, 0]);
227 assert!(sol.is_valid);
228 assert_eq!(sol.size, 0);
229
230 let sol = problem.solution_size(&[0, 1, 2]);
231 assert!(sol.is_valid);
232 assert_eq!(sol.size, 0);
233 }
234
235 #[test]
236 fn test_solution_size_invalid() {
237 let problem = Coloring::new(3, 3, vec![(0, 1), (1, 2)]);
238
239 let sol = problem.solution_size(&[0, 0, 1]);
241 assert!(!sol.is_valid);
242 assert_eq!(sol.size, 1); let sol = problem.solution_size(&[0, 0, 0]);
245 assert!(!sol.is_valid);
246 assert_eq!(sol.size, 2); }
248
249 #[test]
250 fn test_brute_force_path() {
251 let problem = Coloring::new(4, 2, vec![(0, 1), (1, 2), (2, 3)]);
253 let solver = BruteForce::new();
254
255 let solutions = solver.find_best(&problem);
256 for sol in &solutions {
258 assert!(problem.solution_size(sol).is_valid);
259 }
260 }
261
262 #[test]
263 fn test_brute_force_triangle() {
264 let problem = Coloring::new(3, 3, vec![(0, 1), (1, 2), (0, 2)]);
266 let solver = BruteForce::new();
267
268 let solutions = solver.find_best(&problem);
269 for sol in &solutions {
270 assert!(problem.solution_size(sol).is_valid);
271 assert_ne!(sol[0], sol[1]);
273 assert_ne!(sol[1], sol[2]);
274 assert_ne!(sol[0], sol[2]);
275 }
276 }
277
278 #[test]
279 fn test_triangle_2_colors() {
280 let problem = Coloring::new(3, 2, vec![(0, 1), (1, 2), (0, 2)]);
282 let solver = BruteForce::new();
283
284 let solutions = solver.find_best(&problem);
285 for sol in &solutions {
287 assert!(!problem.solution_size(sol).is_valid);
288 assert_eq!(problem.solution_size(sol).size, 1);
289 }
290 }
291
292 #[test]
293 fn test_constraints() {
294 let problem = Coloring::new(3, 2, vec![(0, 1), (1, 2)]);
295 let constraints = problem.constraints();
296 assert_eq!(constraints.len(), 2); }
298
299 #[test]
300 fn test_energy_mode() {
301 let problem = Coloring::new(2, 2, vec![(0, 1)]);
302 assert!(problem.energy_mode().is_minimization());
303 }
304
305 #[test]
306 fn test_is_valid_coloring_function() {
307 let edges = vec![(0, 1), (1, 2)];
308
309 assert!(is_valid_coloring(3, &edges, &[0, 1, 0], 2));
310 assert!(is_valid_coloring(3, &edges, &[0, 1, 2], 3));
311 assert!(!is_valid_coloring(3, &edges, &[0, 0, 1], 2)); assert!(!is_valid_coloring(3, &edges, &[0, 1, 1], 2)); assert!(!is_valid_coloring(3, &edges, &[0, 1], 2)); assert!(!is_valid_coloring(3, &edges, &[0, 2, 0], 2)); }
316
317 #[test]
318 fn test_empty_graph() {
319 let problem = Coloring::new(3, 1, vec![]);
320 let solver = BruteForce::new();
321
322 let solutions = solver.find_best(&problem);
323 assert!(problem.solution_size(&solutions[0]).is_valid);
325 }
326
327 #[test]
328 fn test_complete_graph_k4() {
329 let problem = Coloring::new(4, 4, vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
331 let solver = BruteForce::new();
332
333 let solutions = solver.find_best(&problem);
334 for sol in &solutions {
335 assert!(problem.solution_size(sol).is_valid);
336 }
337 }
338
339 #[test]
340 fn test_is_satisfied() {
341 let problem = Coloring::new(3, 3, vec![(0, 1), (1, 2)]);
342
343 assert!(problem.is_satisfied(&[0, 1, 0]));
344 assert!(problem.is_satisfied(&[0, 1, 2]));
345 assert!(!problem.is_satisfied(&[0, 0, 1]));
346 }
347
348 #[test]
349 fn test_problem_size() {
350 let problem = Coloring::new(5, 3, vec![(0, 1), (1, 2)]);
351 let size = problem.problem_size();
352 assert_eq!(size.get("num_vertices"), Some(5));
353 assert_eq!(size.get("num_edges"), Some(2));
354 assert_eq!(size.get("num_colors"), Some(3));
355 }
356
357 #[test]
358 fn test_csp_methods() {
359 let problem = Coloring::new(3, 2, vec![(0, 1)]);
360
361 let objectives = problem.objectives();
363 assert!(objectives.is_empty());
364
365 let weights: Vec<i32> = problem.weights();
367 assert!(weights.is_empty());
368
369 assert!(!problem.is_weighted());
371 }
372
373 #[test]
374 fn test_set_weights() {
375 let mut problem = Coloring::new(3, 2, vec![(0, 1)]);
376 problem.set_weights(vec![1, 2, 3]);
378 assert!(!problem.is_weighted());
379 }
380}