problemreductions/models/graph/
maximum_contact_map_overlap.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
18use crate::traits::Problem;
19use crate::types::Max;
20use serde::{Deserialize, Serialize};
21use std::collections::HashSet;
22
23inventory::submit! {
24 ProblemSchemaEntry {
25 name: "MaximumContactMapOverlap",
26 display_name: "Maximum Contact Map Overlap",
27 aliases: &["CMO", "MaxCMO"],
28 dimensions: &[],
29 module_path: module_path!(),
30 description: "Maximize the number of preserved contacts under an order-preserving partial injective alignment from G_1 into G_2",
31 fields: &[
32 FieldInfo {
33 name: "num_vertices_1",
34 type_name: "usize",
35 description: "Number of ordered residues/vertices in the first contact map G_1",
36 },
37 FieldInfo {
38 name: "contacts_1",
39 type_name: "Vec<(usize,usize)>",
40 description: "Simple undirected contacts of G_1 as canonicalized (u,v) pairs with u < v",
41 },
42 FieldInfo {
43 name: "num_vertices_2",
44 type_name: "usize",
45 description: "Number of ordered residues/vertices in the second contact map G_2",
46 },
47 FieldInfo {
48 name: "contacts_2",
49 type_name: "Vec<(usize,usize)>",
50 description: "Simple undirected contacts of G_2 as canonicalized (u,v) pairs with u < v",
51 },
52 ],
53 }
54}
55
56inventory::submit! {
57 ProblemSizeFieldEntry {
58 name: "MaximumContactMapOverlap",
59 fields: &["num_vertices_1", "num_vertices_2", "num_contacts_1", "num_contacts_2"],
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
79pub struct MaximumContactMapOverlap {
80 num_vertices_1: usize,
81 contacts_1: Vec<(usize, usize)>,
82 num_vertices_2: usize,
83 contacts_2: Vec<(usize, usize)>,
84}
85
86fn canonicalize_contacts(
90 raw: Vec<(usize, usize)>,
91 num_vertices: usize,
92 side: &str,
93) -> Vec<(usize, usize)> {
94 let mut seen: HashSet<(usize, usize)> = HashSet::new();
95 let mut out = Vec::with_capacity(raw.len());
96 for (u, v) in raw {
97 assert!(
98 u < num_vertices && v < num_vertices,
99 "{side} contact endpoint out of range for num_vertices = {num_vertices}: ({u}, {v})"
100 );
101 assert!(u != v, "{side} contact has self-loop: ({u}, {v})");
102 let (a, b) = if u < v { (u, v) } else { (v, u) };
103 assert!(
104 seen.insert((a, b)),
105 "{side} has duplicate contact after normalization: ({a}, {b})"
106 );
107 out.push((a, b));
108 }
109 out
110}
111
112impl MaximumContactMapOverlap {
113 pub fn new(
118 num_vertices_1: usize,
119 contacts_1: Vec<(usize, usize)>,
120 num_vertices_2: usize,
121 contacts_2: Vec<(usize, usize)>,
122 ) -> Self {
123 let contacts_1 = canonicalize_contacts(contacts_1, num_vertices_1, "G_1");
124 let contacts_2 = canonicalize_contacts(contacts_2, num_vertices_2, "G_2");
125 Self {
126 num_vertices_1,
127 contacts_1,
128 num_vertices_2,
129 contacts_2,
130 }
131 }
132
133 pub fn num_vertices_1(&self) -> usize {
135 self.num_vertices_1
136 }
137
138 pub fn num_vertices_2(&self) -> usize {
140 self.num_vertices_2
141 }
142
143 pub fn num_contacts_1(&self) -> usize {
145 self.contacts_1.len()
146 }
147
148 pub fn num_contacts_2(&self) -> usize {
150 self.contacts_2.len()
151 }
152
153 pub fn contacts_1(&self) -> &[(usize, usize)] {
155 &self.contacts_1
156 }
157
158 pub fn contacts_2(&self) -> &[(usize, usize)] {
160 &self.contacts_2
161 }
162
163 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
171 if config.len() != self.num_vertices_1 {
172 return false;
173 }
174 let max_value = self.num_vertices_2; let mut previous_nonzero: Option<usize> = None;
176 let mut used: HashSet<usize> = HashSet::new();
177 for &value in config {
178 if value > max_value {
179 return false;
180 }
181 if value == 0 {
182 continue;
183 }
184 if !used.insert(value) {
185 return false;
186 }
187 if let Some(prev) = previous_nonzero {
188 if value <= prev {
189 return false;
190 }
191 }
192 previous_nonzero = Some(value);
193 }
194 true
195 }
196
197 pub fn preserved_contact_count(&self, config: &[usize]) -> Option<usize> {
200 if !self.is_valid_solution(config) {
201 return None;
202 }
203 let contacts_2_set: HashSet<(usize, usize)> = self.contacts_2.iter().copied().collect();
204 let mut count = 0usize;
205 for &(i, k) in &self.contacts_1 {
206 let fi = config[i];
207 let fk = config[k];
208 if fi == 0 || fk == 0 {
209 continue;
210 }
211 let a = fi - 1;
213 let b = fk - 1;
214 let pair = if a < b { (a, b) } else { (b, a) };
215 if contacts_2_set.contains(&pair) {
216 count += 1;
217 }
218 }
219 Some(count)
220 }
221}
222
223impl Problem for MaximumContactMapOverlap {
224 const NAME: &'static str = "MaximumContactMapOverlap";
225 type Value = Max<i64>;
226
227 fn variant() -> Vec<(&'static str, &'static str)> {
228 crate::variant_params![]
229 }
230
231 fn dims(&self) -> Vec<usize> {
232 vec![self.num_vertices_2 + 1; self.num_vertices_1]
233 }
234
235 fn evaluate(&self, config: &[usize]) -> Max<i64> {
236 match self.preserved_contact_count(config) {
237 Some(count) => Max(Some(count as i64)),
238 None => Max(None),
239 }
240 }
241}
242
243crate::declare_variants! {
244 default MaximumContactMapOverlap => "(num_vertices_2 + 1)^num_vertices_1",
245}
246
247#[cfg(feature = "example-db")]
248pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
249 vec![crate::example_db::specs::ModelExampleSpec {
259 id: "maximum_contact_map_overlap",
260 instance: Box::new(MaximumContactMapOverlap::new(
261 4,
262 vec![(0, 2), (1, 3)],
263 5,
264 vec![(0, 3), (1, 4), (0, 2)],
265 )),
266 optimal_config: vec![1, 2, 4, 5],
267 optimal_value: serde_json::json!(2),
268 }]
269}
270
271#[cfg(test)]
272#[path = "../../unit_tests/models/graph/maximum_contact_map_overlap.rs"]
273mod tests;