Skip to main content

problemreductions/rules/
setsplitting_ilp.rs

1//! Reduction from SetSplitting to ILP (Integer Linear Programming).
2//!
3//! Binary variable $x_i \in \{0,1\}$ per universe element: 0 means element $i$
4//! is placed in part $S_1$, 1 means it is placed in part $S_2$.
5//!
6//! For each subset $C = \{i_1, \ldots, i_k\}$ we need:
7//! - At least one element in $S_2$: $\sum_{j \in C} x_j \geq 1$
8//! - At least one element in $S_1$: $\sum_{j \in C} x_j \leq k - 1$
9//!
10//! Objective: feasibility (minimize 0).
11
12use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
13use crate::models::set::SetSplitting;
14use crate::reduction;
15use crate::rules::traits::{ReduceTo, ReductionResult};
16
17/// Result of reducing SetSplitting to ILP.
18#[derive(Debug, Clone)]
19pub struct ReductionSetSplittingToILP {
20    target: ILP<bool>,
21}
22
23impl ReductionResult for ReductionSetSplittingToILP {
24    type Source = SetSplitting;
25    type Target = ILP<bool>;
26
27    fn target_problem(&self) -> &ILP<bool> {
28        &self.target
29    }
30
31    fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
32        target_solution.to_vec()
33    }
34}
35
36#[reduction(
37    overhead = {
38        num_vars = "universe_size",
39        num_constraints = "2 * num_subsets",
40    }
41)]
42impl ReduceTo<ILP<bool>> for SetSplitting {
43    type Result = ReductionSetSplittingToILP;
44
45    fn reduce_to(&self) -> Self::Result {
46        let num_vars = self.universe_size();
47        let mut constraints = Vec::new();
48
49        for subset in self.subsets() {
50            let k = subset.len();
51            let terms: Vec<(usize, f64)> = subset.iter().map(|&e| (e, 1.0)).collect();
52
53            // At least one element in S2: sum >= 1
54            constraints.push(LinearConstraint::ge(terms.clone(), 1.0));
55
56            // At least one element in S1: sum <= k - 1
57            constraints.push(LinearConstraint::le(terms, (k - 1) as f64));
58        }
59
60        let target = ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize);
61        ReductionSetSplittingToILP { target }
62    }
63}
64
65#[cfg(feature = "example-db")]
66pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
67    vec![crate::example_db::specs::RuleExampleSpec {
68        id: "setsplitting_to_ilp",
69        build: || {
70            let source = SetSplitting::new(
71                6,
72                vec![vec![0, 1, 2], vec![2, 3, 4], vec![0, 4, 5], vec![1, 3, 5]],
73            );
74            crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
75        },
76    }]
77}
78
79#[cfg(test)]
80#[path = "../unit_tests/rules/setsplitting_ilp.rs"]
81mod tests;