problemreductions/models/algebraic/
minimum_matrix_cover.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::traits::Problem;
8use crate::types::Min;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "MinimumMatrixCover",
14 display_name: "Minimum Matrix Cover",
15 aliases: &[],
16 dimensions: &[],
17 category: crate::registry::ProblemCategory::Algebraic,
18 module_path: module_path!(),
19 description: "Find sign assignment minimizing quadratic form over nonnegative integer matrix",
20 fields: &[
21 FieldInfo { name: "matrix", type_name: "Vec<Vec<i64>>", description: "n×n nonnegative integer matrix" },
22 ],
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(try_from = "Data")]
55pub struct MinimumMatrixCover {
56 matrix: Vec<Vec<i64>>,
58}
59
60#[derive(Deserialize)]
61struct Data {
62 matrix: Vec<Vec<i64>>,
63}
64
65impl TryFrom<Data> for MinimumMatrixCover {
66 type Error = crate::registry::ConstructionError;
67
68 fn try_from(data: Data) -> Result<Self, Self::Error> {
69 Self::try_new(data.matrix)
70 }
71}
72
73impl MinimumMatrixCover {
74 pub fn new(matrix: Vec<Vec<i64>>) -> Self {
80 Self::try_new(matrix).unwrap_or_else(|error| panic!("{error}"))
81 }
82
83 fn try_new(matrix: Vec<Vec<i64>>) -> Result<Self, crate::registry::ConstructionError> {
84 let n = matrix.len();
85 for (i, row) in matrix.iter().enumerate() {
86 if row.len() != n {
87 return Err(format!(
88 "Matrix must be square: row {i} has {} columns, expected {n}",
89 row.len()
90 )
91 .into());
92 }
93 if row.iter().any(|&entry| entry < 0) {
94 return Err(format!("matrix row {i} contains a negative entry").into());
95 }
96 }
97 Ok(Self { matrix })
98 }
99
100 pub fn num_rows(&self) -> usize {
102 self.matrix.len()
103 }
104
105 pub fn matrix(&self) -> &[Vec<i64>] {
107 &self.matrix
108 }
109}
110
111impl Problem for MinimumMatrixCover {
112 const NAME: &'static str = "MinimumMatrixCover";
113 type Solution = Vec<bool>;
114 type Value = Min<i64>;
115
116 crate::problem_parameters![("num_rows", num_rows),];
117
118 fn variant() -> Vec<(&'static str, &'static str)> {
119 crate::variant_params![]
120 }
121
122 fn evaluate(
123 &self,
124 config: &Self::Solution,
125 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
126 Ok({
127 let n = self.num_rows();
128 if config.len() != n {
129 return Err(crate::traits::EvaluationError::InvalidConfiguration(
130 "row-sign assignment length does not match the matrix".into(),
131 ));
132 }
133 let signs: Vec<i64> = config
135 .iter()
136 .map(|&value| if value { 1 } else { -1 })
137 .collect();
138
139 let mut value: i64 = 0;
141 for i in 0..n {
142 for j in 0..n {
143 let term = self.matrix[i][j]
144 .checked_mul(signs[i])
145 .and_then(|term| term.checked_mul(signs[j]))
146 .ok_or_else(|| {
147 crate::traits::EvaluationError::IntegerOverflow(
148 "multiplying matrix-cover objective term".into(),
149 )
150 })?;
151 value = value.checked_add(term).ok_or_else(|| {
152 crate::traits::EvaluationError::IntegerOverflow(
153 "summing matrix-cover objective".into(),
154 )
155 })?;
156 }
157 }
158
159 Min(Some(value))
160 })
161 }
162}
163
164impl crate::solvers::BruteForceProblem for MinimumMatrixCover {
165 fn dimensions(&self) -> Vec<usize> {
166 vec![2; self.num_rows()]
167 }
168}
169
170crate::declare_variants! {
171 default MinimumMatrixCover => "2^num_rows",
172}
173
174crate::register_brute_force! {
175 MinimumMatrixCover decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
176}
177
178#[cfg(feature = "example-db")]
179pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
180 vec![crate::example_db::specs::ModelExampleSpec {
183 id: "minimum_matrix_cover",
184 instance: Box::new(MinimumMatrixCover::new(vec![
185 vec![0, 3, 1, 0],
186 vec![3, 0, 0, 2],
187 vec![1, 0, 0, 4],
188 vec![0, 2, 4, 0],
189 ])),
190 optimal_config: serde_json::json!(vec![false, true, true, false]),
191 optimal_value: serde_json::json!(-20),
192 }]
193}
194
195#[cfg(test)]
196#[path = "../../unit_tests/models/algebraic/minimum_matrix_cover.rs"]
197mod tests;