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_solution(self.target_problem(), target_solution)?;
25
26        Ok(target_solution[..self.target.num_periods() - 1]
27            .iter()
28            .map(|&production| production > 0)
29            .collect())
30    }
31}
32
33#[reduction(
34    transform = exact {
35        num_periods = "num_elements + 1",
36    },
37    unavailable = {
38        max_capacity = "the exact target parameter is not represented by this reduction's symbolic transform",
39    }
40)]
41impl ReduceTo<ProductionPlanning> for Partition {
42    type Result = ReductionPartitionToProductionPlanning;
43
44    fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
45        let half_floor = self.total_sum() / 2;
46        let half_ceil = half_floor + (self.total_sum() % 2);
47        let mut demands = vec![0; self.num_elements()];
48        demands.push(half_ceil);
49
50        let mut capacities = self.sizes().to_vec();
51        capacities.push(0);
52
53        let mut setup_costs = self.sizes().to_vec();
54        setup_costs.push(0);
55
56        let production_costs = vec![0; self.num_elements() + 1];
57        let inventory_costs = vec![0; self.num_elements() + 1];
58
59        let num_periods = self.num_elements() + 1;
60        Ok(ReductionPartitionToProductionPlanning {
61            target: ProductionPlanning::new(
62                num_periods,
63                demands,
64                capacities,
65                setup_costs,
66                production_costs,
67                inventory_costs,
68                half_floor,
69            ),
70        })
71    }
72}
73
74#[cfg(feature = "example-db")]
75pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
76    use crate::export::SolutionPair;
77
78    vec![crate::example_db::specs::RuleExampleSpec {
79        id: "partition_to_production_planning",
80        build: || {
81            crate::example_db::specs::rule_example_with_witness::<_, ProductionPlanning>(
82                Partition::new(vec![3, 5, 2, 4, 6]).unwrap(),
83                SolutionPair {
84                    source_config: serde_json::json!(vec![false, false, false, true, true]),
85                    target_config: serde_json::json!(vec![0, 0, 0, 4, 6, 0]),
86                },
87            )
88        },
89    }]
90}
91
92#[cfg(test)]
93#[path = "../unit_tests/rules/partition_productionplanning.rs"]
94mod tests;