problemreductions/rules/
rectilinearpicturecompression_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
7use crate::models::misc::RectilinearPictureCompression;
8use crate::reduction;
9use crate::rules::traits::{ReduceTo, ReductionResult};
10
11#[derive(Debug, Clone)]
12pub struct ReductionRPCToILP {
13 target: ILP<bool>,
14}
15
16impl ReductionResult for ReductionRPCToILP {
17 type Source = RectilinearPictureCompression;
18 type Target = ILP<bool>;
19
20 fn target_problem(&self) -> &ILP<bool> {
21 &self.target
22 }
23
24 fn extract_solution(
25 &self,
26 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
27 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
28 crate::rules::traits::validate_target_witness(
29 self.target_problem(),
30 target_solution,
31 |value| value.value.is_some(),
32 "target ILP assignment is infeasible",
33 )?;
34
35 Ok(target_solution.iter().map(|&value| value == 1).collect())
36 }
37}
38
39#[crate::aggregate_reduction(ilp_feasibility)]
40impl crate::rules::AggregateReductionResult for ReductionRPCToILP {}
41
42#[reduction(
48 transform = {
49 exact {
50 num_vars = "num_rectangles",
51 num_nonzeros = "total_rectangle_area + num_rectangles",
52 },
53 upper_bound {
54 max_constraint_magnitude_bits = "num_rows + num_cols",
55 num_constraints = "num_rows * num_cols + 1",
56 },
57 },
58)]
59impl ReduceTo<ILP<bool>> for RectilinearPictureCompression {
60 type Result = ReductionRPCToILP;
61
62 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
63 let rects = self.maximal_rectangles();
64 let num_vars = rects.len();
65 let mut constraints = Vec::new();
66
67 for i in 0..self.num_rows() {
69 for j in 0..self.num_cols() {
70 if self.matrix()[i][j] {
71 let terms: Vec<(usize, i64)> = rects
72 .iter()
73 .enumerate()
74 .filter(|(_, &(r1, c1, r2, c2))| i >= r1 && i <= r2 && j >= c1 && j <= c2)
75 .map(|(idx, _)| (idx, 1))
76 .collect();
77 constraints.push(LinearConstraint::ge(terms, 1));
78 }
79 }
80 }
81
82 let bound_terms: Vec<(usize, i64)> = (0..num_vars).map(|i| (i, 1)).collect();
84 let max_rectangles = Self::exact_i64(num_vars, "encoding the rectangle-count bound")?;
85 constraints.push(LinearConstraint::le(
86 bound_terms,
87 self.bound().clamp(-1, max_rectangles),
88 ));
89
90 let target = ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize)
91 .map_err(Self::target_construction)?;
92 Ok(ReductionRPCToILP { target })
93 }
94}
95
96#[cfg(feature = "example-db")]
97pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
98 vec![crate::example_db::specs::RuleExampleSpec {
99 id: "rectilinearpicturecompression_to_ilp",
100 build: || {
101 let source =
102 RectilinearPictureCompression::new(vec![vec![true, true], vec![true, true]], 1);
103 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
104 },
105 }]
106}
107
108#[cfg(test)]
109#[path = "../unit_tests/rules/rectilinearpicturecompression_ilp.rs"]
110mod tests;