1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
11use crate::models::misc::RegisterSufficiency;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14
15#[derive(Debug, Clone)]
16pub struct ReductionRegisterSufficiencyToILP {
17 target: ILP<i32>,
18 num_vertices: usize,
19}
20
21impl ReductionResult for ReductionRegisterSufficiencyToILP {
22 type Source = RegisterSufficiency;
23 type Target = ILP<i32>;
24
25 fn target_problem(&self) -> &ILP<i32> {
26 &self.target
27 }
28
29 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
30 target_solution[..self.num_vertices].to_vec()
31 }
32}
33
34#[reduction(overhead = {
35 num_vars = "3 * num_vertices^2 + num_vertices * (num_vertices - 1) / 2 + 2 * num_vertices",
36 num_constraints = "9 * num_vertices^2 + 3 * num_vertices * (num_vertices - 1) / 2 + 3 * num_vertices + 2 * num_arcs + num_sinks",
37})]
38impl ReduceTo<ILP<i32>> for RegisterSufficiency {
39 type Result = ReductionRegisterSufficiencyToILP;
40
41 fn reduce_to(&self) -> Self::Result {
42 let n = self.num_vertices();
43 let pair_list: Vec<(usize, usize)> = (0..n)
44 .flat_map(|u| ((u + 1)..n).map(move |v| (u, v)))
45 .collect();
46 let num_pair_vars = pair_list.len();
47
48 let time_offset = 0;
49 let latest_offset = n;
50 let order_offset = 2 * n;
51 let before_offset = order_offset + num_pair_vars;
52 let after_offset = before_offset + n * n;
53 let live_offset = after_offset + n * n;
54 let num_vars = live_offset + n * n;
55
56 let time_idx = |vertex: usize| -> usize { time_offset + vertex };
57 let latest_idx = |vertex: usize| -> usize { latest_offset + vertex };
58 let order_idx = |pair_idx: usize| -> usize { order_offset + pair_idx };
59 let before_idx =
60 |vertex: usize, step: usize| -> usize { before_offset + vertex * n + step };
61 let after_idx = |vertex: usize, step: usize| -> usize { after_offset + vertex * n + step };
62 let live_idx = |vertex: usize, step: usize| -> usize { live_offset + vertex * n + step };
63
64 let big_m = n as f64;
65 let mut has_dependent = vec![false; n];
66 let mut constraints = Vec::new();
67
68 for vertex in 0..n {
69 constraints.push(LinearConstraint::le(
70 vec![(time_idx(vertex), 1.0)],
71 (n.saturating_sub(1)) as f64,
72 ));
73 constraints.push(LinearConstraint::le(
74 vec![(latest_idx(vertex), 1.0)],
75 n as f64,
76 ));
77 }
78
79 for (pair_idx, &(u, v)) in pair_list.iter().enumerate() {
80 let order_var = order_idx(pair_idx);
81 constraints.push(LinearConstraint::le(vec![(order_var, 1.0)], 1.0));
82 constraints.push(LinearConstraint::ge(
83 vec![(time_idx(v), 1.0), (time_idx(u), -1.0), (order_var, -big_m)],
84 1.0 - big_m,
85 ));
86 constraints.push(LinearConstraint::ge(
87 vec![(time_idx(u), 1.0), (time_idx(v), -1.0), (order_var, big_m)],
88 1.0,
89 ));
90 }
91
92 for &(dependent, dependency) in self.arcs() {
93 has_dependent[dependency] = true;
94 constraints.push(LinearConstraint::ge(
95 vec![(time_idx(dependent), 1.0), (time_idx(dependency), -1.0)],
96 1.0,
97 ));
98 constraints.push(LinearConstraint::ge(
99 vec![(latest_idx(dependency), 1.0), (time_idx(dependent), -1.0)],
100 0.0,
101 ));
102 }
103
104 for (vertex, &has_child) in has_dependent.iter().enumerate() {
105 if !has_child {
106 constraints.push(LinearConstraint::eq(
107 vec![(latest_idx(vertex), 1.0)],
108 n as f64,
109 ));
110 }
111 }
112
113 for vertex in 0..n {
114 for step in 0..n {
115 let before_var = before_idx(vertex, step);
116 constraints.push(LinearConstraint::le(vec![(before_var, 1.0)], 1.0));
117 constraints.push(LinearConstraint::le(
118 vec![(time_idx(vertex), 1.0), (before_var, big_m)],
119 step as f64 + big_m,
120 ));
121 constraints.push(LinearConstraint::ge(
122 vec![(time_idx(vertex), 1.0), (before_var, big_m)],
123 (step + 1) as f64,
124 ));
125
126 let after_var = after_idx(vertex, step);
127 constraints.push(LinearConstraint::le(vec![(after_var, 1.0)], 1.0));
128 constraints.push(LinearConstraint::ge(
129 vec![(latest_idx(vertex), 1.0), (after_var, -big_m)],
130 (step + 1) as f64 - big_m,
131 ));
132 constraints.push(LinearConstraint::le(
133 vec![(latest_idx(vertex), 1.0), (after_var, -big_m)],
134 step as f64,
135 ));
136
137 let live_var = live_idx(vertex, step);
138 constraints.push(LinearConstraint::le(
139 vec![(live_var, 1.0), (before_var, -1.0)],
140 0.0,
141 ));
142 constraints.push(LinearConstraint::le(
143 vec![(live_var, 1.0), (after_var, -1.0)],
144 0.0,
145 ));
146 constraints.push(LinearConstraint::ge(
147 vec![(live_var, 1.0), (before_var, -1.0), (after_var, -1.0)],
148 -1.0,
149 ));
150 }
151 }
152
153 for step in 0..n {
154 let live_terms: Vec<(usize, f64)> =
155 (0..n).map(|vertex| (live_idx(vertex, step), 1.0)).collect();
156 constraints.push(LinearConstraint::le(live_terms, self.bound() as f64));
157 }
158
159 ReductionRegisterSufficiencyToILP {
160 target: ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize),
161 num_vertices: n,
162 }
163 }
164}
165
166#[cfg(feature = "example-db")]
167pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
168 vec![crate::example_db::specs::RuleExampleSpec {
169 id: "registersufficiency_to_ilp",
170 build: || {
171 let source = RegisterSufficiency::new(
172 7,
173 vec![
174 (2, 0),
175 (2, 1),
176 (3, 1),
177 (4, 2),
178 (4, 3),
179 (5, 0),
180 (6, 4),
181 (6, 5),
182 ],
183 3,
184 );
185 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
186 },
187 }]
188}
189
190#[cfg(test)]
191#[path = "../unit_tests/rules/registersufficiency_ilp.rs"]
192mod tests;