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 module_path: module_path!(),
20 description: "Find K columns of a binary matrix that can be permuted to have the consecutive ones property",
21 fields: &[
22 FieldInfo { name: "matrix", type_name: "Vec<Vec<bool>>", description: "m×n binary matrix A" },
23 FieldInfo { name: "bound", type_name: "i64", description: "Required number of columns K" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct ConsecutiveOnesSubmatrix {
62 matrix: Vec<Vec<bool>>,
63 bound: i64,
64}
65
66impl ConsecutiveOnesSubmatrix {
67 pub fn new(matrix: Vec<Vec<bool>>, bound: i64) -> Self {
73 let n = if matrix.is_empty() {
74 0
75 } else {
76 matrix[0].len()
77 };
78 for row in &matrix {
79 assert_eq!(row.len(), n, "All rows must have the same length");
80 }
81 assert!(
82 bound <= n as i64,
83 "bound ({bound}) must be <= number of columns ({n})"
84 );
85 Self { matrix, bound }
86 }
87
88 pub fn matrix(&self) -> &[Vec<bool>] {
90 &self.matrix
91 }
92
93 pub fn bound(&self) -> i64 {
95 self.bound
96 }
97
98 pub fn num_rows(&self) -> usize {
100 self.matrix.len()
101 }
102
103 pub fn num_cols(&self) -> usize {
105 if self.matrix.is_empty() {
106 0
107 } else {
108 self.matrix[0].len()
109 }
110 }
111
112 fn has_c1p(&self, col_order: &[usize]) -> bool {
116 for row in &self.matrix {
117 let mut first_one = None;
118 let mut last_one = None;
119 let mut count_ones = 0;
120 for (pos, &col_idx) in col_order.iter().enumerate() {
121 if row[col_idx] {
122 if first_one.is_none() {
123 first_one = Some(pos);
124 }
125 last_one = Some(pos);
126 count_ones += 1;
127 }
128 }
129 if count_ones > 0 {
131 let span = last_one.unwrap() - first_one.unwrap() + 1;
132 if span != count_ones {
133 return false;
134 }
135 }
136 }
137 true
138 }
139
140 fn any_permutation_has_c1p(&self, cols: &[usize]) -> bool {
142 let k = cols.len();
143 if k == 0 {
144 return true;
145 }
146 let mut perm: Vec<usize> = cols.to_vec();
147 let mut c = vec![0usize; k];
149 if self.has_c1p(&perm) {
150 return true;
151 }
152 let mut i = 0;
153 while i < k {
154 if c[i] < i {
155 if i % 2 == 0 {
156 perm.swap(0, i);
157 } else {
158 perm.swap(c[i], i);
159 }
160 if self.has_c1p(&perm) {
161 return true;
162 }
163 c[i] += 1;
164 i = 0;
165 } else {
166 c[i] = 0;
167 i += 1;
168 }
169 }
170 false
171 }
172}
173
174impl Problem for ConsecutiveOnesSubmatrix {
175 const NAME: &'static str = "ConsecutiveOnesSubmatrix";
176 type Value = crate::types::Or;
177
178 fn variant() -> Vec<(&'static str, &'static str)> {
179 crate::variant_params![]
180 }
181
182 fn dims(&self) -> Vec<usize> {
183 vec![2; self.num_cols()]
184 }
185
186 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
187 crate::types::Or({
188 if config.len() != self.num_cols() {
189 return crate::types::Or(false);
190 }
191 if config.iter().any(|&v| v >= 2) {
192 return crate::types::Or(false);
193 }
194 let selected: Vec<usize> = config
196 .iter()
197 .enumerate()
198 .filter(|(_, &v)| v == 1)
199 .map(|(i, _)| i)
200 .collect();
201 if (selected.len() as i64) != self.bound {
202 return crate::types::Or(false);
203 }
204 self.any_permutation_has_c1p(&selected)
205 })
206 }
207}
208
209crate::declare_variants! {
210 default ConsecutiveOnesSubmatrix => "2^(num_cols) * (num_rows + num_cols)",
211}
212
213#[cfg(feature = "example-db")]
214pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
215 vec![crate::example_db::specs::ModelExampleSpec {
216 id: "consecutive_ones_submatrix",
217 instance: Box::new(ConsecutiveOnesSubmatrix::new(
220 vec![
221 vec![true, true, false, true],
222 vec![true, false, true, true],
223 vec![false, true, true, false],
224 ],
225 3,
226 )),
227 optimal_config: vec![1, 1, 0, 1],
228 optimal_value: serde_json::json!(true),
229 }]
230}
231
232#[cfg(test)]
233#[path = "../../unit_tests/models/algebraic/consecutive_ones_submatrix.rs"]
234mod tests;