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 category: crate::registry::ProblemCategory::Algebraic,
18 module_path: module_path!(),
19 description: "Find minimum subset of 1-entries in a binary matrix that dominates all other 1-entries by shared row or column",
20 fields: &[
21 FieldInfo { name: "matrix", type_name: "Vec<Vec<bool>>", description: "n×n binary matrix M" },
22 ],
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
57#[serde(try_from = "MinimumMatrixDominationData")]
58pub struct MinimumMatrixDomination {
59 matrix: Vec<Vec<bool>>,
61 ones: Vec<(usize, usize)>,
63}
64
65#[derive(Deserialize)]
66struct MinimumMatrixDominationData {
67 matrix: Vec<Vec<bool>>,
68}
69
70impl TryFrom<MinimumMatrixDominationData> for MinimumMatrixDomination {
71 type Error = crate::registry::ConstructionError;
72
73 fn try_from(data: MinimumMatrixDominationData) -> Result<Self, Self::Error> {
74 Self::try_new(data.matrix)
75 }
76}
77
78impl MinimumMatrixDomination {
79 pub fn new(matrix: Vec<Vec<bool>>) -> Self {
85 Self::try_new(matrix).unwrap_or_else(|error| panic!("{error}"))
86 }
87
88 fn try_new(matrix: Vec<Vec<bool>>) -> Result<Self, crate::registry::ConstructionError> {
89 let num_cols = matrix.first().map_or(0, Vec::len);
90 if matrix.iter().any(|row| row.len() != num_cols) {
91 return Err("all matrix rows must have the same length".into());
92 }
93 let ones = matrix
94 .iter()
95 .enumerate()
96 .flat_map(|(i, row)| {
97 row.iter()
98 .enumerate()
99 .filter(|(_, &value)| value)
100 .map(move |(j, _)| (i, j))
101 })
102 .collect();
103 Ok(Self { matrix, ones })
104 }
105
106 pub fn matrix(&self) -> &[Vec<bool>] {
108 &self.matrix
109 }
110
111 pub fn ones(&self) -> &[(usize, usize)] {
113 &self.ones
114 }
115
116 pub fn num_rows(&self) -> usize {
118 self.matrix.len()
119 }
120
121 pub fn num_cols(&self) -> usize {
123 self.matrix.first().map_or(0, Vec::len)
124 }
125
126 pub fn num_ones(&self) -> usize {
128 self.ones.len()
129 }
130}
131
132impl Problem for MinimumMatrixDomination {
133 const NAME: &'static str = "MinimumMatrixDomination";
134 type Solution = Vec<bool>;
135 type Value = Min<i64>;
136
137 crate::problem_parameters![
138 ("num_cols", num_cols),
139 ("num_ones", num_ones),
140 ("num_rows", num_rows),
141 ];
142
143 fn variant() -> Vec<(&'static str, &'static str)> {
144 crate::variant_params![]
145 }
146
147 fn evaluate(
148 &self,
149 config: &Self::Solution,
150 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
151 Ok({
152 if config.len() != self.num_ones() {
153 return Err(crate::traits::EvaluationError::InvalidConfiguration(
154 "selected-entry vector length does not match the matrix".into(),
155 ));
156 }
157 let selected: Vec<usize> = config
159 .iter()
160 .enumerate()
161 .filter(|(_, &v)| v)
162 .map(|(i, _)| i)
163 .collect();
164
165 let mut covered_rows = std::collections::HashSet::new();
167 let mut covered_cols = std::collections::HashSet::new();
168 for &idx in &selected {
169 let (r, c) = self.ones[idx];
170 covered_rows.insert(r);
171 covered_cols.insert(c);
172 }
173
174 for (k, &(r, c)) in self.ones.iter().enumerate() {
177 if config[k] {
178 continue; }
180 if !covered_rows.contains(&r) && !covered_cols.contains(&c) {
181 return Ok(Min(None)); }
183 }
184
185 Min(Some(i64::try_from(selected.len()).map_err(|_| {
186 crate::traits::EvaluationError::IntegerOverflow(
187 "converting matrix-domination cardinality to i64".into(),
188 )
189 })?))
190 })
191 }
192}
193
194impl crate::solvers::BruteForceProblem for MinimumMatrixDomination {
195 fn dimensions(&self) -> Vec<usize> {
196 vec![2; self.num_ones()]
197 }
198}
199
200crate::declare_variants! {
201 default MinimumMatrixDomination => "2^num_ones",
202}
203
204crate::register_brute_force! {
205 MinimumMatrixDomination decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
206}
207
208#[cfg(feature = "example-db")]
209pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
210 let matrix = vec![
214 vec![false, true, false, false, false, false],
215 vec![true, false, true, false, false, false],
216 vec![false, true, false, true, false, false],
217 vec![false, false, true, false, true, false],
218 vec![false, false, false, true, false, true],
219 vec![false, false, false, false, true, false],
220 ];
221 vec![crate::example_db::specs::ModelExampleSpec {
222 id: "minimum_matrix_domination",
223 instance: Box::new(MinimumMatrixDomination::new(matrix)),
224 optimal_config: serde_json::json!(vec![
225 true, true, false, false, false, false, true, true, false, false
226 ]),
227 optimal_value: serde_json::json!(4),
228 }]
229}
230
231#[cfg(test)]
232#[path = "../../unit_tests/models/algebraic/minimum_matrix_domination.rs"]
233mod tests;