problemreductions/rules/
partitionintopathsoflength2_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
20use crate::models::graph::PartitionIntoPathsOfLength2;
21use crate::reduction;
22use crate::rules::ilp_helpers::mccormick_product;
23use crate::rules::traits::{ReduceTo, ReductionResult};
24use crate::topology::{Graph, SimpleGraph};
25
26#[derive(Debug, Clone)]
32pub struct ReductionPIPL2ToILP {
33 target: ILP<bool>,
34 num_vertices: usize,
35 num_groups: usize,
36}
37
38impl ReductionResult for ReductionPIPL2ToILP {
39 type Source = PartitionIntoPathsOfLength2<SimpleGraph>;
40 type Target = ILP<bool>;
41
42 fn target_problem(&self) -> &ILP<bool> {
43 &self.target
44 }
45
46 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_solution(self.target_problem(), target_solution)?;
52
53 crate::rules::ilp_helpers::one_hot_decode_rows(
54 target_solution,
55 self.num_vertices,
56 self.num_groups,
57 0,
58 )
59 }
60}
61
62#[reduction(
63 transform = upper_bound {
64 num_vars = "num_vertices^2 + num_edges * num_vertices",
65 num_constraints = "num_vertices^2 + num_edges * num_vertices + num_vertices",
66 },
67 unavailable = {
68 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
69 }
70)]
71impl ReduceTo<ILP<bool>> for PartitionIntoPathsOfLength2<SimpleGraph> {
72 type Result = ReductionPIPL2ToILP;
73
74 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
75 let num_vertices = self.num_vertices();
76 let q = self.num_groups();
77 let edges: Vec<(usize, usize)> = self.graph().edges();
78 let num_edges = edges.len();
79 let num_vars = num_vertices * q + num_edges * q;
80
81 let mut constraints = Vec::new();
82
83 for v in 0..num_vertices {
85 let terms: Vec<(usize, i64)> = (0..q).map(|g| (v * q + g, 1)).collect();
86 constraints.push(LinearConstraint::eq(terms, 1));
87 }
88
89 for g in 0..q {
91 let terms: Vec<(usize, i64)> = (0..num_vertices).map(|v| (v * q + g, 1)).collect();
92 constraints.push(LinearConstraint::eq(terms, 3));
93 }
94
95 for (e, &(u, v)) in edges.iter().enumerate() {
98 for g in 0..q {
99 let y = num_vertices * q + e * q + g;
100 let xu = u * q + g;
101 let xv = v * q + g;
102
103 constraints.extend(mccormick_product(y, xu, xv));
104 }
105 }
106
107 for g in 0..q {
109 let terms: Vec<(usize, i64)> = (0..num_edges)
110 .map(|e| (num_vertices * q + e * q + g, 1))
111 .collect();
112 constraints.push(LinearConstraint::ge(terms, 2));
113 }
114
115 let target = ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize)
116 .map_err(<Self as ReduceTo<ILP<bool>>>::target_construction)?;
117
118 Ok(ReductionPIPL2ToILP {
119 target,
120 num_vertices,
121 num_groups: q,
122 })
123 }
124}
125
126#[cfg(feature = "example-db")]
127pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
128 vec![crate::example_db::specs::RuleExampleSpec {
129 id: "partitionintopathsoflength2_to_ilp",
130 build: || {
131 let source = PartitionIntoPathsOfLength2::new(SimpleGraph::new(
133 6,
134 vec![(0, 1), (1, 2), (3, 4), (4, 5)],
135 ));
136 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
137 },
138 }]
139}
140
141#[cfg(test)]
142#[path = "../unit_tests/rules/partitionintopathsoflength2_ilp.rs"]
143mod tests;