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_witness(
38 self.target_problem(),
39 target_solution,
40 |value| value.0,
41 "target witness does not satisfy the target problem",
42 )?;
43
44 Ok({
45 let source_bits = &target_solution[..self.source_len];
46
47 match self.padding_relation {
48 PaddingRelation::None => source_bits.to_vec(),
49 PaddingRelation::SameSide => {
50 let padding_is_selected = target_solution[self.source_len];
51 source_bits
52 .iter()
53 .map(|&bit| if padding_is_selected { bit } else { !bit })
54 .collect()
55 }
56 PaddingRelation::OppositeSide => {
57 let padding_is_selected = target_solution[self.source_len];
58 source_bits
59 .iter()
60 .map(|&bit| if padding_is_selected { !bit } else { bit })
61 .collect()
62 }
63 }
64 })
65 }
66}
67
68#[crate::aggregate_reduction(identity)]
69impl crate::rules::AggregateReductionResult for ReductionSubsetSumToPartition {}
70
71#[reduction(
73 transform = upper_bound {
74 max_numeric_magnitude_bits = "max_numeric_magnitude_bits + num_elements + 1",
75 num_elements = "num_elements + 1",
76 },
77 unavailable = {
78 total_sum = "target sum is source_sum + abs(source_sum - 2*target); neither raw source_sum nor target is in the source schema, which accepts arbitrary-precision values; a scale-sensitive bound from magnitude bits requires a variable exponent unsupported by the exact evaluator",
79 })]
80impl ReduceTo<Partition> for SubsetSum {
81 type Result = ReductionSubsetSumToPartition;
82
83 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
84 let total: BigUint = self.sizes().iter().cloned().sum();
85 let double_target = self.target() * 2u32;
86 let relation = total.cmp(&double_target);
87 let padding_relation = match relation {
88 Ordering::Equal => PaddingRelation::None,
89 Ordering::Greater => PaddingRelation::SameSide,
90 Ordering::Less => PaddingRelation::OppositeSide,
91 };
92
93 let convert = |value: &BigUint| {
94 value.to_i64().ok_or_else(|| {
95 crate::rules::ReductionError::invalid_target::<SubsetSum, Partition>(
96 "a source size or derived padding does not fit the Partition i64 domain",
97 )
98 })
99 };
100 let mut sizes: Vec<i64> = self.sizes().iter().map(convert).collect::<Result<_, _>>()?;
101 match relation {
102 Ordering::Equal => {}
103 Ordering::Greater => sizes.push(convert(&(total - double_target))?),
104 Ordering::Less => sizes.push(convert(&(double_target - total))?),
105 }
106
107 Ok(ReductionSubsetSumToPartition {
108 target: Partition::new(sizes).map_err(|error| {
109 crate::rules::ReductionError::construction::<SubsetSum, Partition>(error)
110 })?,
111 source_len: self.num_elements(),
112 padding_relation,
113 })
114 }
115}
116
117#[cfg(feature = "example-db")]
118pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
119 use crate::export::SolutionPair;
120
121 vec![crate::example_db::specs::RuleExampleSpec {
122 id: "subsetsum_to_partition",
123 build: || {
124 crate::example_db::specs::rule_example_with_witness::<_, Partition>(
125 SubsetSum::new(vec![1u32, 5, 6, 8], 11u32),
126 SolutionPair {
127 source_config: serde_json::json!(vec![false, true, true, false]),
128 target_config: serde_json::json!(vec![false, true, true, false, false]),
129 },
130 )
131 },
132 }]
133}
134
135#[cfg(test)]
136#[path = "../unit_tests/rules/subsetsum_partition.rs"]
137mod tests;