1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
11use crate::models::graph::KColoring;
12use crate::reduction;
13use crate::rules::ilp_helpers::one_hot_decode_rows;
14use crate::rules::traits::{ReduceTo, ReductionResult};
15use crate::topology::{Graph, SimpleGraph};
16use crate::variant::{KValue, K2, K3, KN};
17
18#[derive(Debug, Clone)]
25pub struct ReductionKColoringToILP<K: KValue, G> {
26 target: ILP<bool>,
27 num_vertices: usize,
28 num_colors: usize,
29 _phantom: std::marker::PhantomData<(K, G)>,
30}
31
32impl<K: KValue, G> ReductionResult for ReductionKColoringToILP<K, G>
33where
34 G: Graph + crate::variant::VariantParam,
35{
36 type Source = KColoring<K, G>;
37 type Target = ILP<bool>;
38
39 fn target_problem(&self) -> &ILP<bool> {
40 &self.target
41 }
42
43 fn extract_solution(
48 &self,
49 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
50 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
51 crate::rules::traits::validate_target_witness(
52 self.target_problem(),
53 target_solution,
54 |value| value.value.is_some(),
55 "target ILP assignment is infeasible",
56 )?;
57
58 one_hot_decode_rows(target_solution, self.num_vertices, self.num_colors, 0)
59 }
60}
61
62fn reduce_kcoloring_to_ilp<K: KValue, G: Graph>(
64 problem: &KColoring<K, G>,
65) -> Result<ReductionKColoringToILP<K, G>, crate::registry::ConstructionError> {
66 let k = problem.num_colors();
67 let num_vertices = problem.graph().num_vertices();
68 let num_vars = num_vertices * k;
69
70 let var_index = |v: usize, c: usize| -> usize { v * k + c };
72
73 let mut constraints = Vec::new();
74
75 for v in 0..num_vertices {
78 let terms: Vec<(usize, i64)> = (0..k).map(|c| (var_index(v, c), 1)).collect();
79 constraints.push(LinearConstraint::eq(terms, 1));
80 }
81
82 for (u, v) in problem.graph().edges() {
85 for c in 0..k {
86 constraints.push(LinearConstraint::le(
87 vec![(var_index(u, c), 1), (var_index(v, c), 1)],
88 1,
89 ));
90 }
91 }
92
93 let objective: Vec<(usize, i64)> = vec![];
96
97 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize)?;
98
99 Ok(ReductionKColoringToILP {
100 target,
101 num_vertices,
102 num_colors: k,
103 _phantom: std::marker::PhantomData,
104 })
105}
106
107crate::register_aggregate_reduction!(ReductionKColoringToILP<KN, SimpleGraph>);
108
109#[crate::aggregate_reduction(ilp_feasibility)]
110impl<K: KValue, G: Graph + crate::variant::VariantParam> crate::rules::AggregateReductionResult
111 for ReductionKColoringToILP<K, G>
112{
113}
114
115#[reduction(transform = {
118 exact {
119 num_vars = "num_vertices * num_colors",
120 num_constraints = "num_vertices + num_edges * num_colors",
121 },
122 upper_bound {
123 num_nonzeros = "num_colors * (num_vertices + 2 * num_edges)",
124 max_constraint_magnitude_bits = "2",
125 },
126})]
127impl ReduceTo<ILP<bool>> for KColoring<KN, SimpleGraph> {
128 type Result = ReductionKColoringToILP<KN, SimpleGraph>;
129
130 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
131 reduce_kcoloring_to_ilp(self).map_err(<Self as ReduceTo<ILP<bool>>>::target_construction)
132 }
133}
134
135macro_rules! impl_kcoloring_to_ilp {
137 ($($ktype:ty),+) => {$(
138 impl ReduceTo<ILP<bool>> for KColoring<$ktype, SimpleGraph> {
139 type Result = ReductionKColoringToILP<$ktype, SimpleGraph>;
140 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
141 reduce_kcoloring_to_ilp(self)
142 .map_err(<Self as ReduceTo<ILP<bool>>>::target_construction)
143 }
144 }
145 )+};
146}
147
148impl_kcoloring_to_ilp!(K2, K3);
149
150#[cfg(feature = "example-db")]
151pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
152 use crate::export::SolutionPair;
153 use crate::topology::SimpleGraph;
154
155 vec![crate::example_db::specs::RuleExampleSpec {
156 id: "kcoloring_to_ilp",
157 build: || {
158 let (n, edges) = crate::topology::small_graphs::petersen();
159 let source = KColoring::<KN, _>::with_k(SimpleGraph::new(n, edges), 3);
160 crate::example_db::specs::rule_example_with_witness::<_, ILP<bool>>(
161 source,
162 SolutionPair {
163 source_config: serde_json::json!(vec![0, 2, 0, 1, 2, 1, 1, 2, 0, 0]),
164 target_config: serde_json::json!(vec![
165 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1,
166 0, 0, 1, 0, 0,
167 ]),
168 },
169 )
170 },
171 }]
172}
173
174#[cfg(test)]
175#[path = "../unit_tests/rules/coloring_ilp.rs"]
176mod tests;