problemreductions/rules/
paintshop_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
8use crate::models::misc::PaintShop;
9use crate::reduction;
10use crate::rules::traits::{ReduceTo, ReductionResult};
11
12#[derive(Debug, Clone)]
13pub struct ReductionPaintShopToILP {
14 target: ILP<bool>,
15 num_cars: usize,
16}
17
18impl ReductionResult for ReductionPaintShopToILP {
19 type Source = PaintShop;
20 type Target = ILP<bool>;
21
22 fn target_problem(&self) -> &ILP<bool> {
23 &self.target
24 }
25
26 fn extract_solution(
28 &self,
29 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
30 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
31 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
32
33 Ok(target_solution[..self.num_cars]
34 .iter()
35 .map(|&value| value == 1)
36 .collect())
37 }
38}
39
40#[reduction(
41 transform = upper_bound {
42 num_vars = "num_cars + 2 * num_sequence",
43 num_constraints = "num_sequence + 2 * num_sequence",
44 },
45 unavailable = {
46 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
47 }
48)]
49impl ReduceTo<ILP<bool>> for PaintShop {
50 type Result = ReductionPaintShopToILP;
51
52 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
53 let nc = self.num_cars();
54 let seq_len = self.sequence_len();
55
56 let k_offset = nc;
61 let c_offset = nc + seq_len;
62 let num_vars = nc + 2 * seq_len;
63
64 let mut constraints = Vec::new();
65
66 for (position, (&car, &is_first)) in self
67 .sequence_indices()
68 .iter()
69 .zip(self.is_first())
70 .enumerate()
71 {
72 if is_first {
73 constraints.push(LinearConstraint::eq(
75 vec![(k_offset + position, 1), (car, -1)],
76 0,
77 ));
78 } else {
79 constraints.push(LinearConstraint::eq(
81 vec![(k_offset + position, 1), (car, 1)],
82 1,
83 ));
84 }
85 }
86
87 for p in 1..seq_len {
89 constraints.push(LinearConstraint::ge(
91 vec![(c_offset + p, 1), (k_offset + p, -1), (k_offset + p - 1, 1)],
92 0,
93 ));
94 constraints.push(LinearConstraint::ge(
96 vec![(c_offset + p, 1), (k_offset + p - 1, -1), (k_offset + p, 1)],
97 0,
98 ));
99 }
100
101 let objective: Vec<(usize, i64)> = (1..seq_len).map(|p| (c_offset + p, 1)).collect();
103
104 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize)
105 .map_err(<Self as ReduceTo<ILP<bool>>>::target_construction)?;
106 Ok(ReductionPaintShopToILP {
107 target,
108 num_cars: nc,
109 })
110 }
111}
112
113#[cfg(feature = "example-db")]
114pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
115 use crate::export::SolutionPair;
116 vec![crate::example_db::specs::RuleExampleSpec {
117 id: "paintshop_to_ilp",
118 build: || {
119 let source = PaintShop::new(vec!["A", "B", "A", "C", "B", "C"]);
121 let reduction: ReductionPaintShopToILP =
122 ReduceTo::<ILP<bool>>::reduce_to(&source).expect("reduction should succeed");
123 let target_config = {
124 let ilp_solver = crate::solvers::ILPSolver::new();
125 ilp_solver
126 .solve(reduction.target_problem())
127 .expect("ILP should be solvable")
128 };
129 let source_config = reduction.extract_solution(&target_config).unwrap();
130 crate::example_db::specs::rule_example_with_witness::<_, ILP<bool>>(
131 source,
132 SolutionPair {
133 source_config: serde_json::to_value(source_config)
134 .expect("solution serialization must succeed"),
135 target_config: serde_json::to_value(target_config)
136 .expect("solution serialization must succeed"),
137 },
138 )
139 },
140 }]
141}
142
143#[cfg(test)]
144#[path = "../unit_tests/rules/paintshop_ilp.rs"]
145mod tests;