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)]
55pub struct MinimumWeightDecoding {
56 matrix: Vec<Vec<bool>>,
58 target: Vec<bool>,
60}
61
62#[derive(Debug, Deserialize, crate::CreateSpec)]
63struct MinimumWeightDecodingCreateSpec {
64 #[create(codec = "json")]
66 matrix: Vec<Vec<bool>>,
67 #[create(name = "rhs", codec = "comma-separated")]
69 target: Vec<bool>,
70}
71
72impl TryFrom<MinimumWeightDecodingCreateSpec> for MinimumWeightDecoding {
73 type Error = crate::registry::ConstructionError;
74 fn try_from(spec: MinimumWeightDecodingCreateSpec) -> Result<Self, Self::Error> {
75 let first = spec
76 .matrix
77 .first()
78 .ok_or("matrix must have at least one row")?;
79 if first.is_empty() {
80 return Err("matrix must have at least one column".into());
81 }
82 if spec.matrix.iter().any(|row| row.len() != first.len()) {
83 return Err("all matrix rows must have the same length".into());
84 }
85 if spec.target.len() != spec.matrix.len() {
86 return Err("rhs length must equal number of rows".into());
87 }
88 Ok(Self {
89 matrix: spec.matrix,
90 target: spec.target,
91 })
92 }
93}
94
95impl MinimumWeightDecoding {
96 pub fn new(matrix: Vec<Vec<bool>>, target: Vec<bool>) -> Self {
103 assert!(!matrix.is_empty(), "Matrix must have at least one row");
104 let num_cols = matrix[0].len();
105 assert!(num_cols > 0, "Matrix must have at least one column");
106 for row in &matrix {
107 assert_eq!(row.len(), num_cols, "All rows must have the same length");
108 }
109 assert_eq!(
110 target.len(),
111 matrix.len(),
112 "Target length must equal number of rows"
113 );
114 Self { matrix, target }
115 }
116
117 pub fn matrix(&self) -> &[Vec<bool>] {
119 &self.matrix
120 }
121
122 pub fn target(&self) -> &[bool] {
124 &self.target
125 }
126
127 pub fn num_rows(&self) -> usize {
129 self.matrix.len()
130 }
131
132 pub fn num_cols(&self) -> usize {
134 self.matrix[0].len()
135 }
136}
137
138impl Problem for MinimumWeightDecoding {
139 const NAME: &'static str = "MinimumWeightDecoding";
140 type Solution = Vec<bool>;
141 type Value = Min<i64>;
142
143 crate::problem_parameters![("num_cols", num_cols), ("num_rows", num_rows),];
144
145 fn variant() -> Vec<(&'static str, &'static str)> {
146 crate::variant_params![]
147 }
148
149 fn evaluate(
150 &self,
151 config: &Self::Solution,
152 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
153 Ok({
154 if config.len() != self.num_cols() {
155 return Err(crate::traits::EvaluationError::InvalidConfiguration(
156 "decoded-word length does not match the matrix columns".into(),
157 ));
158 }
159 for (i, row) in self.matrix.iter().enumerate() {
161 let dot: usize = row
162 .iter()
163 .zip(config.iter())
164 .filter(|(&h, &x)| h && x)
165 .count();
166 let syndrome_bit = dot % 2 == 1;
167 if syndrome_bit != self.target[i] {
168 return Ok(Min(None));
169 }
170 }
171
172 let weight: usize = config.iter().filter(|&&v| v).count();
174 Min(Some(i64::try_from(weight).map_err(|_| {
175 crate::traits::EvaluationError::IntegerOverflow(
176 "converting Hamming weight to i64".into(),
177 )
178 })?))
179 })
180 }
181}
182
183impl crate::solvers::BruteForceProblem for MinimumWeightDecoding {
184 fn dimensions(&self) -> Vec<usize> {
185 vec![2; self.num_cols()]
186 }
187}
188
189crate::declare_variants! {
190 default MinimumWeightDecoding => "2^(0.0494 * num_cols)" create MinimumWeightDecodingCreateSpec,
191}
192
193crate::register_brute_force! {
194 MinimumWeightDecoding decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
195}
196
197#[cfg(feature = "example-db")]
198pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
199 let matrix = vec![
202 vec![true, false, true, true],
203 vec![false, true, true, false],
204 vec![true, true, false, true],
205 ];
206 let target = vec![true, true, false];
207 vec![crate::example_db::specs::ModelExampleSpec {
208 id: "minimum_weight_decoding",
209 instance: Box::new(MinimumWeightDecoding::new(matrix, target)),
210 optimal_config: serde_json::json!(vec![false, false, true, false]),
211 optimal_value: serde_json::json!(1),
212 }]
213}
214
215#[cfg(test)]
216#[path = "../../unit_tests/models/algebraic/minimum_weight_decoding.rs"]
217mod tests;