Skip to main content

problemreductions/rules/
partition_productionplanning.rs

1//! Reduction from Partition to Production Planning.
2
3use crate::models::misc::{Partition, ProductionPlanning};
4use crate::reduction;
5use crate::rules::traits::{ReduceTo, ReductionResult};
6
7#[derive(Debug, Clone)]
8pub struct ReductionPartitionToProductionPlanning {
9    target: ProductionPlanning,
10}
11
12impl ReductionResult for ReductionPartitionToProductionPlanning {
13    type Source = Partition;
14    type Target = ProductionPlanning;
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_witness(
25            self.target_problem(),
26            target_solution,
27            |value| value.0,
28            "target witness does not satisfy the target problem",
29        )?;
30
31        Ok(target_solution[..self.target.num_periods() - 1]
32            .iter()
33            .map(|&production| production > 0)
34            .collect())
35    }
36}
37
38#[crate::aggregate_reduction(identity)]
39impl crate::rules::AggregateReductionResult for ReductionPartitionToProductionPlanning {}
40
41#[reduction(
42    transform = {
43        exact { num_periods = "num_elements + 1", },
44        upper_bound {
45            // Capacities are the positive source sizes followed by zero.
46            max_capacity = "total_sum",
47            max_numeric_magnitude_bits = "max_numeric_magnitude_bits + num_elements",
48        },
49    },
50)]
51impl ReduceTo<ProductionPlanning> for Partition {
52    type Result = ReductionPartitionToProductionPlanning;
53
54    fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
55        let half_floor = self.total_sum() / 2;
56        let half_ceil = half_floor + (self.total_sum() % 2);
57        let mut demands = vec![0; self.num_elements()];
58        demands.push(half_ceil);
59
60        let mut capacities = self.sizes().to_vec();
61        capacities.push(0);
62
63        let mut setup_costs = self.sizes().to_vec();
64        setup_costs.push(0);
65
66        let production_costs = vec![0; self.num_elements() + 1];
67        let inventory_costs = vec![0; self.num_elements() + 1];
68
69        let num_periods = self.num_elements() + 1;
70        Ok(ReductionPartitionToProductionPlanning {
71            target: ProductionPlanning::new(
72                num_periods,
73                demands,
74                capacities,
75                setup_costs,
76                production_costs,
77                inventory_costs,
78                half_floor,
79            ),
80        })
81    }
82}
83
84#[cfg(feature = "example-db")]
85pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
86    use crate::export::SolutionPair;
87
88    vec![crate::example_db::specs::RuleExampleSpec {
89        id: "partition_to_production_planning",
90        build: || {
91            crate::example_db::specs::rule_example_with_witness::<_, ProductionPlanning>(
92                Partition::new(vec![3, 5, 2, 4, 6]).unwrap(),
93                SolutionPair {
94                    source_config: serde_json::json!(vec![false, false, false, true, true]),
95                    target_config: serde_json::json!(vec![0, 0, 0, 4, 6, 0]),
96                },
97            )
98        },
99    }]
100}
101
102#[cfg(test)]
103#[path = "../unit_tests/rules/partition_productionplanning.rs"]
104mod tests;