problemreductions/rules/
consecutiveblockminimization_ilp.rs1use crate::models::algebraic::{
7 ConsecutiveBlockMinimization, LinearConstraint, ObjectiveSense, ILP,
8};
9use crate::reduction;
10use crate::rules::ilp_helpers::{one_hot_assignment_constraints, one_hot_decode};
11use crate::rules::traits::{ReduceTo, ReductionResult};
12
13#[derive(Debug, Clone)]
14pub struct ReductionCBMToILP {
15 target: ILP<bool>,
16 num_cols: usize,
17}
18
19impl ReductionResult for ReductionCBMToILP {
20 type Source = ConsecutiveBlockMinimization;
21 type Target = ILP<bool>;
22
23 fn target_problem(&self) -> &ILP<bool> {
24 &self.target
25 }
26
27 fn extract_solution(
28 &self,
29 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
30 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
31 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
32
33 one_hot_decode(target_solution, self.num_cols, self.num_cols, 0)
34 }
35}
36
37#[reduction(
38 transform = upper_bound {
39 num_vars = "num_cols * num_cols + num_rows * num_cols + num_rows * num_cols",
40 num_constraints = "num_cols + num_cols + num_rows * num_cols + num_rows + num_rows * num_cols + 1",
41 },
42 unavailable = {
43 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
44 }
45)]
46impl ReduceTo<ILP<bool>> for ConsecutiveBlockMinimization {
47 type Result = ReductionCBMToILP;
48
49 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
50 let m = self.num_rows();
51 let n = self.num_cols();
52
53 let x_offset = 0;
61 let a_offset = n * n;
62 let b_offset = n * n + m * n;
63 let num_vars = n * n + 2 * m * n;
64
65 let mut constraints = Vec::new();
66
67 constraints.extend(one_hot_assignment_constraints(n, n, x_offset));
69
70 for r in 0..m {
72 for p in 0..n {
73 let a_idx = a_offset + r * n + p;
74 let mut terms = vec![(a_idx, 1)];
76 for c in 0..n {
77 if self.matrix()[r][c] {
78 terms.push((x_offset + c * n + p, -1));
79 }
80 }
81 constraints.push(LinearConstraint::eq(terms, 0));
82 }
83 }
84
85 for r in 0..m {
87 let b_idx = b_offset + r * n;
89 let a_idx = a_offset + r * n;
90 constraints.push(LinearConstraint::eq(vec![(b_idx, 1), (a_idx, -1)], 0));
91
92 for p in 1..n {
94 let b_idx = b_offset + r * n + p;
95 let a_cur = a_offset + r * n + p;
96 let a_prev = a_offset + r * n + (p - 1);
97 constraints.push(LinearConstraint::ge(
98 vec![(b_idx, 1), (a_cur, -1), (a_prev, 1)],
99 0,
100 ));
101 }
102 }
103
104 let mut bound_terms = Vec::new();
106 for r in 0..m {
107 for p in 0..n {
108 bound_terms.push((b_offset + r * n + p, 1));
109 }
110 }
111 constraints.push(LinearConstraint::le(bound_terms, self.bound()));
112
113 let target = ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize)
114 .map_err(Self::target_construction)?;
115 Ok(ReductionCBMToILP {
116 target,
117 num_cols: n,
118 })
119 }
120}
121
122#[cfg(feature = "example-db")]
123pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
124 vec![crate::example_db::specs::RuleExampleSpec {
125 id: "consecutiveblockminimization_to_ilp",
126 build: || {
127 let source = ConsecutiveBlockMinimization::new(
129 vec![vec![true, false, true], vec![false, true, true]],
130 2,
131 );
132 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
133 },
134 }]
135}
136
137#[cfg(test)]
138#[path = "../unit_tests/rules/consecutiveblockminimization_ilp.rs"]
139mod tests;