problemreductions/rules/
subsetsum_partition.rs1use crate::models::misc::{Partition, SubsetSum};
4use crate::reduction;
5use crate::rules::traits::{ReduceTo, ReductionResult};
6use num_bigint::BigUint;
7use num_traits::ToPrimitive;
8use std::cmp::Ordering;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11enum PaddingRelation {
12 None,
13 SameSide,
14 OppositeSide,
15}
16
17#[derive(Debug, Clone)]
19pub struct ReductionSubsetSumToPartition {
20 target: Partition,
21 source_len: usize,
22 padding_relation: PaddingRelation,
23}
24
25impl ReductionResult for ReductionSubsetSumToPartition {
26 type Source = SubsetSum;
27 type Target = Partition;
28
29 fn target_problem(&self) -> &Self::Target {
30 &self.target
31 }
32
33 fn extract_solution(
34 &self,
35 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
36 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
37 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
38
39 Ok({
40 let source_bits = &target_solution[..self.source_len];
41
42 match self.padding_relation {
43 PaddingRelation::None => source_bits.to_vec(),
44 PaddingRelation::SameSide => {
45 let padding_is_selected = target_solution[self.source_len];
46 source_bits
47 .iter()
48 .map(|&bit| if padding_is_selected { bit } else { !bit })
49 .collect()
50 }
51 PaddingRelation::OppositeSide => {
52 let padding_is_selected = target_solution[self.source_len];
53 source_bits
54 .iter()
55 .map(|&bit| if padding_is_selected { !bit } else { bit })
56 .collect()
57 }
58 }
59 })
60 }
61}
62
63#[reduction(
64 transform = exact {
65 num_elements = "num_elements + 1",
66 })]
67impl ReduceTo<Partition> for SubsetSum {
68 type Result = ReductionSubsetSumToPartition;
69
70 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
71 let total: BigUint = self.sizes().iter().cloned().sum();
72 let double_target = self.target() * 2u32;
73 let relation = total.cmp(&double_target);
74 let padding_relation = match relation {
75 Ordering::Equal => PaddingRelation::None,
76 Ordering::Greater => PaddingRelation::SameSide,
77 Ordering::Less => PaddingRelation::OppositeSide,
78 };
79
80 let convert = |value: &BigUint| {
81 value.to_i64().ok_or_else(|| {
82 crate::rules::ReductionError::invalid_target::<SubsetSum, Partition>(
83 "a source size or derived padding does not fit the Partition i64 domain",
84 )
85 })
86 };
87 let mut sizes: Vec<i64> = self.sizes().iter().map(convert).collect::<Result<_, _>>()?;
88 match relation {
89 Ordering::Equal => {}
90 Ordering::Greater => sizes.push(convert(&(total - double_target))?),
91 Ordering::Less => sizes.push(convert(&(double_target - total))?),
92 }
93
94 Ok(ReductionSubsetSumToPartition {
95 target: Partition::new(sizes).map_err(|error| {
96 crate::rules::ReductionError::construction::<SubsetSum, Partition>(error)
97 })?,
98 source_len: self.num_elements(),
99 padding_relation,
100 })
101 }
102}
103
104#[cfg(feature = "example-db")]
105pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
106 use crate::export::SolutionPair;
107
108 vec![crate::example_db::specs::RuleExampleSpec {
109 id: "subsetsum_to_partition",
110 build: || {
111 crate::example_db::specs::rule_example_with_witness::<_, Partition>(
112 SubsetSum::new(vec![1u32, 5, 6, 8], 11u32),
113 SolutionPair {
114 source_config: serde_json::json!(vec![false, true, true, false]),
115 target_config: serde_json::json!(vec![false, true, true, false, false]),
116 },
117 )
118 },
119 }]
120}
121
122#[cfg(test)]
123#[path = "../unit_tests/rules/subsetsum_partition.rs"]
124mod tests;