problemreductions/models/algebraic/
minimum_matrix_domination.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::traits::Problem;
8use crate::types::Min;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "MinimumMatrixDomination",
14 display_name: "Minimum Matrix Domination",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Find minimum subset of 1-entries in a binary matrix that dominates all other 1-entries by shared row or column",
19 fields: &[
20 FieldInfo { name: "matrix", type_name: "Vec<Vec<bool>>", description: "n×n binary matrix M" },
21 ],
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct MinimumMatrixDomination {
57 matrix: Vec<Vec<bool>>,
59 ones: Vec<(usize, usize)>,
61}
62
63impl MinimumMatrixDomination {
64 pub fn new(matrix: Vec<Vec<bool>>) -> Self {
70 let num_cols = matrix.first().map_or(0, Vec::len);
71 for row in &matrix {
72 assert_eq!(row.len(), num_cols, "All rows must have the same length");
73 }
74 let ones: Vec<(usize, usize)> = matrix
75 .iter()
76 .enumerate()
77 .flat_map(|(i, row)| {
78 row.iter()
79 .enumerate()
80 .filter(|(_, &v)| v)
81 .map(move |(j, _)| (i, j))
82 })
83 .collect();
84 Self { matrix, ones }
85 }
86
87 pub fn matrix(&self) -> &[Vec<bool>] {
89 &self.matrix
90 }
91
92 pub fn ones(&self) -> &[(usize, usize)] {
94 &self.ones
95 }
96
97 pub fn num_rows(&self) -> usize {
99 self.matrix.len()
100 }
101
102 pub fn num_cols(&self) -> usize {
104 self.matrix.first().map_or(0, Vec::len)
105 }
106
107 pub fn num_ones(&self) -> usize {
109 self.ones.len()
110 }
111}
112
113impl Problem for MinimumMatrixDomination {
114 const NAME: &'static str = "MinimumMatrixDomination";
115 type Value = Min<usize>;
116
117 fn variant() -> Vec<(&'static str, &'static str)> {
118 crate::variant_params![]
119 }
120
121 fn dims(&self) -> Vec<usize> {
122 vec![2; self.num_ones()]
123 }
124
125 fn evaluate(&self, config: &[usize]) -> Min<usize> {
126 if config.len() != self.num_ones() {
127 return Min(None);
128 }
129 if config.iter().any(|&v| v >= 2) {
130 return Min(None);
131 }
132
133 let selected: Vec<usize> = config
135 .iter()
136 .enumerate()
137 .filter(|(_, &v)| v == 1)
138 .map(|(i, _)| i)
139 .collect();
140
141 let mut covered_rows = std::collections::HashSet::new();
143 let mut covered_cols = std::collections::HashSet::new();
144 for &idx in &selected {
145 let (r, c) = self.ones[idx];
146 covered_rows.insert(r);
147 covered_cols.insert(c);
148 }
149
150 for (k, &(r, c)) in self.ones.iter().enumerate() {
153 if config[k] == 1 {
154 continue; }
156 if !covered_rows.contains(&r) && !covered_cols.contains(&c) {
157 return Min(None); }
159 }
160
161 Min(Some(selected.len()))
162 }
163}
164
165crate::declare_variants! {
166 default MinimumMatrixDomination => "2^num_ones",
167}
168
169#[cfg(feature = "example-db")]
170pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
171 let matrix = vec![
175 vec![false, true, false, false, false, false],
176 vec![true, false, true, false, false, false],
177 vec![false, true, false, true, false, false],
178 vec![false, false, true, false, true, false],
179 vec![false, false, false, true, false, true],
180 vec![false, false, false, false, true, false],
181 ];
182 vec![crate::example_db::specs::ModelExampleSpec {
183 id: "minimum_matrix_domination",
184 instance: Box::new(MinimumMatrixDomination::new(matrix)),
185 optimal_config: vec![1, 1, 0, 0, 0, 0, 1, 1, 0, 0],
186 optimal_value: serde_json::json!(4),
187 }]
188}
189
190#[cfg(test)]
191#[path = "../../unit_tests/models/algebraic/minimum_matrix_domination.rs"]
192mod tests;