1use crate::models::algebraic::{Bounded, IntegerVariable, LinearConstraint, ObjectiveSense, ILP};
11use crate::models::graph::OptimalLinearArrangement;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14use crate::topology::{Graph, SimpleGraph};
15
16#[derive(Debug, Clone)]
23pub struct ReductionOLAToILP {
24 target: ILP<i64, i64, Bounded>,
25 num_vertices: usize,
26}
27
28impl ReductionResult for ReductionOLAToILP {
29 type Source = OptimalLinearArrangement<SimpleGraph>;
30 type Target = ILP<i64, i64, Bounded>;
31
32 fn target_problem(&self) -> &ILP<i64, i64, Bounded> {
33 &self.target
34 }
35
36 fn extract_solution(
38 &self,
39 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
40 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
41 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
42
43 crate::rules::ilp_helpers::one_hot_decode_rows(
44 target_solution,
45 self.num_vertices,
46 self.num_vertices,
47 0,
48 )
49 }
50}
51
52#[reduction(transform = {
53 exact {
54 num_vars = "num_vertices^2 + num_vertices + num_edges",
55 num_constraints = "2 * num_vertices + num_vertices^2 + num_vertices + num_vertices + 3 * num_edges",
56 },
57 upper_bound {
58 max_constraint_magnitude_bits = "num_vertices + 1",
59 num_nonzeros = "(num_vertices^2 + num_vertices + num_edges) * (2 * num_vertices + num_vertices^2 + num_vertices + num_vertices + 3 * num_edges)",
60 },
61})]
62impl ReduceTo<ILP<i64, i64, Bounded>> for OptimalLinearArrangement<SimpleGraph> {
63 type Result = ReductionOLAToILP;
64
65 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
66 let n = self.num_vertices();
67 let graph = self.graph();
68 let edges = graph.edges();
69 let m = edges.len();
70
71 let num_x = n * n;
72 let num_vars = num_x + n + m;
73
74 let x_idx = |v: usize, p: usize| -> usize { v * n + p };
75 let p_idx = |v: usize| -> usize { num_x + v };
76 let z_idx = |e: usize| -> usize { num_x + n + e };
77
78 let mut constraints = Vec::new();
79 let n_i64 =
80 <Self as ReduceTo<ILP<i64, i64, Bounded>>>::exact_i64(n, "encoding a vertex position")?;
81
82 for v in 0..n {
84 let terms: Vec<(usize, i64)> = (0..n).map(|p| (x_idx(v, p), 1)).collect();
85 constraints.push(LinearConstraint::eq(terms, 1));
86 }
87
88 for p in 0..n {
90 let terms: Vec<(usize, i64)> = (0..n).map(|v| (x_idx(v, p), 1)).collect();
91 constraints.push(LinearConstraint::eq(terms, 1));
92 }
93
94 for v in 0..n {
96 for p in 0..n {
97 constraints.push(LinearConstraint::le(vec![(x_idx(v, p), 1)], 1));
98 }
99 }
100
101 for v in 0..n {
104 let mut terms: Vec<(usize, i64)> = vec![(p_idx(v), 1)];
105 for p in 0..n {
106 terms.push((
107 x_idx(v, p),
108 -<Self as ReduceTo<ILP<i64, i64, Bounded>>>::exact_i64(
109 p,
110 "encoding a vertex position",
111 )?,
112 ));
113 }
114 constraints.push(LinearConstraint::eq(terms, 0));
115 }
116
117 for v in 0..n {
119 constraints.push(LinearConstraint::le(vec![(p_idx(v), 1)], n_i64 - 1));
120 }
121
122 for (e, &(u, v)) in edges.iter().enumerate() {
124 constraints.push(LinearConstraint::ge(
126 vec![(z_idx(e), 1), (p_idx(u), -1), (p_idx(v), 1)],
127 0,
128 ));
129 constraints.push(LinearConstraint::ge(
131 vec![(z_idx(e), 1), (p_idx(v), -1), (p_idx(u), 1)],
132 0,
133 ));
134 constraints.push(LinearConstraint::le(vec![(z_idx(e), 1)], n_i64 - 1));
136 }
137
138 let objective: Vec<(usize, i64)> = (0..m).map(|e| (z_idx(e), 1)).collect();
140 let mut variables = vec![IntegerVariable::binary(); num_vars];
141 variables[num_x..].fill(
142 IntegerVariable::new(Some(0), Some((n_i64 - 1).max(0)))
143 .map_err(<Self as ReduceTo<ILP<i64, i64, Bounded>>>::target_construction)?,
144 );
145
146 let target =
147 ILP::with_variables(variables, constraints, objective, ObjectiveSense::Minimize)
148 .map_err(<Self as ReduceTo<ILP<i64, i64, Bounded>>>::target_construction)?;
149
150 Ok(ReductionOLAToILP {
151 target,
152 num_vertices: n,
153 })
154 }
155}
156
157#[cfg(feature = "example-db")]
158pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
159 vec![crate::example_db::specs::RuleExampleSpec {
160 id: "optimallineararrangement_to_ilp",
161 build: || {
162 let source =
164 OptimalLinearArrangement::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]));
165 crate::example_db::specs::rule_example_via_bounded_ilp::<_>(source)
166 },
167 }]
168}
169
170#[cfg(test)]
171#[path = "../unit_tests/rules/optimallineararrangement_ilp.rs"]
172mod tests;