problemreductions/models/algebraic/
minimum_weight_decoding.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry};
8use crate::traits::Problem;
9use crate::types::Min;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "MinimumWeightDecoding",
15 display_name: "Minimum Weight Decoding",
16 aliases: &[],
17 dimensions: &[],
18 category: crate::registry::ProblemCategory::Algebraic,
19 module_path: module_path!(),
20 description: "Find minimum Hamming weight binary vector x such that Hx ≡ s (mod 2)",
21 fields: MinimumWeightDecodingCreateSpec::FIELDS,
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(try_from = "MinimumWeightDecodingCreateSpec")]
56pub struct MinimumWeightDecoding {
57 matrix: Vec<Vec<bool>>,
59 target: Vec<bool>,
61}
62
63#[derive(Debug, Deserialize, crate::CreateSpec)]
64struct MinimumWeightDecodingCreateSpec {
65 #[create(codec = "json")]
67 matrix: Vec<Vec<bool>>,
68 #[create(name = "rhs", codec = "comma-separated")]
70 target: Vec<bool>,
71}
72
73impl TryFrom<MinimumWeightDecodingCreateSpec> for MinimumWeightDecoding {
74 type Error = crate::registry::ConstructionError;
75
76 fn try_from(spec: MinimumWeightDecodingCreateSpec) -> Result<Self, Self::Error> {
77 Self::try_new(spec.matrix, spec.target)
78 }
79}
80
81impl MinimumWeightDecoding {
82 pub fn new(matrix: Vec<Vec<bool>>, target: Vec<bool>) -> Self {
89 Self::try_new(matrix, target).unwrap_or_else(|error| panic!("{error}"))
90 }
91
92 fn try_new(
93 matrix: Vec<Vec<bool>>,
94 target: Vec<bool>,
95 ) -> Result<Self, crate::registry::ConstructionError> {
96 let first = matrix.first().ok_or("matrix must have at least one row")?;
97 if first.is_empty() {
98 return Err("matrix must have at least one column".into());
99 }
100 if matrix.iter().any(|row| row.len() != first.len()) {
101 return Err("all matrix rows must have the same length".into());
102 }
103 if target.len() != matrix.len() {
104 return Err("Target length must equal number of rows".into());
105 }
106 Ok(Self { matrix, target })
107 }
108
109 pub fn matrix(&self) -> &[Vec<bool>] {
111 &self.matrix
112 }
113
114 pub fn target(&self) -> &[bool] {
116 &self.target
117 }
118
119 pub fn num_rows(&self) -> usize {
121 self.matrix.len()
122 }
123
124 pub fn num_cols(&self) -> usize {
126 self.matrix[0].len()
127 }
128}
129
130impl Problem for MinimumWeightDecoding {
131 const NAME: &'static str = "MinimumWeightDecoding";
132 type Solution = Vec<bool>;
133 type Value = Min<i64>;
134
135 crate::problem_parameters![("num_cols", num_cols), ("num_rows", num_rows),];
136
137 fn variant() -> Vec<(&'static str, &'static str)> {
138 crate::variant_params![]
139 }
140
141 fn evaluate(
142 &self,
143 config: &Self::Solution,
144 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
145 Ok({
146 if config.len() != self.num_cols() {
147 return Err(crate::traits::EvaluationError::InvalidConfiguration(
148 "decoded-word length does not match the matrix columns".into(),
149 ));
150 }
151 for (i, row) in self.matrix.iter().enumerate() {
153 let dot: usize = row
154 .iter()
155 .zip(config.iter())
156 .filter(|(&h, &x)| h && x)
157 .count();
158 let syndrome_bit = dot % 2 == 1;
159 if syndrome_bit != self.target[i] {
160 return Ok(Min(None));
161 }
162 }
163
164 let weight: usize = config.iter().filter(|&&v| v).count();
166 Min(Some(i64::try_from(weight).map_err(|_| {
167 crate::traits::EvaluationError::IntegerOverflow(
168 "converting Hamming weight to i64".into(),
169 )
170 })?))
171 })
172 }
173}
174
175impl crate::solvers::BruteForceProblem for MinimumWeightDecoding {
176 fn dimensions(&self) -> Vec<usize> {
177 vec![2; self.num_cols()]
178 }
179}
180
181crate::declare_variants! {
182 default MinimumWeightDecoding => "2^(0.0494 * num_cols)" create MinimumWeightDecodingCreateSpec,
183}
184
185crate::register_brute_force! {
186 MinimumWeightDecoding decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
187}
188
189#[cfg(feature = "example-db")]
190pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
191 let matrix = vec![
194 vec![true, false, true, true],
195 vec![false, true, true, false],
196 vec![true, true, false, true],
197 ];
198 let target = vec![true, true, false];
199 vec![crate::example_db::specs::ModelExampleSpec {
200 id: "minimum_weight_decoding",
201 instance: Box::new(MinimumWeightDecoding::new(matrix, target)),
202 optimal_config: serde_json::json!(vec![false, false, true, false]),
203 optimal_value: serde_json::json!(1),
204 }]
205}
206
207#[cfg(test)]
208#[path = "../../unit_tests/models/algebraic/minimum_weight_decoding.rs"]
209mod tests;