problemreductions/rules/
maximumcontactmapoverlap_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
18use crate::models::graph::MaximumContactMapOverlap;
19use crate::reduction;
20use crate::rules::traits::{ReduceTo, ReductionResult};
21
22#[derive(Debug, Clone)]
30pub struct ReductionCMOToILP {
31 target: ILP<bool>,
32 num_vertices_1: usize,
33 num_vertices_2: usize,
34}
35
36impl ReductionResult for ReductionCMOToILP {
37 type Source = MaximumContactMapOverlap;
38 type Target = ILP<bool>;
39
40 fn target_problem(&self) -> &ILP<bool> {
41 &self.target
42 }
43
44 fn extract_solution(
50 &self,
51 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
52 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
53 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
54
55 let n2 = self.num_vertices_2;
56 (0..self.num_vertices_1)
57 .map(|residue| {
58 let mut selected =
59 (0..n2).filter(|&mapped| target_solution[residue * n2 + mapped] == 1);
60 match (selected.next(), selected.next()) {
61 (Some(mapped), None) => Ok(mapped + 1),
62 (None, _) => Ok(0),
63 (Some(_), Some(_)) => Err(crate::rules::ExtractionError::invalid(format!(
64 "source residue {residue} maps to multiple target residues"
65 ))),
66 }
67 })
68 .collect()
69 }
70}
71
72#[reduction(transform = {
73 exact {
74 num_vars = "num_vertices_1 * num_vertices_2 + num_contacts_1 * num_contacts_2",
75 num_constraints = "num_vertices_1 + num_vertices_2 + num_vertices_1 * (num_vertices_1 - 1) / 2 * num_vertices_2 * (num_vertices_2 + 1) / 2 + 2 * num_contacts_1 * num_contacts_2",
76 num_nonzeros = "2 * num_vertices_1 * num_vertices_2 + num_vertices_1 * (num_vertices_1 - 1) * num_vertices_2 * (num_vertices_2 + 1) / 2 + 4 * num_contacts_1 * num_contacts_2",
77 },
78 upper_bound {
79 max_constraint_magnitude_bits = "2",
80 },
81})]
82impl ReduceTo<ILP<bool>> for MaximumContactMapOverlap {
83 type Result = ReductionCMOToILP;
84
85 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
86 let n1 = self.num_vertices_1();
87 let n2 = self.num_vertices_2();
88 let contacts_1 = self.contacts_1();
89 let contacts_2 = self.contacts_2();
90
91 let num_x = n1 * n2;
92 let x_idx = |i: usize, j: usize| -> usize { i * n2 + j };
93
94 let num_y = contacts_1.len() * contacts_2.len();
97 let num_vars = num_x + num_y;
98 let y_idx = |seq: usize| -> usize { num_x + seq };
99
100 let mut constraints: Vec<LinearConstraint> = Vec::new();
101
102 for i in 0..n1 {
104 let terms: Vec<(usize, i64)> = (0..n2).map(|j| (x_idx(i, j), 1)).collect();
105 constraints.push(LinearConstraint::le(terms, 1));
106 }
107
108 for j in 0..n2 {
110 let terms: Vec<(usize, i64)> = (0..n1).map(|i| (x_idx(i, j), 1)).collect();
111 constraints.push(LinearConstraint::le(terms, 1));
112 }
113
114 for i in 0..n1 {
118 for k in (i + 1)..n1 {
119 for j in 0..n2 {
120 for l in 0..=j {
121 constraints.push(LinearConstraint::le(
122 vec![(x_idx(i, j), 1), (x_idx(k, l), 1)],
123 1,
124 ));
125 }
126 }
127 }
128 }
129
130 let mut seq = 0usize;
135 for &(i, k) in contacts_1 {
136 for &(j, l) in contacts_2 {
137 let yv = y_idx(seq);
138 constraints.push(LinearConstraint::le(vec![(yv, 1), (x_idx(i, j), -1)], 0));
139 constraints.push(LinearConstraint::le(vec![(yv, 1), (x_idx(k, l), -1)], 0));
140 seq += 1;
141 }
142 }
143
144 let objective: Vec<(usize, i64)> = (0..num_y).map(|s| (y_idx(s), 1)).collect();
146
147 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Maximize)
148 .map_err(Self::target_construction)?;
149
150 Ok(ReductionCMOToILP {
151 target,
152 num_vertices_1: n1,
153 num_vertices_2: n2,
154 })
155 }
156}
157
158#[cfg(feature = "example-db")]
159pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
160 vec![crate::example_db::specs::RuleExampleSpec {
161 id: "maximumcontactmapoverlap_to_ilp",
162 build: || {
163 let source = MaximumContactMapOverlap::new(
168 4,
169 vec![(0, 2), (1, 3)],
170 5,
171 vec![(0, 3), (1, 4), (0, 2)],
172 );
173 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
174 },
175 }]
176}
177
178#[cfg(test)]
179#[path = "../unit_tests/rules/maximumcontactmapoverlap_ilp.rs"]
180mod tests;