problemreductions/models/algebraic/
consecutive_block_minimization.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
12use crate::traits::Problem;
13use serde::{Deserialize, Serialize};
14
15inventory::submit! {
16 ProblemSchemaEntry {
17 name: "ConsecutiveBlockMinimization",
18 display_name: "Consecutive Block Minimization",
19 aliases: &["CBM"],
20 dimensions: &[],
21 module_path: module_path!(),
22 description: "Permute columns of a binary matrix to have at most K consecutive blocks of 1s",
23 fields: &[
24 FieldInfo { name: "matrix", type_name: "Vec<Vec<bool>>", description: "Binary matrix A (m x n)" },
25 FieldInfo { name: "bound", type_name: "i64", description: "Upper bound K on total consecutive blocks" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(
62 try_from = "ConsecutiveBlockMinimizationDef",
63 into = "ConsecutiveBlockMinimizationDef"
64)]
65pub struct ConsecutiveBlockMinimization {
66 matrix: Vec<Vec<bool>>,
68 num_rows: usize,
70 num_cols: usize,
72 bound: i64,
74}
75
76impl ConsecutiveBlockMinimization {
77 pub fn new(matrix: Vec<Vec<bool>>, bound: i64) -> Self {
86 Self::try_new(matrix, bound).unwrap_or_else(|err| panic!("{err}"))
87 }
88
89 pub fn try_new(matrix: Vec<Vec<bool>>, bound: i64) -> Result<Self, String> {
92 let (num_rows, num_cols) = validate_matrix_dimensions(&matrix)?;
93 Ok(Self {
94 matrix,
95 num_rows,
96 num_cols,
97 bound,
98 })
99 }
100
101 pub fn matrix(&self) -> &[Vec<bool>] {
103 &self.matrix
104 }
105
106 pub fn num_rows(&self) -> usize {
108 self.num_rows
109 }
110
111 pub fn num_cols(&self) -> usize {
113 self.num_cols
114 }
115
116 pub fn bound(&self) -> i64 {
118 self.bound
119 }
120
121 pub fn count_consecutive_blocks(&self, config: &[usize]) -> Option<usize> {
128 if config.len() != self.num_cols {
129 return None;
130 }
131
132 let mut seen = vec![false; self.num_cols];
134 for &col in config {
135 if col >= self.num_cols || seen[col] {
136 return None;
137 }
138 seen[col] = true;
139 }
140
141 let mut total_blocks = 0;
142 for row in &self.matrix {
143 let mut in_block = false;
144 for &pos in config {
145 if row[pos] {
146 if !in_block {
147 total_blocks += 1;
148 in_block = true;
149 }
150 } else {
151 in_block = false;
152 }
153 }
154 }
155
156 Some(total_blocks)
157 }
158}
159
160impl Problem for ConsecutiveBlockMinimization {
161 const NAME: &'static str = "ConsecutiveBlockMinimization";
162 type Value = crate::types::Or;
163
164 fn dims(&self) -> Vec<usize> {
165 vec![self.num_cols; self.num_cols]
166 }
167
168 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
169 crate::types::Or({
170 match self.count_consecutive_blocks(config) {
171 Some(total) => (total as i64) <= self.bound,
172 None => false,
173 }
174 })
175 }
176
177 fn variant() -> Vec<(&'static str, &'static str)> {
178 crate::variant_params![]
179 }
180
181 fn num_variables(&self) -> usize {
182 self.num_cols
183 }
184}
185
186crate::declare_variants! {
187 default ConsecutiveBlockMinimization => "factorial(num_cols) * num_rows * num_cols",
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191struct ConsecutiveBlockMinimizationDef {
192 matrix: Vec<Vec<bool>>,
193 bound: i64,
194}
195
196impl TryFrom<ConsecutiveBlockMinimizationDef> for ConsecutiveBlockMinimization {
197 type Error = String;
198
199 fn try_from(value: ConsecutiveBlockMinimizationDef) -> Result<Self, Self::Error> {
200 Self::try_new(value.matrix, value.bound)
201 }
202}
203
204impl From<ConsecutiveBlockMinimization> for ConsecutiveBlockMinimizationDef {
205 fn from(value: ConsecutiveBlockMinimization) -> Self {
206 Self {
207 matrix: value.matrix,
208 bound: value.bound,
209 }
210 }
211}
212
213fn validate_matrix_dimensions(matrix: &[Vec<bool>]) -> Result<(usize, usize), String> {
214 let num_rows = matrix.len();
215 let num_cols = matrix.first().map_or(0, Vec::len);
216
217 if matrix.iter().any(|row| row.len() != num_cols) {
218 return Err("all matrix rows must have the same length".to_string());
219 }
220
221 Ok((num_rows, num_cols))
222}
223
224#[cfg(feature = "example-db")]
225pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
226 vec![crate::example_db::specs::ModelExampleSpec {
229 id: "consecutive_block_minimization",
230 instance: Box::new(ConsecutiveBlockMinimization::new(
231 vec![
232 vec![false, true, false, false, false, false],
233 vec![true, false, true, false, false, false],
234 vec![false, true, false, true, false, false],
235 vec![false, false, true, false, true, false],
236 vec![false, false, false, true, false, true],
237 vec![false, false, false, false, true, false],
238 ],
239 6,
240 )),
241 optimal_config: vec![0, 2, 4, 1, 3, 5],
242 optimal_value: serde_json::json!(true),
243 }]
244}
245
246#[cfg(test)]
247#[path = "../../unit_tests/models/algebraic/consecutive_block_minimization.rs"]
248mod tests;