problemreductions/rules/
paintshop_qubo.rs1use crate::models::algebraic::QUBO;
11use crate::models::misc::PaintShop;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14
15#[derive(Debug, Clone)]
17pub struct ReductionPaintShopToQUBO {
18 target: QUBO<i64>,
19}
20
21impl ReductionResult for ReductionPaintShopToQUBO {
22 type Source = PaintShop;
23 type Target = QUBO<i64>;
24
25 fn target_problem(&self) -> &Self::Target {
26 &self.target
27 }
28
29 fn extract_solution(
32 &self,
33 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
34 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
35 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
36
37 Ok(target_solution.to_vec())
38 }
39}
40
41#[reduction(transform = {
42 exact {
43 num_vars = "num_cars",
44 },
45 upper_bound {
46 num_quadratic_terms = "num_cars * (num_cars - 1) / 2",
47 },
48})]
49impl ReduceTo<QUBO<i64>> for PaintShop {
50 type Result = ReductionPaintShopToQUBO;
51
52 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
53 let n = self.num_cars();
54 let seq = self.sequence_indices();
55 let is_first = self.is_first();
56 let seq_len = seq.len();
57
58 let mut matrix = vec![vec![0i64; n]; n];
59 let overflow = |operation| {
60 crate::rules::ReductionError::integer_overflow::<PaintShop, QUBO<i64>>(operation)
61 };
62
63 for pos in 0..seq_len.saturating_sub(1) {
65 let a = seq[pos];
66 let b = seq[pos + 1];
67
68 if a == b {
70 continue;
71 }
72
73 let parity_a = is_first[pos];
74 let parity_b = is_first[pos + 1];
75
76 let (lo, hi) = if a < b { (a, b) } else { (b, a) };
78
79 if parity_a == parity_b {
80 matrix[a][a] = matrix[a][a]
83 .checked_add(1)
84 .ok_or_else(|| overflow("adding a PaintShop diagonal coefficient"))?;
85 matrix[b][b] = matrix[b][b]
86 .checked_add(1)
87 .ok_or_else(|| overflow("adding a PaintShop diagonal coefficient"))?;
88 matrix[lo][hi] = matrix[lo][hi]
89 .checked_sub(2)
90 .ok_or_else(|| overflow("adding a PaintShop interaction coefficient"))?;
91 } else {
92 matrix[a][a] = matrix[a][a]
95 .checked_sub(1)
96 .ok_or_else(|| overflow("adding a PaintShop diagonal coefficient"))?;
97 matrix[b][b] = matrix[b][b]
98 .checked_sub(1)
99 .ok_or_else(|| overflow("adding a PaintShop diagonal coefficient"))?;
100 matrix[lo][hi] = matrix[lo][hi]
101 .checked_add(2)
102 .ok_or_else(|| overflow("adding a PaintShop interaction coefficient"))?;
103 }
104 }
105
106 Ok(ReductionPaintShopToQUBO {
107 target: QUBO::from_matrix(matrix).map_err(|message| {
108 crate::rules::ReductionError::construction::<PaintShop, QUBO<i64>>(message)
109 })?,
110 })
111 }
112}
113
114#[cfg(feature = "example-db")]
115pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
116 use crate::export::SolutionPair;
117
118 vec![crate::example_db::specs::RuleExampleSpec {
119 id: "paintshop_to_qubo",
120 build: || {
121 let source = PaintShop::new(vec!["A", "B", "C", "A", "D", "B", "D", "C"]);
123 crate::example_db::specs::rule_example_with_witness::<_, QUBO<i64>>(
124 source,
125 SolutionPair {
126 source_config: serde_json::json!(vec![true, false, false, false]),
127 target_config: serde_json::json!(vec![true, false, false, false]),
128 },
129 )
130 },
131 }]
132}
133
134#[cfg(test)]
135#[path = "../unit_tests/rules/paintshop_qubo.rs"]
136mod tests;