1use crate::models::decision::Decision;
12use crate::models::graph::{MinimumCoveringByCliques, PartitionIntoCliques};
13use crate::reduction;
14use crate::rules::traits::{ReduceTo, ReductionResult};
15use crate::topology::{Graph, SimpleGraph};
16use std::collections::BTreeMap;
17
18#[derive(Debug, Clone)]
19struct OrlinLayout {
20 num_source_vertices: usize,
21 directed_pairs: Vec<(usize, usize)>,
22}
23
24impl OrlinLayout {
25 fn new(graph: &SimpleGraph) -> Self {
26 let n = graph.num_vertices();
27 let directed_pairs = (0..n)
28 .flat_map(|i| {
29 (0..n).filter_map(move |j| (i != j && graph.has_edge(i, j)).then_some((i, j)))
30 })
31 .collect();
32 Self {
33 num_source_vertices: n,
34 directed_pairs,
35 }
36 }
37
38 fn num_directed_pairs(&self) -> usize {
39 self.directed_pairs.len()
40 }
41
42 fn x(&self, i: usize) -> usize {
43 i
44 }
45
46 fn y(&self, i: usize) -> usize {
47 self.num_source_vertices + i
48 }
49
50 fn a(&self, directed_pair_index: usize) -> usize {
51 2 * self.num_source_vertices + directed_pair_index
52 }
53
54 fn b(&self, directed_pair_index: usize) -> usize {
55 2 * self.num_source_vertices + self.num_directed_pairs() + directed_pair_index
56 }
57
58 fn z_left(&self) -> usize {
59 2 * self.num_source_vertices + 2 * self.num_directed_pairs()
60 }
61
62 fn z_right(&self) -> usize {
63 self.z_left() + 1
64 }
65
66 fn left_vertices(&self) -> Vec<usize> {
67 let mut vertices = (0..self.num_source_vertices)
68 .map(|i| self.x(i))
69 .collect::<Vec<_>>();
70 vertices.extend((0..self.num_directed_pairs()).map(|idx| self.a(idx)));
71 vertices.push(self.z_right() + 1);
72 vertices
73 }
74
75 fn right_vertices(&self) -> Vec<usize> {
76 let mut vertices = (0..self.num_source_vertices)
77 .map(|i| self.y(i))
78 .collect::<Vec<_>>();
79 vertices.extend((0..self.num_directed_pairs()).map(|idx| self.b(idx)));
80 vertices.push(self.z_right() + 2);
81 vertices
82 }
83
84 fn dimensions(&self) -> Result<(usize, usize), crate::rules::ReductionError> {
85 let n = self.num_source_vertices;
86 let q = self.num_directed_pairs();
87 let overflow = |operation: &str| {
88 crate::rules::ReductionError::integer_overflow::<
89 PartitionIntoCliques<SimpleGraph>,
90 Decision<MinimumCoveringByCliques<SimpleGraph>>,
91 >(operation)
92 };
93 let side = n
96 .checked_add(q)
97 .and_then(|s| s.checked_add(1))
98 .ok_or_else(|| overflow("counting side vertices"))?;
99 let target_vertices = side
100 .checked_mul(2)
101 .and_then(|s| s.checked_add(2))
102 .ok_or_else(|| overflow("counting target vertices"))?;
103 let target_edges = side
104 .checked_mul(side)
105 .and_then(|s| s.checked_add(side))
106 .and_then(|s| s.checked_add(n))
107 .and_then(|s| q.checked_mul(4).and_then(|cross| s.checked_add(cross)))
108 .ok_or_else(|| overflow("counting target edges"))?;
109 <PartitionIntoCliques<SimpleGraph> as ReduceTo<
110 Decision<MinimumCoveringByCliques<SimpleGraph>>,
111 >>::exact_i64(target_edges, "representing every target cover value")?;
112 Ok((target_vertices, target_edges))
113 }
114}
115
116fn add_clique_edges(vertices: &[usize], edges: &mut Vec<(usize, usize)>) {
117 for i in 0..vertices.len() {
118 for j in (i + 1)..vertices.len() {
119 edges.push((vertices[i], vertices[j]));
120 }
121 }
122}
123
124fn target_clique_bound(
125 num_cliques: i64,
126 num_directed_pairs: i64,
127) -> Result<i64, crate::rules::ReductionError> {
128 num_directed_pairs
129 .checked_add(2)
130 .and_then(|offset| num_cliques.checked_add(offset))
131 .ok_or_else(|| {
132 crate::rules::ReductionError::integer_overflow::<
133 PartitionIntoCliques<SimpleGraph>,
134 Decision<MinimumCoveringByCliques<SimpleGraph>>,
135 >("computing target clique bound")
136 })
137}
138
139#[derive(Debug, Clone)]
141pub struct ReductionPartitionIntoCliquesToMinimumCoveringByCliques {
142 target: Decision<MinimumCoveringByCliques<SimpleGraph>>,
143 num_source_vertices: usize,
144}
145
146impl ReductionResult for ReductionPartitionIntoCliquesToMinimumCoveringByCliques {
147 type Source = PartitionIntoCliques<SimpleGraph>;
148 type Target = Decision<MinimumCoveringByCliques<SimpleGraph>>;
149
150 fn target_problem(&self) -> &Self::Target {
151 &self.target
152 }
153
154 fn extract_solution(
155 &self,
156 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
157 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
158 crate::rules::traits::validate_target_witness(
159 self.target_problem(),
160 target_solution,
161 |value| value.0,
162 "target cover does not certify the source clique bound",
163 )?;
164
165 Ok({
166 let n = self.num_source_vertices;
167 let target_edges = self.target.inner().graph().edges();
168 let mut matching_labels = vec![None; n];
169 for ((u, v), &label) in target_edges.iter().zip(target_solution.iter()) {
170 let matching_index = if *u < n && *v == n + *u {
171 Some(*u)
172 } else if *v < n && *u == n + *v {
173 Some(*v)
174 } else {
175 None
176 };
177
178 if let Some(i) = matching_index {
179 matching_labels[i] = Some(label);
180 }
181 }
182
183 let mut label_map = BTreeMap::new();
184 let extracted = matching_labels
185 .into_iter()
186 .map(|label| {
187 let label = label.ok_or_else(|| {
188 crate::rules::ExtractionError::invalid(
189 "target cover does not label every matching gadget edge",
190 )
191 })?;
192 let next = label_map.len();
193 Ok(*label_map.entry(label).or_insert(next))
194 })
195 .collect::<crate::rules::ExtractionResult<Vec<_>>>()?;
196
197 extracted
200 })
201 }
202}
203
204#[crate::aggregate_reduction(identity)]
205impl crate::rules::AggregateReductionResult
206 for ReductionPartitionIntoCliquesToMinimumCoveringByCliques
207{
208}
209
210#[reduction(
211 transform = upper_bound {
212 num_vertices = "2 * num_vertices + 4 * num_edges + 4",
213 num_edges = "(num_vertices + 2 * num_edges)^2 + 4 * num_vertices + 14 * num_edges + 2",
214 }
215)]
216impl ReduceTo<Decision<MinimumCoveringByCliques<SimpleGraph>>>
217 for PartitionIntoCliques<SimpleGraph>
218{
219 type Result = ReductionPartitionIntoCliquesToMinimumCoveringByCliques;
220
221 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
222 let n = self.num_vertices();
223 let layout = OrlinLayout::new(self.graph());
224 let q = layout.num_directed_pairs();
225 let (target_vertices, target_edges) = layout.dimensions()?;
226 let source_bound =
227 <Self as ReduceTo<Decision<MinimumCoveringByCliques<SimpleGraph>>>>::exact_i64(
228 self.num_cliques().min(n),
229 "converting effective clique bound",
230 )?;
231 let directed_pairs =
232 <Self as ReduceTo<Decision<MinimumCoveringByCliques<SimpleGraph>>>>::exact_i64(
233 q,
234 "converting gadget count",
235 )?;
236 let target_bound = target_clique_bound(source_bound, directed_pairs)?;
237 let left_vertices = layout.left_vertices();
238 let right_vertices = layout.right_vertices();
239 let mut edges = Vec::with_capacity(target_edges);
240
241 add_clique_edges(&left_vertices, &mut edges);
243 add_clique_edges(&right_vertices, &mut edges);
244
245 for &u in &left_vertices {
247 edges.push((layout.z_left(), u));
248 }
249 for &u in &right_vertices {
250 edges.push((layout.z_right(), u));
251 }
252
253 for i in 0..self.num_vertices() {
255 edges.push((layout.x(i), layout.y(i)));
256 }
257
258 for (idx, &(i, j)) in layout.directed_pairs.iter().enumerate() {
260 edges.push((layout.x(i), layout.y(j)));
261 edges.push((layout.x(i), layout.b(idx)));
262 edges.push((layout.a(idx), layout.y(j)));
263 edges.push((layout.a(idx), layout.b(idx)));
264 }
265
266 let target_graph = SimpleGraph::new(target_vertices, edges);
267 let target = MinimumCoveringByCliques::new(target_graph);
268
269 Ok(ReductionPartitionIntoCliquesToMinimumCoveringByCliques {
270 target: Decision::new(target, target_bound),
271 num_source_vertices: n,
272 })
273 }
274}
275
276#[cfg(any(test, feature = "example-db"))]
277fn edge_labels_from_clique_cover(graph: &SimpleGraph, cliques: &[Vec<usize>]) -> Vec<usize> {
278 let clique_sets = cliques
279 .iter()
280 .map(|clique| {
281 clique
282 .iter()
283 .copied()
284 .collect::<std::collections::BTreeSet<_>>()
285 })
286 .collect::<Vec<_>>();
287
288 graph
289 .edges()
290 .into_iter()
291 .map(|(u, v)| {
292 clique_sets
293 .iter()
294 .position(|clique| clique.contains(&u) && clique.contains(&v))
295 .expect("canonical cover should cover every target edge")
296 })
297 .collect()
298}
299
300#[cfg(feature = "example-db")]
301pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
302 use crate::export::SolutionPair;
303
304 vec![crate::example_db::specs::RuleExampleSpec {
305 id: "partitionintocliques_to_minimumcoveringbycliques",
306 build: || {
307 let source = PartitionIntoCliques::new(SimpleGraph::new(3, vec![(0, 1)]), 2);
308 let reduction =
309 ReduceTo::<Decision<MinimumCoveringByCliques<SimpleGraph>>>::reduce_to(&source)
310 .expect("reduction should succeed");
311 let layout = OrlinLayout::new(source.graph());
312
313 let target_config = edge_labels_from_clique_cover(
314 reduction.target_problem().inner().graph(),
315 &[
316 vec![layout.x(0), layout.x(1), layout.y(0), layout.y(1)],
317 vec![layout.x(2), layout.y(2)],
318 vec![layout.x(0), layout.a(0), layout.b(0), layout.y(1)],
319 vec![layout.x(1), layout.a(1), layout.b(1), layout.y(0)],
320 {
321 let mut clique = layout.left_vertices();
322 clique.push(layout.z_left());
323 clique
324 },
325 {
326 let mut clique = layout.right_vertices();
327 clique.push(layout.z_right());
328 clique
329 },
330 ],
331 );
332
333 crate::example_db::specs::rule_example_with_witness::<
334 _,
335 Decision<MinimumCoveringByCliques<SimpleGraph>>,
336 >(
337 source,
338 SolutionPair {
339 source_config: serde_json::json!(vec![0, 0, 1]),
340 target_config: serde_json::to_value(target_config)
341 .expect("solution serialization must succeed"),
342 },
343 )
344 },
345 }]
346}
347
348#[cfg(test)]
349#[path = "../unit_tests/rules/partitionintocliques_minimumcoveringbycliques.rs"]
350mod tests;