problemreductions/models/algebraic/
consecutive_ones_submatrix.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
10use crate::traits::Problem;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "ConsecutiveOnesSubmatrix",
16 display_name: "Consecutive Ones Submatrix",
17 aliases: &[],
18 dimensions: &[],
19 category: crate::registry::ProblemCategory::Algebraic,
20 module_path: module_path!(),
21 description: "Find K columns of a binary matrix that can be permuted to have the consecutive ones property",
22 fields: &[
23 FieldInfo { name: "matrix", type_name: "Vec<Vec<bool>>", description: "m×n binary matrix A" },
24 FieldInfo { name: "bound", type_name: "i64", description: "Required number of columns K" },
25 ],
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(try_from = "ConsecutiveOnesSubmatrixData")]
63pub struct ConsecutiveOnesSubmatrix {
64 matrix: Vec<Vec<bool>>,
65 bound: i64,
66}
67
68#[derive(Deserialize)]
69struct ConsecutiveOnesSubmatrixData {
70 matrix: Vec<Vec<bool>>,
71 bound: i64,
72}
73
74impl TryFrom<ConsecutiveOnesSubmatrixData> for ConsecutiveOnesSubmatrix {
75 type Error = crate::registry::ConstructionError;
76
77 fn try_from(data: ConsecutiveOnesSubmatrixData) -> Result<Self, Self::Error> {
78 Self::try_new(data.matrix, data.bound)
79 }
80}
81
82impl ConsecutiveOnesSubmatrix {
83 pub fn new(matrix: Vec<Vec<bool>>, bound: i64) -> Self {
89 Self::try_new(matrix, bound).unwrap_or_else(|error| panic!("{error}"))
90 }
91
92 fn try_new(
93 matrix: Vec<Vec<bool>>,
94 bound: i64,
95 ) -> Result<Self, crate::registry::ConstructionError> {
96 let n = matrix.first().map_or(0, Vec::len);
97 if matrix.iter().any(|row| row.len() != n) {
98 return Err("all matrix rows must have the same length".into());
99 }
100 if !(bound < 0 || usize::try_from(bound).is_ok_and(|bound| bound <= n)) {
101 return Err(format!("bound ({bound}) must be <= number of columns ({n})").into());
102 }
103 Ok(Self { matrix, bound })
104 }
105
106 pub fn matrix(&self) -> &[Vec<bool>] {
108 &self.matrix
109 }
110
111 pub fn bound(&self) -> i64 {
113 self.bound
114 }
115
116 pub fn num_rows(&self) -> usize {
118 self.matrix.len()
119 }
120
121 pub fn num_cols(&self) -> usize {
123 if self.matrix.is_empty() {
124 0
125 } else {
126 self.matrix[0].len()
127 }
128 }
129
130 fn has_c1p(&self, col_order: &[usize]) -> bool {
134 for row in &self.matrix {
135 let mut first_one = None;
136 let mut last_one = None;
137 let mut count_ones = 0;
138 for (pos, &col_idx) in col_order.iter().enumerate() {
139 if row[col_idx] {
140 if first_one.is_none() {
141 first_one = Some(pos);
142 }
143 last_one = Some(pos);
144 count_ones += 1;
145 }
146 }
147 if count_ones > 0 {
149 let span = last_one.unwrap() - first_one.unwrap() + 1;
150 if span != count_ones {
151 return false;
152 }
153 }
154 }
155 true
156 }
157
158 fn any_permutation_has_c1p(&self, cols: &[usize]) -> bool {
160 let k = cols.len();
161 if k == 0 {
162 return true;
163 }
164 let mut perm: Vec<usize> = cols.to_vec();
165 let mut c = vec![0usize; k];
167 if self.has_c1p(&perm) {
168 return true;
169 }
170 let mut i = 0;
171 while i < k {
172 if c[i] < i {
173 if i % 2 == 0 {
174 perm.swap(0, i);
175 } else {
176 perm.swap(c[i], i);
177 }
178 if self.has_c1p(&perm) {
179 return true;
180 }
181 c[i] += 1;
182 i = 0;
183 } else {
184 c[i] = 0;
185 i += 1;
186 }
187 }
188 false
189 }
190}
191
192impl Problem for ConsecutiveOnesSubmatrix {
193 const NAME: &'static str = "ConsecutiveOnesSubmatrix";
194 type Solution = Vec<bool>;
195 type Value = crate::types::Or;
196
197 crate::problem_parameters![
198 ("bound", bound),
199 ("num_cols", num_cols),
200 ("num_rows", num_rows),
201 ];
202
203 fn variant() -> Vec<(&'static str, &'static str)> {
204 crate::variant_params![]
205 }
206
207 fn evaluate(
208 &self,
209 config: &Self::Solution,
210 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
211 Ok({
212 crate::types::Or({
213 if config.len() != self.num_cols() {
214 return Err(crate::traits::EvaluationError::InvalidConfiguration(
215 "column-selection length does not match the matrix".into(),
216 ));
217 }
218 let selected: Vec<usize> = config
220 .iter()
221 .enumerate()
222 .filter(|(_, &v)| v)
223 .map(|(i, _)| i)
224 .collect();
225 if usize::try_from(self.bound) != Ok(selected.len()) {
226 return Ok(crate::types::Or(false));
227 }
228 self.any_permutation_has_c1p(&selected)
229 })
230 })
231 }
232}
233
234impl crate::solvers::BruteForceProblem for ConsecutiveOnesSubmatrix {
235 fn dimensions(&self) -> Vec<usize> {
236 vec![2; self.num_cols()]
237 }
238}
239
240crate::declare_variants! {
241 default ConsecutiveOnesSubmatrix => "2^(num_cols) * (num_rows + num_cols)",
242}
243
244crate::register_brute_force! {
245 ConsecutiveOnesSubmatrix decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
246}
247
248#[cfg(feature = "example-db")]
249pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
250 vec![crate::example_db::specs::ModelExampleSpec {
251 id: "consecutive_ones_submatrix",
252 instance: Box::new(ConsecutiveOnesSubmatrix::new(
255 vec![
256 vec![true, true, false, true],
257 vec![true, false, true, true],
258 vec![false, true, true, false],
259 ],
260 3,
261 )),
262 optimal_config: serde_json::json!(vec![true, true, false, true]),
263 optimal_value: serde_json::json!(true),
264 }]
265}
266
267#[cfg(test)]
268#[path = "../../unit_tests/models/algebraic/consecutive_ones_submatrix.rs"]
269mod tests;