problemreductions/rules/
minimumweightdecoding_ilp.rs1use crate::models::algebraic::MinimumWeightDecoding;
19use crate::models::algebraic::{Bounded, IntegerVariable, LinearConstraint, ObjectiveSense, ILP};
20use crate::reduction;
21use crate::rules::traits::{ReduceTo, ReductionResult};
22
23#[derive(Debug, Clone)]
29pub struct ReductionMinimumWeightDecodingToILP {
30 target: ILP<i64, i64, Bounded>,
31 num_cols: usize,
32}
33
34impl ReductionResult for ReductionMinimumWeightDecodingToILP {
35 type Source = MinimumWeightDecoding;
36 type Target = ILP<i64, i64, Bounded>;
37
38 fn target_problem(&self) -> &ILP<i64, i64, Bounded> {
39 &self.target
40 }
41
42 fn extract_solution(
44 &self,
45 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
46 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
47 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
48
49 Ok(target_solution[..self.num_cols]
50 .iter()
51 .map(|&value| value == 1)
52 .collect())
53 }
54}
55
56#[reduction(transform = {
57 exact {
58 num_vars = "num_cols + num_rows",
59 num_constraints = "num_rows + num_cols",
60 },
61 upper_bound {
62 max_constraint_magnitude_bits = "num_cols + 2",
63 num_nonzeros = "(num_cols + num_rows) * (num_rows + num_cols)",
64 },
65})]
66impl ReduceTo<ILP<i64, i64, Bounded>> for MinimumWeightDecoding {
67 type Result = ReductionMinimumWeightDecodingToILP;
68
69 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
70 let m = self.num_cols();
71 let n = self.num_rows();
72 let num_vars = m + n;
73
74 let x = |j: usize| j; let k = |i: usize| m + i; let mut constraints = Vec::new();
78
79 for i in 0..n {
81 let mut terms: Vec<(usize, i64)> = Vec::new();
82 for j in 0..m {
83 if self.matrix()[i][j] {
84 terms.push((x(j), 1));
85 }
86 }
87 terms.push((k(i), -2));
88 let rhs = if self.target()[i] { 1 } else { 0 };
89 constraints.push(LinearConstraint::eq(terms, rhs));
90 }
91
92 for j in 0..m {
94 constraints.push(LinearConstraint::le(vec![(x(j), 1)], 1));
95 }
96
97 let objective: Vec<(usize, i64)> = (0..m).map(|j| (x(j), 1)).collect();
99
100 let mut variables = vec![IntegerVariable::binary(); num_vars];
101 variables[m..].fill(
102 IntegerVariable::new(
103 Some(0),
104 Some(Self::exact_i64(m / 2, "bounding parity quotients")?),
105 )
106 .map_err(Self::target_construction)?,
107 );
108
109 Ok(ReductionMinimumWeightDecodingToILP {
110 target: ILP::with_variables(
111 variables,
112 constraints,
113 objective,
114 ObjectiveSense::Minimize,
115 )
116 .map_err(Self::target_construction)?,
117 num_cols: m,
118 })
119 }
120}
121
122#[cfg(feature = "example-db")]
123pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
124 vec![crate::example_db::specs::RuleExampleSpec {
125 id: "minimumweightdecoding_to_ilp",
126 build: || {
127 let source = MinimumWeightDecoding::new(
128 vec![
129 vec![true, false, true, true],
130 vec![false, true, true, false],
131 vec![true, true, false, true],
132 ],
133 vec![true, true, false],
134 );
135 crate::example_db::specs::rule_example_via_bounded_ilp::<_>(source)
136 },
137 }]
138}
139
140#[cfg(test)]
141#[path = "../unit_tests/rules/minimumweightdecoding_ilp.rs"]
142mod tests;