problemreductions/rules/
subsetsum_integerexpressionmembership.rs1use crate::models::misc::SubsetSum;
2use crate::models::misc::{IntExpr, IntegerExpressionMembership};
3use crate::reduction;
4use crate::rules::traits::{ReduceTo, ReductionResult};
5use num_traits::ToPrimitive;
6
7#[derive(Debug, Clone)]
8pub struct ReductionSubsetSumToIntegerExpressionMembership {
9 target: IntegerExpressionMembership,
10}
11
12impl ReductionResult for ReductionSubsetSumToIntegerExpressionMembership {
13 type Source = SubsetSum;
14 type Target = IntegerExpressionMembership;
15
16 fn target_problem(&self) -> &Self::Target {
17 &self.target
18 }
19
20 fn extract_solution(
21 &self,
22 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
23 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
24 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
25
26 Ok({
27 target_solution.to_vec()
30 })
31 }
32}
33
34fn build_expression(sizes: &[i64]) -> Result<IntExpr, &'static str> {
43 let make_union = |size: i64| -> Result<IntExpr, &'static str> {
44 let included = size
45 .checked_add(1)
46 .ok_or("an item size cannot be shifted into the target expression domain")?;
47 Ok(IntExpr::Union(
48 Box::new(IntExpr::Atom(1)),
49 Box::new(IntExpr::Atom(included)),
50 ))
51 };
52
53 let mut sizes = sizes.iter().copied();
54 let first = sizes
55 .next()
56 .ok_or("the target expression requires at least one source item")?;
57 let mut expr = make_union(first)?;
58 for size in sizes {
59 expr = IntExpr::Sum(Box::new(expr), Box::new(make_union(size)?));
60 }
61 Ok(expr)
62}
63
64#[reduction(
65 transform = exact {
66 num_union_nodes = "num_elements",
67 })]
68impl ReduceTo<IntegerExpressionMembership> for SubsetSum {
69 type Result = ReductionSubsetSumToIntegerExpressionMembership;
70
71 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
72 let sizes: Vec<i64> = self
73 .sizes()
74 .iter()
75 .map(|size| {
76 size.to_i64().ok_or_else(|| {
77 crate::rules::ReductionError::invalid_target::<
78 SubsetSum,
79 IntegerExpressionMembership,
80 >("subset size does not fit the target i64 domain")
81 })
82 })
83 .collect::<Result<_, _>>()?;
84
85 let shift =
86 i64::try_from(self.num_elements()).map_err(|_| {
87 crate::rules::ReductionError::integer_overflow::<
88 SubsetSum,
89 IntegerExpressionMembership,
90 >("converting the number of elements to i64")
91 })?;
92 let source_target = self.target().to_i64().ok_or_else(|| {
93 crate::rules::ReductionError::invalid_target::<SubsetSum, IntegerExpressionMembership>(
94 "subset target does not fit the target i64 domain",
95 )
96 })?;
97 let target =
98 source_target.checked_add(shift).ok_or_else(|| {
99 crate::rules::ReductionError::integer_overflow::<
100 SubsetSum,
101 IntegerExpressionMembership,
102 >("computing the shifted target")
103 })?;
104
105 let expr = build_expression(&sizes).map_err(|message| {
106 crate::rules::ReductionError::invalid_target::<SubsetSum, IntegerExpressionMembership>(
107 message,
108 )
109 })?;
110
111 Ok(ReductionSubsetSumToIntegerExpressionMembership {
112 target: IntegerExpressionMembership::new(expr, target),
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_integerexpressionmembership",
123 build: || {
124 crate::example_db::specs::rule_example_with_witness::<_, IntegerExpressionMembership>(
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]),
129 },
130 )
131 },
132 }]
133}
134
135#[cfg(test)]
136#[path = "../unit_tests/rules/subsetsum_integerexpressionmembership.rs"]
137mod tests;