problemreductions/rules/
monochromatictriangle_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
8use crate::models::graph::MonochromaticTriangle;
9use crate::reduction;
10use crate::rules::traits::{ReduceTo, ReductionResult};
11use crate::topology::{Graph, SimpleGraph};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone)]
16pub struct ReductionMonochromaticTriangleToILP {
17 target: ILP<bool>,
18}
19
20impl ReductionResult for ReductionMonochromaticTriangleToILP {
21 type Source = MonochromaticTriangle<SimpleGraph>;
22 type Target = ILP<bool>;
23
24 fn target_problem(&self) -> &Self::Target {
25 &self.target
26 }
27
28 fn extract_solution(
29 &self,
30 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
31 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
32 crate::rules::traits::validate_target_witness(
33 self.target_problem(),
34 target_solution,
35 |value| value.value.is_some(),
36 "target ILP assignment is infeasible",
37 )?;
38
39 Ok(target_solution.iter().map(|&value| value == 1).collect())
40 }
41}
42
43#[crate::aggregate_reduction(ilp_feasibility)]
44impl crate::rules::AggregateReductionResult for ReductionMonochromaticTriangleToILP {}
45
46#[reduction(transform = upper_bound {
52 max_constraint_magnitude_bits = "2",
53 num_vars = "num_edges",
54 num_constraints = "2 * num_triangles + 3 * num_triangles * (num_vertices - 3) * (num_vertices - 4) / 4",
55 num_nonzeros = "6 * num_triangles + 2 * num_triangles * (num_vertices - 3) * (num_vertices - 4)",
56})]
57impl ReduceTo<ILP<bool>> for MonochromaticTriangle<SimpleGraph> {
58 type Result = ReductionMonochromaticTriangleToILP;
59
60 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
61 let mut constraints = Vec::with_capacity(2 * self.num_triangles());
62 for triangle in self.triangles() {
63 let terms: Vec<(usize, i64)> = triangle.iter().map(|&edge_idx| (edge_idx, 1)).collect();
64 constraints.push(LinearConstraint::ge(terms.clone(), 1));
65 constraints.push(LinearConstraint::le(terms, 2));
66 }
67
68 let graph = self.graph();
73 let edge_list = self.edge_list();
74 let edge_indices: HashMap<_, _> = edge_list
75 .iter()
76 .enumerate()
77 .map(|(index, &(u, v))| ((u.min(v), u.max(v)), index))
78 .collect();
79 for triangle in self.triangles() {
80 let mut vertices: Vec<_> = triangle
81 .iter()
82 .flat_map(|&edge| [edge_list[edge].0, edge_list[edge].1])
83 .collect();
84 vertices.sort_unstable();
85 vertices.dedup();
86 let [a, b, c] = [vertices[0], vertices[1], vertices[2]];
87 let mut common: Vec<_> = graph
88 .neighbors(c)
89 .into_iter()
90 .filter(|&v| !vertices.contains(&v) && graph.has_edge(a, v) && graph.has_edge(b, v))
91 .collect();
92 common.sort_unstable();
93 common.dedup();
94 let mut first_opposite = None;
99 for (index, &d) in common.iter().enumerate() {
100 for &e in &common[index + 1..] {
101 if graph.has_edge(d, e) {
102 let opposite = edge_indices[&(d, e)];
103 if let Some(first) = first_opposite {
104 constraints
105 .push(LinearConstraint::eq(vec![(first, 1), (opposite, -1)], 0));
106 } else {
107 first_opposite = Some(opposite);
108 }
109 if d <= c {
112 continue;
113 }
114 let clique = [a, b, c, d, e];
115 for &u in &clique {
116 let terms = clique
117 .iter()
118 .filter(|&&v| v != u)
119 .map(|&v| (edge_indices[&(u.min(v), u.max(v))], 1))
120 .collect();
121 constraints.push(LinearConstraint::eq(terms, 2));
122 }
123 }
124 }
125 }
126 }
127
128 Ok(ReductionMonochromaticTriangleToILP {
129 target: ILP::new(
130 self.num_edges(),
131 constraints,
132 vec![],
133 ObjectiveSense::Minimize,
134 )
135 .map_err(Self::target_construction)?,
136 })
137 }
138}
139
140#[cfg(feature = "example-db")]
141pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
142 use crate::topology::SimpleGraph;
143
144 vec![crate::example_db::specs::RuleExampleSpec {
145 id: "monochromatictriangle_to_ilp",
146 build: || {
147 let source = MonochromaticTriangle::new(SimpleGraph::new(
148 4,
149 vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
150 ));
151 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
152 },
153 }]
154}
155
156#[cfg(test)]
157#[path = "../unit_tests/rules/monochromatictriangle_ilp.rs"]
158mod tests;