problemreductions/rules/
sumofsquarespartition_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
20use crate::models::misc::SumOfSquaresPartition;
21use crate::reduction;
22use crate::rules::traits::{ReduceTo, ReductionResult};
23
24#[derive(Debug, Clone)]
32pub struct ReductionSSPToILP {
33 target: ILP<bool>,
34 num_elements: usize,
35 num_groups: usize,
36}
37
38impl ReductionSSPToILP {
39 fn x_var(&self, i: usize, g: usize) -> usize {
40 i * self.num_groups + g
41 }
42
43 fn z_var(&self, i: usize, j: usize, g: usize) -> usize {
44 let n = self.num_elements;
45 let k = self.num_groups;
46 n * k + (i * n + j) * k + g
47 }
48}
49
50impl ReductionResult for ReductionSSPToILP {
51 type Source = SumOfSquaresPartition;
52 type Target = ILP<bool>;
53
54 fn target_problem(&self) -> &ILP<bool> {
55 &self.target
56 }
57
58 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
60 let num_groups = self.num_groups;
61 (0..self.num_elements)
62 .map(|i| {
63 (0..num_groups)
64 .find(|&g| {
65 let idx = i * num_groups + g;
66 idx < target_solution.len() && target_solution[idx] == 1
67 })
68 .unwrap_or(0)
69 })
70 .collect()
71 }
72}
73
74#[reduction(
75 overhead = {
76 num_vars = "num_elements * num_groups + num_elements^2 * num_groups",
77 num_constraints = "num_elements + 3 * num_elements^2 * num_groups",
78 }
79)]
80impl ReduceTo<ILP<bool>> for SumOfSquaresPartition {
81 type Result = ReductionSSPToILP;
82
83 fn reduce_to(&self) -> Self::Result {
84 let n = self.num_elements();
85 let k = self.num_groups();
86 let num_vars = n * k + n * n * k;
87
88 let result = ReductionSSPToILP {
89 target: ILP::empty(),
90 num_elements: n,
91 num_groups: k,
92 };
93
94 let mut constraints = Vec::new();
95
96 for i in 0..n {
98 let terms: Vec<(usize, f64)> = (0..k).map(|g| (result.x_var(i, g), 1.0)).collect();
99 constraints.push(LinearConstraint::eq(terms, 1.0));
100 }
101
102 for i in 0..n {
104 for j in 0..n {
105 for g in 0..k {
106 let z = result.z_var(i, j, g);
107 let xi = result.x_var(i, g);
108 let xj = result.x_var(j, g);
109
110 constraints.push(LinearConstraint::le(vec![(z, 1.0), (xi, -1.0)], 0.0));
112 constraints.push(LinearConstraint::le(vec![(z, 1.0), (xj, -1.0)], 0.0));
114 constraints.push(LinearConstraint::le(
116 vec![(z, -1.0), (xi, 1.0), (xj, 1.0)],
117 1.0,
118 ));
119 }
120 }
121 }
122
123 let sizes = self.sizes();
125 let mut objective: Vec<(usize, f64)> = Vec::new();
126 for i in 0..n {
127 for j in 0..n {
128 for g in 0..k {
129 let coeff = sizes[i] as f64 * sizes[j] as f64;
130 if coeff.abs() > 0.0 {
131 objective.push((result.z_var(i, j, g), coeff));
132 }
133 }
134 }
135 }
136
137 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize);
138
139 ReductionSSPToILP {
140 target,
141 num_elements: n,
142 num_groups: k,
143 }
144 }
145}
146
147#[cfg(feature = "example-db")]
148pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
149 vec![crate::example_db::specs::RuleExampleSpec {
150 id: "sumofsquarespartition_to_ilp",
151 build: || {
152 let source = SumOfSquaresPartition::new(vec![1, 2, 3, 4], 2);
155 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
156 },
157 }]
158}
159
160#[cfg(test)]
161#[path = "../unit_tests/rules/sumofsquarespartition_ilp.rs"]
162mod tests;