|
206 | 206 | "MaximumClique": [Maximum Clique], |
207 | 207 | "MaximumCoKPlex": [Maximum Co-$k$-Plex], |
208 | 208 | "MaximumCommonEdgeSubgraph": [Maximum Common Edge Subgraph], |
| 209 | + "MaximumContactMapOverlap": [Maximum Contact Map Overlap], |
209 | 210 | "MaximumEdgeWeightedKClique": [Maximum Edge-Weighted $k$-Clique], |
210 | 211 | "HighlyConnectedDeletion": [Highly Connected Deletion], |
211 | 212 | "EulerianPath": [Eulerian Path], |
@@ -943,6 +944,111 @@ In all graph problems below, $G = (V, E)$ denotes an undirected graph with $|V| |
943 | 944 | ] |
944 | 945 | } |
945 | 946 |
|
| 947 | +#{ |
| 948 | + // Hand-authored canonical example mirroring the in-repo example_db fixture |
| 949 | + // (the canonical example for MaximumContactMapOverlap ships via the model |
| 950 | + // file's canonical_model_example_specs rather than docs/paper/data/examples.json). |
| 951 | + let n1 = 4 |
| 952 | + let n2 = 5 |
| 953 | + let contacts1 = ((0, 2), (1, 3)) |
| 954 | + let contacts2 = ((0, 3), (1, 4), (0, 2)) |
| 955 | + // Encoded config: value 0 = unmatched, value j + 1 = matched to vertex j of G_2. |
| 956 | + // The optimum [1, 2, 4, 5] aligns 0->0, 1->1, 2->3, 3->4. |
| 957 | + let config = (1, 2, 4, 5) |
| 958 | + // Decoded alignment as (i, f(i)) pairs for matched i; bot otherwise. |
| 959 | + let alignment = config.enumerate().map(((i, v)) => (i, v)) |
| 960 | + let preserved = 2 |
| 961 | + let fmt-pair(p) = $\{#p.at(0), #p.at(1)\}$ |
| 962 | + let fmt-edges(es) = es.map(fmt-pair).join(", ") |
| 963 | + [ |
| 964 | + #problem-def("MaximumContactMapOverlap")[ |
| 965 | + Given two finite ordered contact maps $G_1 = (V_1, E_1)$ and $G_2 = (V_2, E_2)$ with $V_r = {0, 1, dots, n_r - 1}$ ordered by index and $E_r subset.eq binom(V_r, 2)$ a simple undirected contact set, find an order-preserving partial injective alignment $f: V_1 -> V_2 union {bot}$ maximizing the number of preserved contacts |
| 966 | + $ |{{i, k} in E_1 : i, k "matched and" {f(i), f(k)} in E_2}|. $ |
| 967 | + Feasibility requires injectivity on matched vertices and the order-preserving condition: if $i < k$ in $V_1$ and both are matched, then $f(i) < f(k)$ in $V_2$. |
| 968 | + ][ |
| 969 | + The Maximum Contact Map Overlap problem (CMO) is a standard combinatorial formulation of flexible protein-structure comparison: each protein is represented by an ordered residue contact graph, and the alignment quality is measured by the number of superimposed contacts. Xie and Sahinidis introduced a reduction-based exact algorithm that solves CMO via a sequence of smaller maximum-weight independent-set subproblems on a derived interaction graph @XieSahinidis2007CMO. Andonov, Malod-Dognin, and Yanev later strengthened the integer-programming bound and B&B search, producing one of the fastest known exact CMO solvers @AndonovMalodDogninYanev2011CMO. The order-preserving constraint distinguishes CMO from the general maximum common edge-subgraph problem and reflects the underlying sequence of residues along each protein backbone. The registered exact baseline enumerates every assignment $V_1 -> V_2 union {bot}$ in $O^*((|V_2| + 1)^(|V_1|))$ time and filters to order-preserving injective maps#footnote[No algorithm improving on full enumeration is registered for the unrestricted variant. The specialized exact algorithms of @XieSahinidis2007CMO and @AndonovMalodDogninYanev2011CMO improve on the worst case in practice but not in the registered worst-case complexity bound.]. |
| 970 | + |
| 971 | + *Example.* Let $V_1 = {0, 1, 2, 3\}$ with $E_1 = {#fmt-edges(contacts1)}$ and $V_2 = {0, 1, 2, 3, 4\}$ with $E_2 = {#fmt-edges(contacts2)}$. The alignment $f$ given by |
| 972 | + |
| 973 | + #align(center)[#table( |
| 974 | + columns: (auto, auto, auto, auto, auto), |
| 975 | + align: center, |
| 976 | + stroke: 0.4pt, |
| 977 | + [$i$], [$0$], [$1$], [$2$], [$3$], |
| 978 | + [$f(i)$], [$0$], [$1$], [$3$], [$4$], |
| 979 | + )] |
| 980 | + |
| 981 | + is order-preserving ($0 < 1 < 3 < 4$) and injective. Both contacts of $G_1$ are preserved: |
| 982 | + - $\{0, 2\}$ maps to $\{f(0), f(2)\} = \{0, 3\} in E_2$, |
| 983 | + - $\{1, 3\}$ maps to $\{f(1), f(3)\} = \{1, 4\} in E_2$. |
| 984 | + Hence the alignment achieves the maximum possible objective $#preserved = |E_1|$. |
| 985 | + |
| 986 | + #pred-commands( |
| 987 | + "pred create --example MaximumContactMapOverlap -o cmo.json", |
| 988 | + "pred solve cmo.json --solver brute-force", |
| 989 | + "pred evaluate cmo.json --config " + config.map(str).join(","), |
| 990 | + ) |
| 991 | + |
| 992 | + #figure({ |
| 993 | + let dx = 4.0 |
| 994 | + let pos1 = range(n1).map(i => (i * 1.0, 0.0)) |
| 995 | + let pos2 = range(n2).map(j => (dx + j * 1.0, 0.0)) |
| 996 | + canvas(length: 1cm, { |
| 997 | + import draw: * |
| 998 | + // Backbone of G_1 (visualizes residue order). |
| 999 | + for i in range(n1 - 1) { |
| 1000 | + line(pos1.at(i), pos1.at(i + 1), stroke: (paint: luma(180), thickness: 0.4pt)) |
| 1001 | + } |
| 1002 | + // Backbone of G_2. |
| 1003 | + for j in range(n2 - 1) { |
| 1004 | + line(pos2.at(j), pos2.at(j + 1), stroke: (paint: luma(180), thickness: 0.4pt)) |
| 1005 | + } |
| 1006 | + // Contacts of G_1 as arcs above the backbone. |
| 1007 | + for (u, v) in contacts1 { |
| 1008 | + let p = pos1.at(u) |
| 1009 | + let q = pos1.at(v) |
| 1010 | + let mid = ((p.at(0) + q.at(0)) / 2, (p.at(1) + q.at(1)) / 2 + 0.45 * (v - u)) |
| 1011 | + hobby(p, mid, q, stroke: 0.7pt + luma(90)) |
| 1012 | + } |
| 1013 | + // Contacts of G_2 above its backbone. |
| 1014 | + for (u, v) in contacts2 { |
| 1015 | + let p = pos2.at(u) |
| 1016 | + let q = pos2.at(v) |
| 1017 | + let mid = ((p.at(0) + q.at(0)) / 2, (p.at(1) + q.at(1)) / 2 + 0.45 * (v - u)) |
| 1018 | + hobby(p, mid, q, stroke: 0.7pt + luma(90)) |
| 1019 | + } |
| 1020 | + // Vertices of G_1: highlight matched ones. |
| 1021 | + for (i, pos) in pos1.enumerate() { |
| 1022 | + let matched = config.at(i) != 0 |
| 1023 | + g-node(pos, name: "u" + str(i), |
| 1024 | + fill: if matched { graph-colors.at(0) } else { white }, |
| 1025 | + label: if matched { text(fill: white)[$#i$] } else { [$#i$] }) |
| 1026 | + } |
| 1027 | + // Vertices of G_2. |
| 1028 | + for (j, pos) in pos2.enumerate() { |
| 1029 | + g-node(pos, name: "v" + str(j), fill: white, label: [$#j$]) |
| 1030 | + } |
| 1031 | + // Mapping arrows (drawn below the backbones). |
| 1032 | + for (i, v) in alignment { |
| 1033 | + if v != 0 { |
| 1034 | + let j = v - 1 |
| 1035 | + let p = pos1.at(i) |
| 1036 | + let q = pos2.at(j) |
| 1037 | + let mid = ((p.at(0) + q.at(0)) / 2, (p.at(1) + q.at(1)) / 2 - 1.0) |
| 1038 | + hobby(p, mid, q, |
| 1039 | + stroke: (paint: graph-colors.at(0), thickness: 0.6pt, dash: "dashed")) |
| 1040 | + } |
| 1041 | + } |
| 1042 | + content((pos1.at(0).at(0) - 0.7, 0.0), text(9pt, weight: "bold")[$G_1$]) |
| 1043 | + content((pos2.at(0).at(0) - 0.7, 0.0), text(9pt, weight: "bold")[$G_2$]) |
| 1044 | + }) |
| 1045 | + }, |
| 1046 | + caption: [Maximum Contact Map Overlap instance from the issue. Top: ordered contact maps $G_1$ (left, $|V_1| = #n1$, $|E_1| = #contacts1.len()$) and $G_2$ (right, $|V_2| = #n2$, $|E_2| = #contacts2.len()$); contacts are drawn as arcs above the backbone. Bottom: dashed curves show the order-preserving partial injective alignment $f$; both contacts of $G_1$ are preserved.], |
| 1047 | + ) <fig:cmo-issue> |
| 1048 | + ] |
| 1049 | + ] |
| 1050 | +} |
| 1051 | + |
946 | 1052 | #{ |
947 | 1053 | let x = load-model-example("MaximumEdgeWeightedKClique") |
948 | 1054 | let nv = graph-num-vertices(x.instance) |
|
0 commit comments