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)]
62pub struct ConsecutiveOnesSubmatrix {
63 matrix: Vec<Vec<bool>>,
64 bound: i64,
65}
66
67impl ConsecutiveOnesSubmatrix {
68 pub fn new(matrix: Vec<Vec<bool>>, bound: i64) -> Self {
74 let n = if matrix.is_empty() {
75 0
76 } else {
77 matrix[0].len()
78 };
79 for row in &matrix {
80 assert_eq!(row.len(), n, "All rows must have the same length");
81 }
82 assert!(
83 bound < 0 || usize::try_from(bound).is_ok_and(|bound| bound <= n),
84 "bound ({bound}) must be <= number of columns ({n})"
85 );
86 Self { matrix, bound }
87 }
88
89 pub fn matrix(&self) -> &[Vec<bool>] {
91 &self.matrix
92 }
93
94 pub fn bound(&self) -> i64 {
96 self.bound
97 }
98
99 pub fn num_rows(&self) -> usize {
101 self.matrix.len()
102 }
103
104 pub fn num_cols(&self) -> usize {
106 if self.matrix.is_empty() {
107 0
108 } else {
109 self.matrix[0].len()
110 }
111 }
112
113 fn has_c1p(&self, col_order: &[usize]) -> bool {
117 for row in &self.matrix {
118 let mut first_one = None;
119 let mut last_one = None;
120 let mut count_ones = 0;
121 for (pos, &col_idx) in col_order.iter().enumerate() {
122 if row[col_idx] {
123 if first_one.is_none() {
124 first_one = Some(pos);
125 }
126 last_one = Some(pos);
127 count_ones += 1;
128 }
129 }
130 if count_ones > 0 {
132 let span = last_one.unwrap() - first_one.unwrap() + 1;
133 if span != count_ones {
134 return false;
135 }
136 }
137 }
138 true
139 }
140
141 fn any_permutation_has_c1p(&self, cols: &[usize]) -> bool {
143 let k = cols.len();
144 if k == 0 {
145 return true;
146 }
147 let mut perm: Vec<usize> = cols.to_vec();
148 let mut c = vec![0usize; k];
150 if self.has_c1p(&perm) {
151 return true;
152 }
153 let mut i = 0;
154 while i < k {
155 if c[i] < i {
156 if i % 2 == 0 {
157 perm.swap(0, i);
158 } else {
159 perm.swap(c[i], i);
160 }
161 if self.has_c1p(&perm) {
162 return true;
163 }
164 c[i] += 1;
165 i = 0;
166 } else {
167 c[i] = 0;
168 i += 1;
169 }
170 }
171 false
172 }
173}
174
175impl Problem for ConsecutiveOnesSubmatrix {
176 const NAME: &'static str = "ConsecutiveOnesSubmatrix";
177 type Solution = Vec<bool>;
178 type Value = crate::types::Or;
179
180 crate::problem_parameters![
181 ("bound", bound),
182 ("num_cols", num_cols),
183 ("num_rows", num_rows),
184 ];
185
186 fn variant() -> Vec<(&'static str, &'static str)> {
187 crate::variant_params![]
188 }
189
190 fn evaluate(
191 &self,
192 config: &Self::Solution,
193 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
194 Ok({
195 crate::types::Or({
196 if config.len() != self.num_cols() {
197 return Err(crate::traits::EvaluationError::InvalidConfiguration(
198 "column-selection length does not match the matrix".into(),
199 ));
200 }
201 let selected: Vec<usize> = config
203 .iter()
204 .enumerate()
205 .filter(|(_, &v)| v)
206 .map(|(i, _)| i)
207 .collect();
208 if usize::try_from(self.bound) != Ok(selected.len()) {
209 return Ok(crate::types::Or(false));
210 }
211 self.any_permutation_has_c1p(&selected)
212 })
213 })
214 }
215}
216
217impl crate::solvers::BruteForceProblem for ConsecutiveOnesSubmatrix {
218 fn dimensions(&self) -> Vec<usize> {
219 vec![2; self.num_cols()]
220 }
221}
222
223crate::declare_variants! {
224 default ConsecutiveOnesSubmatrix => "2^(num_cols) * (num_rows + num_cols)",
225}
226
227crate::register_brute_force! {
228 ConsecutiveOnesSubmatrix decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
229}
230
231#[cfg(feature = "example-db")]
232pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
233 vec![crate::example_db::specs::ModelExampleSpec {
234 id: "consecutive_ones_submatrix",
235 instance: Box::new(ConsecutiveOnesSubmatrix::new(
238 vec![
239 vec![true, true, false, true],
240 vec![true, false, true, true],
241 vec![false, true, true, false],
242 ],
243 3,
244 )),
245 optimal_config: serde_json::json!(vec![true, true, false, true]),
246 optimal_value: serde_json::json!(true),
247 }]
248}
249
250#[cfg(test)]
251#[path = "../../unit_tests/models/algebraic/consecutive_ones_submatrix.rs"]
252mod tests;