1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
14use crate::models::misc::FeasibleRegisterAssignment;
15use crate::reduction;
16use crate::rules::traits::{ReduceTo, ReductionResult};
17
18#[derive(Debug, Clone)]
19pub struct ReductionFeasibleRegisterAssignmentToILP {
20 target: ILP<i64>,
21 num_vertices: usize,
22}
23
24impl ReductionResult for ReductionFeasibleRegisterAssignmentToILP {
25 type Source = FeasibleRegisterAssignment;
26 type Target = ILP<i64>;
27
28 fn target_problem(&self) -> &ILP<i64> {
29 &self.target
30 }
31
32 fn extract_solution(
33 &self,
34 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
35 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
36 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
37
38 crate::rules::ilp_helpers::decode_usize_values(&target_solution[..self.num_vertices])
39 }
40}
41
42#[reduction(
43 transform = exact {
44 num_vars = "2 * num_vertices + num_vertices * (num_vertices - 1) / 2",
45 num_constraints = "3 * num_vertices * (num_vertices - 1) / 2 + 3 * num_vertices + 2 * num_arcs + 2 * num_same_register_pairs",
46 },
47 unavailable = {
48 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
49 }
50)]
51impl ReduceTo<ILP<i64>> for FeasibleRegisterAssignment {
52 type Result = ReductionFeasibleRegisterAssignmentToILP;
53
54 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
55 let n = self.num_vertices();
56 let pair_list: Vec<(usize, usize)> = (0..n)
57 .flat_map(|u| ((u + 1)..n).map(move |v| (u, v)))
58 .collect();
59 let same_register_pairs: Vec<(usize, usize, usize)> = pair_list
60 .iter()
61 .copied()
62 .enumerate()
63 .filter(|(_, (u, v))| self.assignment()[*u] == self.assignment()[*v])
64 .map(|(pair_idx, (u, v))| (u, v, pair_idx))
65 .collect();
66
67 let num_pair_vars = pair_list.len();
68 let num_vars = 2 * n + num_pair_vars;
69 let big_m = Self::exact_i64(n, "encoding the schedule order")?;
70 let last_position =
71 Self::exact_i64(n.saturating_sub(1), "encoding the final schedule position")?;
72
73 let time_idx = |vertex: usize| -> usize { vertex };
74 let latest_idx = |vertex: usize| -> usize { n + vertex };
75 let order_idx = |pair_idx: usize| -> usize { 2 * n + pair_idx };
76
77 let mut constraints = Vec::with_capacity(
78 3 * num_pair_vars + 3 * n + 2 * self.num_arcs() + 2 * same_register_pairs.len(),
79 );
80
81 for vertex in 0..n {
82 constraints.push(LinearConstraint::le(
83 vec![(time_idx(vertex), 1)],
84 last_position,
85 ));
86 constraints.push(LinearConstraint::le(
87 vec![(latest_idx(vertex), 1)],
88 last_position,
89 ));
90 constraints.push(LinearConstraint::ge(
91 vec![(latest_idx(vertex), 1), (time_idx(vertex), -1)],
92 0,
93 ));
94 }
95
96 for &(dependent, dependency) in self.arcs() {
97 constraints.push(LinearConstraint::ge(
98 vec![(time_idx(dependent), 1), (time_idx(dependency), -1)],
99 1,
100 ));
101 constraints.push(LinearConstraint::ge(
102 vec![(latest_idx(dependency), 1), (time_idx(dependent), -1)],
103 0,
104 ));
105 }
106
107 for (pair_idx, &(u, v)) in pair_list.iter().enumerate() {
108 let order_var = order_idx(pair_idx);
109 constraints.push(LinearConstraint::le(vec![(order_var, 1)], 1));
110 constraints.push(LinearConstraint::ge(
111 vec![(time_idx(v), 1), (time_idx(u), -1), (order_var, -big_m)],
112 1 - big_m,
113 ));
114 constraints.push(LinearConstraint::ge(
115 vec![(time_idx(u), 1), (time_idx(v), -1), (order_var, big_m)],
116 1,
117 ));
118 }
119
120 for &(u, v, pair_idx) in &same_register_pairs {
121 let order_var = order_idx(pair_idx);
122 constraints.push(LinearConstraint::ge(
123 vec![(time_idx(v), 1), (latest_idx(u), -1), (order_var, -big_m)],
124 -big_m,
125 ));
126 constraints.push(LinearConstraint::ge(
127 vec![(time_idx(u), 1), (latest_idx(v), -1), (order_var, big_m)],
128 0,
129 ));
130 }
131
132 Ok(ReductionFeasibleRegisterAssignmentToILP {
133 target: ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize)
134 .map_err(Self::target_construction)?,
135 num_vertices: n,
136 })
137 }
138}
139
140#[cfg(feature = "example-db")]
141pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
142 vec![crate::example_db::specs::RuleExampleSpec {
143 id: "feasibleregisterassignment_to_ilp",
144 build: || {
145 let source = FeasibleRegisterAssignment::new(
146 4,
147 vec![(0, 1), (0, 2), (1, 3)],
148 2,
149 vec![0, 1, 0, 0],
150 );
151 crate::example_db::specs::rule_example_via_ilp::<_, i64>(source)
152 },
153 }]
154}
155
156#[cfg(test)]
157#[path = "../unit_tests/rules/feasibleregisterassignment_ilp.rs"]
158mod tests;