Skip to content

Fix #1205: [Rule] MaximumContactMapOverlap to MaximumClique - #1235

Open
isPANN wants to merge 1 commit into
mainfrom
issue-1205-maxcontactmapoverlap-maximumclique
Open

isPANN wants to merge 1 commit into
mainfrom
issue-1205-maxcontactmapoverlap-maximumclique

Conversation

@isPANN

@isPANN isPANN commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1205

Adds the reduction MaximumContactMapOverlap → MaximumClique<SimpleGraph, One> (order-preserving association graph): one vertex per pair of contacts, an edge between two pairs that differ in both coordinates and keep the order of the residue assignments consistent. Optimal cliques decode to optimal alignments.

  • Rule: src/rules/maximumcontactmapoverlap_maximumclique.rs, registered in src/rules/mod.rs with a canonical example.
  • Overhead: num_vertices = num_contacts_1 * num_contacts_2 (exact); num_edges is the upper bound m1(m1-1)m2(m2-1)/2.
  • Tests: src/unit_tests/rules/maximumcontactmapoverlap_maximumclique.rs (closed-loop, rejection paths, overflow boundary).
  • Paper: new entry with a worked example whose facts are computed from the fixture and asserted, plus the Strickland et al. reference.
  • maximumcontactmapoverlap_ilp.rs: one-line disambiguation, since the source now has two ReduceTo impls.

Local gate: make check and make paper pass.

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@isPANN

isPANN commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator Author

Agentic Review Report

Subject: rule MaximumContactMapOverlap → MaximumClique/SimpleGraph/One (order-preserving association graph).
Verdict: approve. There are no open findings.

Structural and semantic

  • Rule module: src/rules/maximumcontactmapoverlap_maximumclique.rs is registered in src/rules/mod.rs, together with canonical_rule_example_specs().
    • #[reduction(transform = …)] names each target parameter once: num_vertices = num_contacts_1 * num_contacts_2 is exact; num_edges = m1(m1−1)m2(m2−1)/2 is an upper bound.
    • The impl names MaximumContactMapOverlap literally.
  • Construction: the target graph is built with SimpleGraph::try_new, and its errors are mapped through target_construction. The vertex count uses checked_mul (pair_count), and tests cover both sides of the overflow boundary.
  • Extraction: extract_solution validates the target witness once, through validate_target_witness. That rejects wrong lengths, via evaluate, and non-cliques, via Max(None). It then writes t+1 at each assigned source position, with no clamping or default branch, and every rejection path has a test.
  • Result mapping: no value map is registered. This matches docs/src/design.md (optimization → optimization decodes an optimal witness) and sibling rules such as MaximumIndependentSet → MaximumClique and MaximumContactMapOverlap → ILP. pred reduce --aggregate refuses the path, as expected.
  • Shared files: the only shared-file change is a one-line disambiguation, <Self as ReduceTo<ILP<bool>>>::target_construction, in maximumcontactmapoverlap_ilp.rs. The second ReduceTo impl makes it necessary. No test loses lines, and no generated exports are in the diff.
  • Hand trace: I traced the issue's instance (4/4 residues, E1=[(0,2),(1,3),(0,3)], E2=[(0,1),(2,3),(0,3)]).
    • The target has 9 vertices and edges [(0,8),(4,8)], matching the issue's table. pred path … src.json measures num_vertices = 9 and num_edges = 2, against the bound 18.
    • Clique {0,8} extracts [1,0,2,4], with score 2, which is the brute-force optimum.
  • Paper proof: the paper entry gives the construction, both directions, extraction, the overhead, and YES and NO examples loaded with load-example.
  • Issue compliance:
    • Definition, framing (optimization), vertex order (source contact first, stored order), adjacency rule, extraction and overhead (a sharper but valid bound) all match the issue.
    • Every item on the issue's Rust validation list is tested: all contact-graph pairs through three vertices with every target clique, the canonical example, empty graphs, partial alignments (n1 > n2), malformed and crossing witnesses, parameter predictions, and the non-maximum-witness score caveat.
    • The issue's optimum of 2 is confirmed by brute force.

Quality

  • Test coverage: 14 tests, 2.05 s. They include instances with 5 residues: all sparse contact-set pairs to five residues where the source brute force is small, and an n1 = 5, n2 = 2 instance.
    • Every clique of every exhaustive instance is extracted, asserting score ≥ clique size, with equality to the optimum at maximum cliques.
    • The tests also cover the threshold-3 infeasible case, stored-order labelling, and both sides of the overflow boundary.
  • Coverage: the new module has no uncovered lines.
  • Full checks: make check and make paper pass.

Feature test

I used the pred binary built from this change, in a scratch directory:

  • pred show MaximumContactMapOverlap lists the new outgoing edge with its contract.
  • pred create --example MaximumContactMapOverlap --to MaximumClique/SimpleGraph/One, then pred path, writes a single direct route.
  • pred reduce writes a bundle whose target is the 9-vertex graph with edges [(0,8),(4,8)] and unit weights.
  • pred solve bundle.json (default and --solver brute-force) returns Max(2), with source solution [1,3,0,4], an alternative optimum.
  • pred evaluate src.json --config '[1,0,2,4]' gives Max(2).
  • Corpus-style round trips on 100 instances were all correct.

Findings

None open. All findings from earlier rounds are resolved:

  • Paper example facts: the example paragraph of docs/paper/reductions.typ now computes every fact it states from the loaded fixture, each guarded by an assert:
    • the assignment of each vertex and the compatibility edge list;
    • the clique sizes, by subset enumeration, giving a maximum of 2 and no 3-clique;
    • the contacts the witness preserves;
    • the reversed pair of source positions behind the non-edge between vertices 0 and 4;
    • the path shape of both contact maps (contact count and degree sequence);
    • the identity alignment and its score.
  • Provenance: the module doc of src/rules/maximumcontactmapoverlap_maximumclique.rs cites Strickland, Barnes and Sokol (2005) as background only.

make paper, make check and the full rule test suite pass.

@isPANN

isPANN commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator Author

Verification certificate

Verdict: verified. The construction is correct and the proof holds; no counterexample or disagreement was found.

Type chain. Source MaximumContactMapOverlap (value Max<i64>) → target MaximumClique<SimpleGraph, One> (value Max<i64>). The types are compatible. Before this PR pred path finds no route from the source to MaximumClique (the source's only outgoing edge is to ILP), so the rule is not dominated by an existing path.

Construction. One vertex per pair of contacts (i, j), numbered i*m2+j in stored contact order; two vertices are adjacent iff they differ in both coordinates and the implied residue assignments agree with the order. A maximum clique decodes to a maximum order-preserving alignment. Overhead: num_vertices = m1*m2 (exact); num_edges <= m1(m1-1)m2(m2-1)/2 = 2*C(m1,2)*C(m2,2) (upper bound; the naive C(N,2) bound is never attained for N >= 2).

Checks (all 0 failures).

  • Construction script: 2,034,942 checks (target size against the declared formulas for every m1, m2 in 0..6; forward direction on every alignment; backward direction on every clique; clique enumerator cross-checked against subset brute force for N <= 10; rejection of non-cliques and wrong-length witnesses; a NO instance).
  • Independent check script, written from the proof alone: 4,705,004 checks. Instances: every pair of contact sets with n1, n2 <= 4 (5,776 pairs); n = 5 on one side (all 1,024 contact sets) with n <= 3 on the other, both directions (24,576); 1,500 instances with shuffled contact order; 400 random instances with n = 5–7; 100 benchmark instances (7 residues per map, 12 and 6 contacts); Hypothesis with 4 strategies x 300 examples (derandomize=True, database=None, deadline=None; dense, shared-endpoint, crossing/nested arcs, lopsided/empty). Seed 1205.
  • Mutation testing: 14 construction/extraction mutants plus oracle mutants are each caught by both scripts; the only checks no mutant can fail are the proof's algebraic identity and two fixed facts.
  • Cross-comparison of the two scripts' targets: 39,728 shared instances, 0 mismatches. Against the Rust implementation through pred reduce: 411 instances, 0 mismatches.

Scripts follow (each collapsed).

Proof (Typst)
#set document(title: "MaximumContactMapOverlap to MaximumClique")
#set page(margin: 2cm)
#set heading(numbering: "1.")

= MaximumContactMapOverlap $arrow.r$ MaximumClique⟨SimpleGraph, One⟩

*Provenance.* The reduction of contact-map overlap to maximum clique on a graph of compatible residue pairings is due to Strickland, Barnes and Sokol (Operations Research 53(3), 389--402, 2005; DOI 10.1287/opre.1040.0189). The full text could not be obtained (only the abstract and secondary summaries were reachable), so the construction and proof below are *derived here*, cf. Strickland et al.; the cited paper is background only. Rahrig, Leontis and Zirbel (Bioinformatics 26(21), 2010) describe the same order-preserving alignment graph for the two-contact case.

*Type resolution.* Source: `MaximumContactMapOverlap` has `type Value = Max<i64>` (`src/models/graph/maximum_contact_map_overlap.rs`), configuration `Vec<usize>` of length $n_1$, `0` unmatched, `j+1` image $j$, nonzero entries strictly increasing; the all-unmatched vector is always feasible. Target: `MaximumClique<SimpleGraph, One>` has `type Value = Max<W::Sum>` with `<One as WeightElement>::Sum = i64` (`src/models/graph/maximum_clique.rs`, `src/types.rs`), i.e. `Max<i64>`, configuration `Vec<bool>`, value = clique cardinality. `Max<i64>` $->$ `Max<i64>`: witness-compatible. Redundancy: `pred path MaximumContactMapOverlap MaximumClique/SimpleGraph/One` reports no path; the only outgoing edge of the source is to `ILP/bool/i64/general`, and no path leads back from ILP to MaximumClique. The new edge is not dominated. This is an optimization rule.

*Theorem.* Let $I = (n_1, E_1, n_2, E_2)$ be an instance with contact lists $E_1 = [e_1, dots, e_(m_1)]$, $E_2 = [d_1, dots, d_(m_2)]$ (each contact $(u,v)$ with $u<v$). The graph $G_I$ below has $m_1 m_2$ vertices, its maximum clique size equals the optimum of $I$, and every maximum clique decodes by the extraction below to an optimal alignment.

== Construction

+ *Vertices.* For each $i in [m_1]$, $j in [m_2]$, with $e_i = (u,v)$ and $d_j = (a,b)$, create one vertex $p_(i j)$ with assignment set $A_(i j) = {(u,a), (v,b)}$. Order vertices by $i$ first, then $j$; index $p_(i j) = i m_2 + j$. Every vertex has weight 1.
+ *Edges.* Distinct vertices $p, q$ are adjacent iff for every $(s,t) in A_p$ and $(s', t') in A_q$: $"sgn"(s - s') = "sgn"(t - t')$, i.e. $s<s' arrow.l.r.double t<t'$, $s=s' arrow.l.r.double t=t'$, $s>s' arrow.l.r.double t>t'$. 
+ *Extraction.* Given a vertex set $K$ that is a clique, start with $f = (0, dots, 0)$ of length $n_1$; for every $p in K$ and every $(s,t) in A_p$ set $f[s] = t+1$. Return $f$. If $K$ is not a clique, extraction reports an error.

Notation: for a feasible alignment $f$ the _score_ $sigma(f)$ is the number of $(u,v) in E_1$ with $f(u), f(v)$ both matched and $(f(u), f(v)) in E_2$ (as $f$ is increasing, $f(u)<f(v)$).

== Overhead

#table(columns: 3,
  [target parameter], [formula], [contract],
  [`num_vertices`], [$m_1 m_2$ (`num_contacts_1 * num_contacts_2`)], [exact],
  [`num_edges`], [$<= binom(m_1 m_2, 2)$; sharper: $<= 2 binom(m_1,2) binom(m_2,2)$], [upper bound],
)

The vertex count is exact by construction. For the edge bound: two vertices $p_(i j), p_(i j')$ with $j != j'$ share the source contact $e_i = (u,v)$ so they both assign $u$, to $a$ and to $a'$ respectively; adjacency needs $a = a'$ and (from $v$) $b = b'$, giving $d_j = d_(j')$, contradiction. Symmetrically, let $i != i'$ with $e_i = (u,v)$, $e_(i') = (u',v')$ and $d_j = (a,b)$. If $u != u'$, the assignments $(u,a)$ and $(u',a)$ have $u != u'$ but equal images, violating equality preservation; if $u = u'$ then $v != v'$ and $(v,b),(v',b)$ violate it in the same way. So only pairs differing in both indices can be edges, and their number is $binom(m_1 m_2, 2) - m_1 binom(m_2, 2) - m_2 binom(m_1, 2) = 2 binom(m_1,2) binom(m_2,2)$. The bound $binom(N, 2)$, $N = m_1 m_2$, is attained only when $N <= 1$, so it is not exact for general inputs and is declared as an upper bound. If $m_1 = 0$ or $m_2 = 0$ the graph is empty. Construction time and output size are $O(N + N^2)$.

== Correctness

*Lemma 1 (a vertex is a locally increasing injection).* $A_(i j) = {(u,a),(v,b)}$ has $u<v$ and $a<b$.

*(⇒) Source alignment gives a clique.* Let $f$ be a feasible alignment with score $sigma$. Let $S = {(u,v) in E_1 : f(u), f(v) "matched", (f(u),f(v)) in E_2}$, $|S| = sigma$. For each $e_i in S$ let $j(i)$ be the index of the target contact $(f(u), f(v))$; it is unique since $E_2$ has no duplicates. The vertices $p_(i, j(i))$ for $e_i in S$ are distinct (their first index differs), and $A_(i, j(i)) subset.eq {(s, f(s))}$, the graph of $f$. Take two of them and pairs $(s,t),(s',t')$ from their assignment sets: all four are of the form $(s, f(s))$. As $f$ is a function, $s = s' arrow.double t = t'$; as $f$ is injective, $t = t' arrow.double s = s'$; as $f$ is strictly increasing on its matched positions, $s<s' arrow.l.r.double t<t'$. All three comparisons agree, so the vertices are adjacent. Thus $S$ yields a clique of size $sigma$, and $omega(G_I) >= "OPT"(I)$.

*(⇐) Clique gives an alignment.* Let $K$ be a clique and $R = union.big_(p in K) A_p$. (i) $R$ is a function: if $(s,t),(s,t') in R$, either they come from the same vertex (then $t=t'$ because $s$ occurs once in an assignment set, by $u<v$) or from distinct adjacent vertices, where $s=s$ forces $t=t'$. (ii) $R$ is injective and strictly increasing: if $(s,t),(s',t') in R$ with $s<s'$, adjacency (or Lemma 1 within one vertex) gives $t<t'$. Hence the extracted $f$, with $f[s]=t+1$ exactly for $(s,t) in R$, is a feasible alignment (nonzero entries strictly increasing; $t<n_2$ so $t+1 <= n_2$). Each $p_(i j) in K$ certifies the source contact $e_i$ as preserved: $f(u)=a$, $f(v)=b$, and $(a,b)=d_j in E_2$. Distinct clique vertices certify distinct source contacts: if $p_(i j), p_(i j') in K$ then, as shown above, $d_j = d_(j')$ so $j=j'$. Therefore $sigma(f) >= |K|$ and $"OPT"(I) >= omega(G_I)$.

Combining both directions: $omega(G_I) = "OPT"(I)$. Moreover, for a maximum clique $K$, $"OPT" >= sigma(f) >= |K| = omega = "OPT"$, so $f$ is optimal. For an arbitrary (non-maximum) clique only the inequality $sigma(f) >= |K|$ holds: pairwise compatibility can force extra preserved contacts that were not selected (e.g. source and target both the triangle on positions $0,1,2$; the vertices $p_(0 0)$ and $p_(1 1)$ extract $f = [1,2,3]$ with score 3 but the clique has size 2). Hence the result mapping is the identity only at optimum, and no value map is claimed for non-optimal witnesses. Also $omega <= min(m_1, m_2)$ since distinct clique vertices have distinct source contacts and (by the same argument with roles swapped) distinct target contacts.

== Solution extraction

Described as step 3 of the construction. It validates the clique once (every pair of selected vertices is an edge, selection length $= m_1 m_2$), then writes $t+1$ at source position $s$ for each assignment. Special cases: the empty clique yields the all-unmatched vector; an empty contact set yields an empty graph and the all-unmatched vector of length $n_1$; $n_1 > n_2$ needs no special case because alignments are partial; contacts sharing a source endpoint must agree on its image (equality preservation); disjoint source contacts cannot share a target endpoint; crossing assignments are non-adjacent (strict-inequality preservation); no assumption about connectedness or isolated positions is used. A selection that is not a clique, or of the wrong length, is rejected.

== YES example

$n_1 = n_2 = 4$, $E_1 = [(0,2),(1,3),(0,3)]$, $E_2 = [(0,1),(2,3),(0,3)]$ (both are paths on four vertices, but the orders differ). Vertices $p_(i j)$, index $3i+j$:

#table(columns: 3,
  [vertex], [pair $(i,j)$], [assignments],
  [0], [(0,0)], [$0 -> 0, 2 -> 1$],
  [1], [(0,1)], [$0 -> 2, 2 -> 3$],
  [2], [(0,2)], [$0 -> 0, 2 -> 3$],
  [3], [(1,0)], [$1 -> 0, 3 -> 1$],
  [4], [(1,1)], [$1 -> 2, 3 -> 3$],
  [5], [(1,2)], [$1 -> 0, 3 -> 3$],
  [6], [(2,0)], [$0 -> 0, 3 -> 1$],
  [7], [(2,1)], [$0 -> 2, 3 -> 3$],
  [8], [(2,2)], [$0 -> 0, 3 -> 3$],
)

Overhead: 9 vertices ($= 3 dot 3$), 2 edges $<= binom(9,2) = 36$ and $<= 2 binom(3,2) binom(3,2) = 18$. The edge list is $[(0,8),(4,8)]$: e.g. $p_0, p_8$ assign ${0->0, 2->1}$ and ${0->0, 3->3}$; the comparisons $0=0$ with $0=0$, $0<3$ with $0<3$, $2>0$ with $1>0$, $2<3$ with $1<3$ all agree. Vertices $0$ and $4$ are not adjacent: $2 -> 1$ and $1 -> 2$ reverse the order of source positions $1<2$. The clique ${0,8}$ has size 2 and extracts $f = [1,0,2,4]$: position 0 to 0, position 1 unmatched, position 2 to 1, position 3 to 3; it preserves $(0,2) -> (0,1)$ and $(0,3) -> (0,3)$, score 2 $=$ optimum. The suboptimal cliques ${0}$ and ${4}$ extract $[1,0,2,0]$ and $[0,3,0,4]$, each with score 1; the empty clique extracts $[0,0,0,0]$ with score 0.

== NO example

Same instance with threshold 3 (all three source contacts preserved). Target side: the maximum clique has size 2 since the only edges are $(0,8)$ and $(4,8)$, which form no triangle, so no clique of size 3 exists. Source side: preserving three contacts needs positions $0,1,2,3$ all matched (contacts $(0,2),(1,3),(0,3)$ cover every position); an increasing injection of four positions into four positions is the identity $[1,2,3,4]$, which preserves only $(0,3)$ (contacts $(0,2)$ and $(1,3)$ map to $(0,2)$ and $(1,3)$, absent from $E_2$). So the optimum is 2 and threshold 3 is infeasible on both sides, although the two contact graphs are isomorphic as unordered graphs (an order-ignoring construction would report 3).
Construction script (Python)
#!/usr/bin/env python3
"""Constructor verification: MaximumContactMapOverlap -> MaximumClique<SimpleGraph, One>.

Source: Max<i64>, config = Vec<usize> of length n1, 0 = unmatched, j+1 = image j,
nonzero entries strictly increasing (all-unmatched always feasible).
Target: Max<i64> (One weights), config = Vec<bool>; a clique has value = its size.

Seven sections (see the audit at the end); the last line is
    TOTAL: <checks> checks, <failures> failures
"""
import itertools
import json
import os
import random
import sys

import sympy as sp
from hypothesis import HealthCheck, given, settings, strategies as st

SEED = 20261103
random.seed(SEED)

COUNTS = {}
FAILS = {}
FAIL_MSGS = []


def check(section, cond, msg=""):
    COUNTS[section] = COUNTS.get(section, 0) + 1
    if not cond:
        FAILS[section] = FAILS.get(section, 0) + 1
        if len(FAIL_MSGS) < 20:
            FAIL_MSGS.append(f"[{section}] {msg}")


# ---------------------------------------------------------------- reduction
def canon(contacts):
    return sorted((min(u, v), max(u, v)) for u, v in contacts)


def cmp(x, y):
    return (x > y) - (x < y)


def reduce(n1, E1, n2, E2):
    """Return (candidates, adjacency) where candidates[p] = (i, j, assignments)."""
    E1, E2 = list(E1), list(E2)
    cands = []
    for i, (u, v) in enumerate(E1):
        for j, (a, b) in enumerate(E2):
            cands.append((i, j, ((u, a), (v, b))))
    N = len(cands)
    edges = set()
    for p in range(N):
        for q in range(p + 1, N):
            ok = all(
                cmp(s, s2) == cmp(t, t2)
                for (s, t) in cands[p][2]
                for (s2, t2) in cands[q][2]
            )
            if ok:
                edges.add((p, q))
    return cands, edges


def extract(n1, n2, cands, edges, sel):
    """sel: set of selected vertices; returns None if not a clique."""
    sel = sorted(sel)
    for x, y in itertools.combinations(sel, 2):
        if (x, y) not in edges:
            return None
    cfg = [0] * n1
    for p in sel:
        for (s, t) in cands[p][2]:
            cfg[s] = t + 1
    return cfg


def extract_bits(n1, n2, cands, edges, bits):
    """Target-shaped witness (Vec<bool> of length N); None if wrong length or not a clique."""
    if len(bits) != len(cands):
        return None
    return extract(n1, n2, cands, edges, {p for p, b in enumerate(bits) if b})


# ------------------------------------------------------- source semantics
def feasible(cfg, n2):
    nz = [c for c in cfg if c != 0]
    return all(0 <= c <= n2 for c in cfg) and all(a < b for a, b in zip(nz, nz[1:]))


def score(cfg, E1, E2):
    S2 = set(E2)
    cnt = 0
    for (u, v) in E1:
        if cfg[u] and cfg[v]:
            a, b = cfg[u] - 1, cfg[v] - 1
            if (min(a, b), max(a, b)) in S2:
                cnt += 1
    return cnt


def all_alignments(n1, n2):
    """Every feasible config, by choosing the matched subset and an increasing image."""
    out = []
    for k in range(0, min(n1, n2) + 1):
        for src in itertools.combinations(range(n1), k):
            for img in itertools.combinations(range(n2), k):
                cfg = [0] * n1
                for s, t in zip(src, img):
                    cfg[s] = t + 1
                out.append(cfg)
    return out


_ALIGN_CACHE = {}


def alignments(n1, n2):
    if (n1, n2) not in _ALIGN_CACHE:
        _ALIGN_CACHE[(n1, n2)] = all_alignments(n1, n2)
    return _ALIGN_CACHE[(n1, n2)]


def max_clique_size(N, edges):
    adj = [set() for _ in range(N)]
    for x, y in edges:
        adj[x].add(y)
        adj[y].add(x)
    best = 0

    def bk(R, P, X):
        nonlocal best
        if not P and not X:
            best = max(best, R)
            return
        if R + len(P) <= best:
            return
        u = max(P | X, key=lambda w: len(adj[w] & P))
        for v in list(P - adj[u]):
            bk(R + 1, P & adj[v], X & adj[v])
            P = P - {v}
            X = X | {v}

    bk(0, set(range(N)), set())
    return best


def all_cliques(N, edges):
    """Yield every clique (as a tuple) including the empty one."""
    adj = [set() for _ in range(N)]
    for x, y in edges:
        adj[x].add(y)
        adj[y].add(x)

    def rec(cur, cand):
        yield tuple(cur)
        for v in sorted(cand):
            yield from rec(cur + [v], {w for w in cand if w > v and w in adj[v]})

    yield from rec([], set(range(N)))


# ------------------------------------------------------------ instance sets
def contact_sets(n, max_m=None):
    pairs = list(itertools.combinations(range(n), 2))
    top = len(pairs) if max_m is None else min(max_m, len(pairs))
    for m in range(top + 1):
        for c in itertools.combinations(pairs, m):
            yield list(c)


def instances_exhaustive():
    # all contact-set pairs through 3 vertices (both sides), n >= 1
    for n1 in range(1, 4):
        for n2 in range(1, 4):
            for E1 in contact_sets(n1):
                for E2 in contact_sets(n2):
                    yield n1, E1, n2, E2
    # up to 5 vertices, at most 2 contacts on each side
    for n1 in range(1, 6):
        for n2 in range(1, 6):
            if n1 <= 3 and n2 <= 3:
                continue
            for E1 in contact_sets(n1, 2):
                for E2 in contact_sets(n2, 2):
                    yield n1, E1, n2, E2


def sampled_instances(count, nmax=7, mmax=6):
    for _ in range(count):
        n1, n2 = random.randint(2, nmax), random.randint(2, nmax)
        P1 = list(itertools.combinations(range(n1), 2))
        P2 = list(itertools.combinations(range(n2), 2))
        m1, m2 = random.randint(0, min(mmax, len(P1))), random.randint(0, min(mmax, len(P2)))
        yield n1, sorted(random.sample(P1, m1)), n2, sorted(random.sample(P2, m2))


# ------------------------------------------------------ per-instance checks
def check_instance(n1, E1, n2, E2, full_cliques=True):
    m1, m2 = len(E1), len(E2)
    cands, edges = reduce(n1, E1, n2, E2)
    N = len(cands)
    als = alignments(n1, n2)
    scores = {tuple(c): score(c, E1, E2) for c in als}
    opt = max(scores.values())

    # --- section 4: size vs formula
    check("4 size", N == m1 * m2, f"vertices {N} != {m1*m2}")
    check("4 size", len(edges) <= N * (N - 1) // 2, "edges above bound")

    # --- section 5: well-formedness
    check("5 structure", all(x < y < N for x, y in edges), "edge not a normalised pair")
    for (i, j, A) in cands:
        (s1, t1), (s2, t2) = A
        check("5 structure", s1 < s2 and t1 < t2, "candidate not locally increasing")
        check("5 structure", 0 <= t1 < t2 < n2 and 0 <= s1 < s2 < n1, "candidate out of range")
    index = {(c[0], c[1]) for c in cands}
    check("5 structure", len(index) == N, "duplicate candidate")
    check("5 structure", [(c[0], c[1]) for c in cands] ==
          [(i, j) for i in range(m1) for j in range(m2)], "candidate order")
    # largest clique bounded by min(m1, m2)
    mc = max_clique_size(N, edges)
    check("5 structure", mc <= min(m1, m2), "clique exceeds min(m1,m2)")

    # --- section 2: optimum preserved both ways
    # forward: every alignment yields a clique of size = score
    for c in als:
        sel = []
        for ci, (u, v) in enumerate(E1):
            if c[u] and c[v]:
                a, b = c[u] - 1, c[v] - 1
                if (a, b) in set(E2):
                    sel.append(ci * m2 + E2.index((a, b)))
        ok = all((x, y) in edges for x, y in itertools.combinations(sorted(sel), 2))
        check("2 forward/backward", ok and len(sel) == scores[tuple(c)],
              f"forward clique fails {n1,E1,n2,E2,c}")
    check("2 forward/backward", mc == opt, f"opt {opt} != max clique {mc} on {n1,E1,n2,E2}")

    # --- section 3: enumerator self-test, rejection of non-cliques and wrong lengths
    if N <= 10:
        brute = set()
        for mask in range(1 << N):
            sel = [p for p in range(N) if mask >> p & 1]
            if all((x, y) in edges for x, y in itertools.combinations(sel, 2)):
                brute.add(tuple(sel))
        enum = list(all_cliques(N, edges))
        check("3 extraction", set(enum) == brute and len(enum) == len(brute),
              "clique enumerator disagrees with brute force")
    nonedges = [(x, y) for x in range(N) for y in range(x + 1, N) if (x, y) not in edges]
    for (x, y) in nonedges[:25]:
        bits = [p in (x, y) for p in range(N)]
        check("3 extraction", extract_bits(n1, n2, cands, edges, bits) is None,
              "non-clique accepted")
        bits2 = [True] * N
        check("3 extraction", extract_bits(n1, n2, cands, edges, bits2) is None,
              "all-ones non-clique accepted")
    for bad in (N + 1, max(N - 1, 0)) if N > 0 else (1,):
        check("3 extraction", extract_bits(n1, n2, cands, edges, [False] * bad) is None,
              "wrong length accepted")
    check("3 extraction", extract_bits(n1, n2, cands, edges, [False] * N) == [0] * n1,
          "empty selection")

    # --- section 3: extraction from every clique
    if full_cliques:
        for K in all_cliques(N, edges):
            cfg = extract(n1, n2, cands, edges, K)
            check("3 extraction", cfg is not None, "clique rejected")
            check("3 extraction", feasible(cfg, n2), f"extracted infeasible {cfg}")
            sc = scores[tuple(cfg)]
            check("3 extraction", sc >= len(K), f"score {sc} < size {len(K)}")
            if len(K) == mc:
                check("3 extraction", sc == opt, "maximum clique decodes to non-optimal")
        # non-edges are rejected
    else:
        for K in [()]:
            check("3 extraction", extract(n1, n2, cands, edges, K) == [0] * n1, "empty clique")
    return mc, opt


# ------------------------------------------------------------------ sections
def section1():
    """Tie the declared overhead formulas to the construction's own index structure."""
    m1, m2 = sp.symbols("m1 m2", integer=True, nonnegative=True)
    declared_v = m1 * m2
    declared_e = m1 * m2 * (m1 * m2 - 1) / 2
    sharp = 2 * sp.binomial(m1, 2) * sp.binomial(m2, 2)
    # Symbolic: pairs of vertices minus pairs sharing a contact index equals the sharp bound
    shares_i = m1 * sp.binomial(m2, 2)
    shares_j = m2 * sp.binomial(m1, 2)
    check("1 symbolic",
          sp.simplify(sp.expand_func(declared_e - shares_i - shares_j - sharp)) == 0,
          "C(N,2) - sharing pairs != sharp bound")
    P = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3), (0, 4), (1, 4)]
    for a_ in range(0, 7):
        for b_ in range(0, 7):
            c, e = reduce(5, P[:a_], 5, P[:b_])
            vs = {m1: a_, m2: b_}
            # measured |V| and |E| against the declared formulas, from the built target
            check("1 symbolic", len(c) == int(declared_v.subs(vs)), f"|V| {a_},{b_}")
            check("1 symbolic", len(e) <= declared_e.subs(vs), f"|E| bound {a_},{b_}")
            check("1 symbolic", len(e) <= sharp.subs(vs), f"|E| sharp {a_},{b_}")
            # the construction's own index structure: vertices differing in both indices
            both = sum(1 for p in range(len(c)) for q in range(p + 1, len(c))
                       if c[p][0] != c[q][0] and c[p][1] != c[q][1])
            check("1 symbolic", both == sharp.subs(vs), f"index pairs {a_},{b_}")
            check("1 symbolic", all(c[p][0] != c[q][0] and c[p][1] != c[q][1] for p, q in e),
                  "edge between vertices sharing an index")


def section_exhaustive():
    n = 0
    for inst in instances_exhaustive():
        n += 1
        check_instance(*inst)
    return n


def section_sampled():
    for inst in sampled_instances(400):
        check_instance(*inst, full_cliques=True)
    # larger: only max clique vs optimum plus all-alignment forward check
    for inst in sampled_instances(60, nmax=7, mmax=12):
        check_instance(*inst, full_cliques=False)


def section_edge_exactness():
    """Section 4: the edge count is only a bound; show it can be strict and attained."""
    c, e = reduce(4, [(0, 2), (1, 3), (0, 3)], 4, [(0, 1), (2, 3), (0, 3)])
    check("4 size", len(c) == 9 and len(e) == 2 and len(e) < 36, "issue example size")
    # complete case: m1 = m2 = 1, N = 1, bound 0 attained
    c, e = reduce(2, [(0, 1)], 2, [(0, 1)])
    check("4 size", len(c) == 1 and len(e) == 0, "single candidate")
    # empty side -> empty graph
    c, e = reduce(3, [], 3, [(0, 1)])
    check("4 size", len(c) == 0 and len(e) == 0, "empty E1")
    c, e = reduce(3, [(0, 1)], 3, [])
    check("4 size", len(c) == 0 and len(e) == 0, "empty E2")
    # attained bound: identical single-chain maps give compatible cliques; find max edges ratio
    # Derivation: two candidates sharing a source contact or a target contact are never
    # adjacent, so edges <= C(N,2) - m1*C(m2,2) - m2*C(m1,2) = 2*C(m1,2)*C(m2,2).
    # Hence C(N,2) is attained only for N <= 1; test strictness and the sharper bound.
    attained = tight = 0
    for n1, E1, n2, E2 in instances_exhaustive():
        if n1 > 3 or n2 > 3:
            break
        c, e = reduce(n1, E1, n2, E2)
        N, m1, m2 = len(c), len(E1), len(E2)
        cn = lambda x: x * (x - 1) // 2
        sharper = 2 * cn(m1) * cn(m2)
        check("4 size", len(e) <= sharper, "sharper bound violated")
        check("4 size", N <= 1 or len(e) < cn(N), "C(N,2) attained with N >= 2")
        check("4 size", sharper == cn(N) - m1 * cn(m2) - m2 * cn(m1), "identity")
        attained += len(e) == cn(N)
        tight += len(e) == sharper
    return attained, tight


def section_special_cases():
    # n1 > n2: no special case, alignment partial
    n1, E1, n2, E2 = 5, [(0, 4), (1, 2), (3, 4)], 2, [(0, 1)]
    mc, opt = check_instance(n1, E1, n2, E2)
    check("5 structure", mc == opt == 1)
    # shared source endpoints must agree
    c, e = reduce(3, [(0, 1), (0, 2)], 3, [(0, 1), (0, 2), (1, 2)])
    pid = {(x[0], x[1]): p for p, x in enumerate(c)}
    check("5 structure", (pid[(0, 0)], pid[(1, 2)]) not in e and (pid[(0, 0)], pid[(1, 0)]) not in e
          and (pid[(0, 0)], pid[(1, 1)]) in e)
    # disjoint source contacts cannot reuse target endpoints
    c, e = reduce(4, [(0, 1), (2, 3)], 3, [(0, 1), (1, 2)])
    pid = {(x[0], x[1]): p for p, x in enumerate(c)}
    check("5 structure", (pid[(0, 0)], pid[(1, 1)]) not in e)
    # crossing assignments excluded
    c, e = reduce(4, [(0, 2), (1, 3)], 4, [(0, 3), (1, 2)])
    pid = {(x[0], x[1]): p for p, x in enumerate(c)}
    check("5 structure", (pid[(0, 0)], pid[(1, 1)]) not in e)
    # witness-value caveat: nonmaximum clique decodes to higher score
    n1, E1, n2, E2 = 3, [(0, 1), (1, 2), (0, 2)], 3, [(0, 1), (1, 2), (0, 2)]
    c, e = reduce(n1, E1, n2, E2)
    p01 = [p for p, x in enumerate(c) if (x[0], x[1]) == (0, 0)][0]
    p12 = [p for p, x in enumerate(c) if (x[0], x[1]) == (1, 1)][0]
    cfg = extract(n1, n2, c, e, {p01, p12})
    check("3 extraction", cfg == [1, 2, 3], f"cfg {cfg}")
    check("3 extraction", score(cfg, E1, E2) == 3 > 2, "caveat: score should exceed clique size")
    check("3 extraction", max_clique_size(len(c), e) == 3)
    # non-clique selection is rejected, wrong-order pair is not an edge
    nonedge = next((x, y) for x in range(len(c)) for y in range(x + 1, len(c)) if (x, y) not in e)
    check("3 extraction", extract(3, 3, c, e, set(nonedge)) is None, "non-clique accepted")
    # an unordered (graph-isomorphism) construction would overestimate: mutation test
    ex = (4, [(0, 2), (1, 3), (0, 3)], 4, [(0, 1), (2, 3), (0, 3)])
    check("7 NO", mutant_unordered_opt(*ex) == 3, "mutant should overestimate (detects order loss)")


def mutant_unordered_opt(n1, E1, n2, E2):
    """Deliberately wrong: injective partial maps with no order restriction."""
    S2 = set(E2)
    best = 0
    for k in range(0, min(n1, n2) + 1):
        for src in itertools.combinations(range(n1), k):
            for img in itertools.permutations(range(n2), k):
                f = dict(zip(src, img))
                sc = sum(1 for u, v in E1 if u in f and v in f
                         and (min(f[u], f[v]), max(f[u], f[v])) in S2)
                best = max(best, sc)
    return best


def section_yes_no():
    n1, E1, n2, E2 = 4, [(0, 2), (1, 3), (0, 3)], 4, [(0, 1), (2, 3), (0, 3)]
    c, e = reduce(n1, E1, n2, E2)
    expect_assign = [
        ((0, 0), (2, 1)), ((0, 2), (2, 3)), ((0, 0), (2, 3)),
        ((1, 0), (3, 1)), ((1, 2), (3, 3)), ((1, 0), (3, 3)),
        ((0, 0), (3, 1)), ((0, 2), (3, 3)), ((0, 0), (3, 3)),
    ]
    check("6 YES", [x[2] for x in c] == expect_assign, "nine candidates")
    check("6 YES", sorted(e) == [(0, 8), (4, 8)], f"edges {sorted(e)}")
    check("6 YES", len(c) == 9 and len(e) == 2)
    cfg = extract(n1, n2, c, e, {0, 8})
    check("6 YES", cfg == [1, 0, 2, 4], f"cfg {cfg}")
    check("6 YES", feasible(cfg, n2) and score(cfg, E1, E2) == 2)
    check("6 YES", extract(n1, n2, c, e, {0, 4}) is None, "{0,4} is not a clique")
    check("6 YES", extract(n1, n2, c, e, {0}) == [1, 0, 2, 0])
    check("6 YES", extract(n1, n2, c, e, {4}) == [0, 3, 0, 4])
    check("6 YES", extract(n1, n2, c, e, set()) == [0, 0, 0, 0])
    check("6 YES", max_clique_size(9, e) == 2)
    check("6 YES", max(score(a, E1, E2) for a in alignments(4, 4)) == 2)
    # NO: threshold 3 unreachable on both sides
    check("7 NO", max_clique_size(9, e) < 3, "target has a clique of size 3")
    check("7 NO", all(score(a, E1, E2) < 3 for a in alignments(4, 4)), "source reaches 3")
    check("7 NO", not any(len(K) >= 3 for K in all_cliques(9, e)), "3-clique exists")
    # all-three-contacts alignment would have to be total => identity
    tot = [a for a in alignments(4, 4) if all(a)]
    check("7 NO", tot == [[1, 2, 3, 4]], "total alignments")
    check("7 NO", score([1, 2, 3, 4], E1, E2) == 1)
    # unordered graphs are isomorphic paths yet overlap is 2
    check("7 NO", mutant_unordered_opt(n1, E1, n2, E2) == 3)
    # infeasible target config: wrong-length / out-of-range / non-increasing configs
    check("7 NO", not feasible([2, 1, 0, 0], 4) and not feasible([1, 5, 0, 0], 4))
    check("7 NO", not feasible([1, 1, 0, 0], 4))


def section_benchmark():
    root = os.path.expanduser("<benchmark>/MaximumContactMapOverlap")
    if not os.path.isdir(root):
        return 0
    n = 0
    for fn in sorted(os.listdir(root)):
        if not fn.endswith(".json"):
            continue
        d = json.load(open(os.path.join(root, fn)))["data"]
        n1, n2 = d["num_vertices_1"], d["num_vertices_2"]
        E1, E2 = canon(d["contacts_1"]), canon(d["contacts_2"])
        cands, edges = reduce(n1, E1, n2, E2)
        N = len(cands)
        mc = max_clique_size(N, edges)
        opt = max(score(a, E1, E2) for a in alignments(n1, n2))
        check("2 forward/backward", N == len(E1) * len(E2) and mc == opt,
              f"{fn}: clique {mc} vs opt {opt}")
        check("4 size", len(edges) <= N * (N - 1) // 2)
        n += 1
    return n


# ---------------------------------------------------------------- hypothesis
@st.composite
def instance_small(draw):
    n1, n2 = draw(st.integers(1, 6)), draw(st.integers(1, 6))
    P1 = list(itertools.combinations(range(n1), 2))
    P2 = list(itertools.combinations(range(n2), 2))
    E1 = draw(st.lists(st.sampled_from(P1), unique=True, max_size=4)) if P1 else []
    E2 = draw(st.lists(st.sampled_from(P2), unique=True, max_size=4)) if P2 else []
    return n1, sorted(E1), n2, sorted(E2)


@st.composite
def instance_dense_equal(draw):
    n = draw(st.integers(3, 6))
    P = list(itertools.combinations(range(n), 2))
    E1 = draw(st.lists(st.sampled_from(P), unique=True, min_size=1, max_size=5))
    # target = noisy order-preserving copy of source under a random increasing map
    img = sorted(draw(st.lists(st.integers(0, n + 1), unique=True, min_size=n, max_size=n)))
    E2 = {(img[u], img[v]) for u, v in E1}
    extra = draw(st.lists(st.sampled_from(list(itertools.combinations(range(n + 2), 2))),
                          unique=True, max_size=2))
    E2 |= set(extra)
    return n, sorted(E1), n + 2, sorted(E2)


def section_hypothesis():
    prof = dict(max_examples=250, deadline=None, derandomize=True,
                suppress_health_check=list(HealthCheck))

    @settings(**prof)
    @given(instance_small())
    def t1(inst):
        check_instance(*inst, full_cliques=True)

    @settings(**prof)
    @given(instance_dense_equal())
    def t2(inst):
        n1, E1, n2, E2 = inst
        mc, opt = check_instance(*inst, full_cliques=False)
        # planted copy: optimum at least |E1|
        check("2 forward/backward", opt >= len(E1), "planted alignment not found")

    t1()
    t2()


# ---------------------------------------------------------------------- main
def main():
    section1()
    n_ex = section_exhaustive()
    section_sampled()
    attained = section_edge_exactness()
    section_special_cases()
    section_yes_no()
    n_benchmark = section_benchmark()
    section_hypothesis()

    print(f"exhaustive instances: {n_ex}; benchmark instances: {n_benchmark}; "
          f"instances attaining C(N,2): {attained[0]}, attaining 2C(m1,2)C(m2,2): {attained[1]}")
    names = ["1 symbolic", "2 forward/backward", "3 extraction", "4 size",
             "5 structure", "6 YES", "7 NO"]
    for s in names:
        print(f"  section {s:<20} checks={COUNTS.get(s, 0):>9} failures={FAILS.get(s, 0)}")
        if COUNTS.get(s, 0) == 0:
            FAILS[s] = FAILS.get(s, 0) + 1  # an empty section is a failure
    for m in FAIL_MSGS:
        print("FAIL", m)
    total = sum(COUNTS.values())
    fails = sum(FAILS.values())
    print(f"TOTAL: {total} checks, {fails} failures")
    return 1 if fails else 0


if __name__ == "__main__":
    sys.exit(main())
Independent check script (Python)
#!/usr/bin/env python3
"""Independent check for MaximumContactMapOverlap -> MaximumClique<SimpleGraph, One>.

Written from the proof alone (the constructor script was not read before
this file was written).  Implements the construction, the extraction and both
feasibility checkers from scratch, then attacks every claim of the proof:
vertex count, both edge bounds, the "edges only between pairs differing in both
indices" lemma, (=>) and (<=) directions, omega <= min(m1, m2), extraction
validity, sigma(f) >= |K| for every clique, optimality of every maximum clique,
rejection of non-cliques and wrong lengths, both worked examples and the
triangle remark.

Usage:  python3 independent check.py            (full run, prints audit and TOTAL line)
        python3 independent check.py --quick    (reduced run, used by mutants.py)

The module exposes `run(reduce_fn, extract_fn, quick)` so that mutants.py can
inject broken constructions.
"""
from __future__ import annotations

import itertools
import json
import os
import random
import sys
from collections import Counter
from math import comb

BENCH = "<benchmark>/MaximumContactMapOverlap"
SEED = 1205
CLIQUE_CAP = 200_000
SHARP_FACTOR = 2  # the proof's sharper edge bound is SHARP_FACTOR * C(m1,2) * C(m2,2)


# --------------------------------------------------------------------------
# Construction and extraction (from the proof, independently written)
# --------------------------------------------------------------------------

def sgn(x: int) -> int:
    return (x > 0) - (x < 0)


def assignment_sets(inst):
    """A_{ij} = {(u,a),(v,b)} for e_i=(u,v), d_j=(a,b), index i*m2+j."""
    _n1, E1, _n2, E2 = inst
    return [((u, a), (v, b)) for (u, v) in E1 for (a, b) in E2]


def compatible(Ap, Aq) -> bool:
    for (s, t) in Ap:
        for (s2, t2) in Aq:
            if sgn(s - s2) != sgn(t - t2):
                return False
    return True


def reduce(inst):
    """Return (num_vertices, frozenset of edges (p,q) with p<q)."""
    A = assignment_sets(inst)
    N = len(A)
    edges = set()
    for p in range(N):
        for q in range(p + 1, N):
            if compatible(A[p], A[q]):
                edges.add((p, q))
    return N, frozenset(edges)


class ExtractionError(ValueError):
    pass


def extract(inst, target, sel):
    """Selection (list of bools of length N) -> source config; error if not a clique."""
    n1, E1, n2, E2 = inst
    N, edges = target
    if len(sel) != N:
        raise ExtractionError("wrong length")
    K = [p for p, x in enumerate(sel) if x]
    for p, q in itertools.combinations(K, 2):
        if (p, q) not in edges:
            raise ExtractionError("not a clique")
    A = assignment_sets(inst)
    f = [0] * n1
    for p in K:
        for (s, t) in A[p]:
            f[s] = t + 1
    return f


# --------------------------------------------------------------------------
# Independent oracles
# --------------------------------------------------------------------------

def source_feasible(inst, f) -> bool:
    n1, _E1, n2, _E2 = inst
    if len(f) != n1:
        return False
    if any((not isinstance(x, int)) or x < 0 or x > n2 for x in f):
        return False
    nz = [x for x in f if x]
    return all(a < b for a, b in zip(nz, nz[1:]))


def score(inst, f) -> int:
    _n1, E1, _n2, E2 = inst
    S2 = set(E2)
    c = 0
    for (u, v) in E1:
        if f[u] and f[v] and (f[u] - 1, f[v] - 1) in S2:
            c += 1
    return c


def alignments(n1, n2):
    """All increasing partial injections, as configs."""
    for k in range(min(n1, n2) + 1):
        for src in itertools.combinations(range(n1), k):
            for dst in itertools.combinations(range(n2), k):
                f = [0] * n1
                for s, t in zip(src, dst):
                    f[s] = t + 1
                yield f


def adjacency(target):
    N, edges = target
    adj = [0] * N
    for p, q in edges:
        adj[p] |= 1 << q
        adj[q] |= 1 << p
    return adj


def all_cliques(target):
    """Every clique (including empty) as a sorted tuple."""
    N, _ = target
    adj = adjacency(target)
    out = []

    def ext(cur, cand):
        out.append(tuple(cur))
        if len(out) > CLIQUE_CAP:
            raise RuntimeError("clique enumeration exceeded cap (graph far denser than the bound allows)")
        while cand:
            low = cand & -cand
            v = low.bit_length() - 1
            cand ^= low
            ext(cur + [v], cand & adj[v])

    ext([], (1 << N) - 1)
    return out


def is_clique(target, K) -> bool:
    _N, edges = target
    return all((p, q) in edges for p, q in itertools.combinations(sorted(K), 2))


def omega_bruteforce(target) -> int:
    """Subset brute force, independent of all_cliques (only for tiny N)."""
    N, _ = target
    best = 0
    for r in range(N, 0, -1):
        if r <= best:
            break
        for K in itertools.combinations(range(N), r):
            if is_clique(target, K):
                return r
    return best


# --------------------------------------------------------------------------
# Check machinery
# --------------------------------------------------------------------------

class Audit:
    def __init__(self):
        self.count = Counter()
        self.fail = Counter()
        self.first = {}

    def check(self, name, ok, ctx=None):
        self.count[name] += 1
        if not ok:
            self.fail[name] += 1
            self.first.setdefault(name, ctx)
        return ok


def safe(fn, *a):
    try:
        return fn(*a), None
    except ExtractionError as e:
        return None, e
    except Exception as e:  # a mutant may crash; a crash is a failure, not an abort
        return None, e


def check_instance(audit, inst, reduce_fn, extract_fn, rng, full=True):
    n1, E1, n2, E2 = inst
    m1, m2 = len(E1), len(E2)
    try:
        target = reduce_fn(inst)
    except Exception as e:
        audit.check("reduce_runs", False, (inst, repr(e)))
        return
    audit.check("reduce_runs", True)
    N, edges = target
    # --- overhead / structure
    audit.check("vertices_exact_m1m2", N == m1 * m2, inst)
    audit.check("edges_le_C(N,2)", len(edges) <= comb(N, 2), inst)
    audit.check("edges_le_2C(m1,2)C(m2,2)", len(edges) <= SHARP_FACTOR * comb(m1, 2) * comb(m2, 2), inst)
    audit.check("C(N,2)_not_attained_for_N>=2", N < 2 or len(edges) < comb(N, 2), inst)
    audit.check("edges_well_formed",
                all(0 <= p < q < N for p, q in edges), inst)
    if m2:
        audit.check("edges_differ_in_both_indices",
                    all(p // m2 != q // m2 and p % m2 != q % m2 for p, q in edges), inst)
    # --- (=>): every alignment yields a clique of size sigma(f)
    idx2 = {d: j for j, d in enumerate(E2)}
    opt = 0
    for f in alignments(n1, n2):
        sig = score(inst, f)
        opt = max(opt, sig)
        if not full and rng.random() > 0.3:
            continue
        K = [i * m2 + idx2[(f[u] - 1, f[v] - 1)]
             for i, (u, v) in enumerate(E1)
             if f[u] and f[v] and (f[u] - 1, f[v] - 1) in idx2]
        audit.check("forward_alignment_gives_clique",
                    len(set(K)) == sig and all(k < N for k in K) and is_clique(target, K),
                    (inst, f))
    # --- cliques
    try:
        cl = all_cliques(target)
    except RuntimeError as e:
        audit.check("clique_enumeration_tractable", False, (inst, str(e)))
        return
    audit.check("clique_enumeration_tractable", True)
    omega = max(len(K) for K in cl)
    audit.check("omega_equals_OPT", omega == opt, (inst, omega, opt))
    audit.check("omega_le_min(m1,m2)", omega <= min(m1, m2), inst)
    if N <= 12:
        audit.check("omega_matches_subset_bruteforce", omega_bruteforce(target) == omega, inst)
    for K in cl:
        if m2:
            audit.check("clique_distinct_source_and_target_contacts",
                        len({p // m2 for p in K}) == len(K) and len({p % m2 for p in K}) == len(K),
                        (inst, K))
        sel = [False] * N
        for p in K:
            sel[p] = True
        f, err = safe(extract_fn, inst, target, sel)
        if not audit.check("extract_clique_accepted", err is None, (inst, K, repr(err))):
            continue
        if len(K) == 0:
            audit.check("empty_clique_all_unmatched", f == [0] * n1, (inst, f))
        feas = source_feasible(inst, f)
        audit.check("extract_feasible", feas, (inst, K, f))
        if not feas:
            continue
        sig = score(inst, f)
        audit.check("extract_score_ge_clique_size", sig >= len(K), (inst, K, f))
        if len(K) == omega:
            audit.check("max_clique_extracts_optimum", sig == opt, (inst, K, f, opt))
    # --- rejection
    _, err = safe(extract_fn, inst, target, [False] * (N + 1))
    audit.check("extract_rejects_long", err is not None, inst)
    if N:
        _, err = safe(extract_fn, inst, target, [False] * (N - 1))
        audit.check("extract_rejects_short", err is not None, inst)
    noncliques = []
    for p in range(N):
        for q in range(p + 1, N):
            if (p, q) not in edges:
                noncliques.append((p, q))
    rng.shuffle(noncliques)
    for p, q in noncliques[:5]:
        sel = [False] * N
        sel[p] = sel[q] = True
        # widen with a random extra vertex sometimes
        if N > 2 and rng.random() < 0.5:
            sel[rng.randrange(N)] = True
        _, err = safe(extract_fn, inst, target, sel)
        audit.check("extract_rejects_nonclique", err is not None, (inst, p, q))


# --------------------------------------------------------------------------
# Instance families
# --------------------------------------------------------------------------

def contact_sets(n):
    pairs = list(itertools.combinations(range(n), 2))
    for mask in range(1 << len(pairs)):
        yield tuple(pairs[k] for k in range(len(pairs)) if mask >> k & 1)


def exhaustive(quick):
    """All contact-set pairs with n1,n2 <= 4; plus n=5 on one side x n<=3 on the other."""
    lim = 3 if quick else 4
    for n1 in range(lim + 1):
        for n2 in range(lim + 1):
            for E1 in contact_sets(n1):
                for E2 in contact_sets(n2):
                    yield (n1, E1, n2, E2)
    if quick:
        return
    for n5 in [5]:
        for n_small in range(4):
            for E5 in contact_sets(n5):
                for Es in contact_sets(n_small):
                    yield (n5, E5, n_small, Es)
                    yield (n_small, Es, n5, E5)


def shuffled(inst, rng):
    n1, E1, n2, E2 = inst
    E1, E2 = list(E1), list(E2)
    rng.shuffle(E1)
    rng.shuffle(E2)
    return (n1, tuple(E1), n2, tuple(E2))


def load_benchmark():
    out = []
    if not os.path.isdir(BENCH):
        return out
    for name in sorted(os.listdir(BENCH)):
        d = json.load(open(os.path.join(BENCH, name)))["data"]
        out.append((name, (d["num_vertices_1"], tuple(tuple(sorted(e)) for e in d["contacts_1"]),
                           d["num_vertices_2"], tuple(tuple(sorted(e)) for e in d["contacts_2"]))))
    return out


# --------------------------------------------------------------------------
# Worked examples
# --------------------------------------------------------------------------

YES = (4, ((0, 2), (1, 3), (0, 3)), 4, ((0, 1), (2, 3), (0, 3)))
TRIANGLE = (3, ((0, 1), (0, 2), (1, 2)), 3, ((0, 1), (0, 2), (1, 2)))


def check_examples(audit, reduce_fn, extract_fn):
    inst = YES
    t = reduce_fn(inst)
    N, edges = t
    audit.check("yes_num_vertices_9", N == 9)
    table = [((0, 0), (2, 1)), ((0, 2), (2, 3)), ((0, 0), (2, 3)),
             ((1, 0), (3, 1)), ((1, 2), (3, 3)), ((1, 0), (3, 3)),
             ((0, 0), (3, 1)), ((0, 2), (3, 3)), ((0, 0), (3, 3))]
    audit.check("yes_assignment_table", assignment_sets(inst) == table)
    audit.check("yes_edges_[(0,8),(4,8)]", sorted(edges) == [(0, 8), (4, 8)], sorted(edges))
    audit.check("yes_bounds_36_18", comb(9, 2) == 36 and SHARP_FACTOR * comb(3, 2) ** 2 == 18 and len(edges) <= 18)
    audit.check("yes_0_4_not_adjacent", (0, 4) not in edges)

    def ex(K):
        sel = [p in K for p in range(N)]
        f, err = safe(extract_fn, inst, t, sel)
        return f
    f = ex({0, 8})
    audit.check("yes_clique_0_8_extracts_[1,0,2,4]", f == [1, 0, 2, 4], f)
    audit.check("yes_score_2", f is not None and score(inst, f) == 2)
    opt = max(score(inst, g) for g in alignments(4, 4))
    audit.check("yes_optimum_2", opt == 2, opt)
    audit.check("yes_clique_0", ex({0}) == [1, 0, 2, 0] and score(inst, [1, 0, 2, 0]) == 1)
    audit.check("yes_clique_4", ex({4}) == [0, 3, 0, 4] and score(inst, [0, 3, 0, 4]) == 1)
    audit.check("yes_empty", ex(set()) == [0, 0, 0, 0])
    # oracle self-tests (so a broken oracle cannot hide a broken construction)
    audit.check("oracle_feasibility_sanity",
                source_feasible(inst, [0, 0, 0, 0]) and source_feasible(inst, [1, 2, 3, 4])
                and not source_feasible(inst, [2, 1, 0, 0]) and not source_feasible(inst, [1, 1, 0, 0])
                and not source_feasible(inst, [1, 5, 0, 0]) and not source_feasible(inst, [1, 0, 0]))
    audit.check("oracle_clique_enumeration_sanity",
                len(all_cliques((3, frozenset({(0, 1), (0, 2), (1, 2)})))) == 8
                and len(all_cliques(t)) == 1 + N + len(edges))
    # NO example: threshold 3
    cl = all_cliques(t)  # cap raises on a blown-up target; run() records it as a failure
    audit.check("no_no_3_clique", max(len(K) for K in cl) == 2)
    audit.check("no_no_alignment_score_3", all(score(inst, g) < 3 for g in alignments(4, 4)))
    audit.check("no_identity_scores_1", score(inst, [1, 2, 3, 4]) == 1)
    # order-ignoring overlap would be 3 (the two contact graphs are isomorphic)
    best_unordered = 0
    for perm in itertools.permutations(range(4)):
        f = [p + 1 for p in perm]
        s = sum(1 for (u, v) in inst[1] if tuple(sorted((perm[u], perm[v]))) in set(inst[3]))
        best_unordered = max(best_unordered, s)
    audit.check("no_order_ignoring_would_give_3", best_unordered == 3)
    # triangle remark: {p00, p11} extracts [1,2,3] with score 3 > 2
    tt = reduce_fn(TRIANGLE)
    Nt = tt[0]
    sel = [p in (0, 4) for p in range(Nt)]
    f, err = safe(extract_fn, TRIANGLE, tt, sel)
    audit.check("triangle_p00_p11_extracts_[1,2,3]_score_3",
                err is None and f == [1, 2, 3] and score(TRIANGLE, f) == 3, (f, err))


# --------------------------------------------------------------------------
# Hypothesis strategies aimed at the special cases
# --------------------------------------------------------------------------

def run_hypothesis(audit, reduce_fn, extract_fn, max_examples):
    from hypothesis import given, settings, strategies as st, HealthCheck

    rng = random.Random(SEED + 7)
    common = settings(max_examples=max_examples, deadline=None, derandomize=True,
                      suppress_health_check=list(HealthCheck), database=None)

    def side(draw, n, max_m, kind):
        pairs = list(itertools.combinations(range(n), 2))
        if not pairs:
            return ()
        if kind == "dense":
            E = draw(st.lists(st.sampled_from(pairs), unique=True, max_size=max_m))
        elif kind == "star":  # many contacts sharing endpoints (equality preservation)
            hubs = draw(st.lists(st.integers(0, n - 1), min_size=1, max_size=2, unique=True))
            E = [p for p in pairs if p[0] in hubs or p[1] in hubs]
            E = draw(st.permutations(E))[:max_m]
        elif kind == "arcs":  # crossing / nested arcs, protein-like backbone
            E = [(i, i + 1) for i in range(n - 1)] if draw(st.booleans()) else []
            extra = draw(st.lists(st.sampled_from(pairs), unique=True, max_size=3))
            E = list(dict.fromkeys(E + extra))[:max_m]
        else:
            E = []
        return tuple(draw(st.permutations(E)))

    @st.composite
    def cmo(draw, kinds, n_range):
        n1 = draw(st.integers(*n_range))
        n2 = draw(st.integers(*n_range))
        k1 = draw(st.sampled_from(kinds))
        k2 = draw(st.sampled_from(kinds))
        return (n1, side(draw, n1, 7, k1), n2, side(draw, n2, 7, k2))

    @st.composite
    def lopsided(draw):  # n1 > n2, tiny target, or one empty side
        n2 = draw(st.integers(0, 3))
        n1 = draw(st.integers(n2, 7))
        k1 = draw(st.sampled_from(["dense", "star", "arcs", "empty"]))
        k2 = draw(st.sampled_from(["dense", "star", "empty"]))
        inst = (n1, side(draw, n1, 7, k1), n2, side(draw, n2, 3, k2))
        if draw(st.booleans()):
            inst = (inst[2], inst[3], inst[0], inst[1])
        return inst

    seen = Counter()
    for name, strat in [("dense", cmo(["dense"], (0, 7))),
                        ("shared_endpoint", cmo(["star"], (2, 7))),
                        ("crossing_nested", cmo(["arcs", "dense"], (2, 7))),
                        ("lopsided_or_empty", lopsided())]:
        @common
        @given(strat)
        def prop(inst):
            seen[name] += 1
            check_instance(audit, inst, reduce_fn, extract_fn, rng)
        prop()
    return seen


# --------------------------------------------------------------------------

def run(reduce_fn=reduce, extract_fn=extract, quick=False, verbose=True):
    audit = Audit()
    rng = random.Random(SEED)
    sections = {}

    def section(name, fn):
        before = sum(audit.count.values())
        fn()
        sections[name] = sum(audit.count.values()) - before

    n_ex = Counter()

    def s_exh():
        for inst in exhaustive(quick):
            n_ex["instances"] += 1
            check_instance(audit, inst, reduce_fn, extract_fn, rng, full=not quick)
    section("exhaustive", s_exh)

    def s_shuf():
        r = random.Random(SEED + 1)
        base = [(a, E, b, F) for a in range(5) for b in range(5)
                for E in contact_sets(a) for F in contact_sets(b) if len(E) >= 2 or len(F) >= 2]
        for inst in r.sample(base, 300 if quick else 1500):
            n_ex["shuffled"] += 1
            check_instance(audit, shuffled(inst, r), reduce_fn, extract_fn, rng)
    section("shuffled_contact_order", s_shuf)

    def s_rand():
        r = random.Random(SEED + 2)
        for _ in range(30 if quick else 400):
            n1, n2 = r.randint(5, 7), r.randint(5, 7)
            p1, p2 = r.random(), r.random()
            E1 = tuple(e for e in itertools.combinations(range(n1), 2) if r.random() < p1 * 0.5)[:8]
            E2 = tuple(e for e in itertools.combinations(range(n2), 2) if r.random() < p2 * 0.5)[:8]
            n_ex["random"] += 1
            check_instance(audit, shuffled((n1, E1, n2, E2), r), reduce_fn, extract_fn, rng, full=False)
    section("random_n5to7", s_rand)

    def s_benchmark():
        for _name, inst in load_benchmark()[: (5 if quick else 100)]:
            n_ex["benchmark"] += 1
            check_instance(audit, inst, reduce_fn, extract_fn, rng, full=False)
    section("benchmark", s_benchmark)

    def s_examples():
        try:
            check_examples(audit, reduce_fn, extract_fn)
        except Exception as e:
            audit.check("examples_run", False, repr(e))
    section("worked_examples", s_examples)

    def s_overhead():
        # the proof's count: pairs differing in both indices = C(N,2) - m1 C(m2,2) - m2 C(m1,2)
        #                    = 2 C(m1,2) C(m2,2); counted directly, and symbolically
        for m1 in range(0, 9):
            for m2 in range(0, 9):
                idx = [(i, j) for i in range(m1) for j in range(m2)]
                both = sum(1 for (i, j), (k, l) in itertools.combinations(idx, 2) if i != k and j != l)
                N = m1 * m2
                audit.check("overhead_pair_count_identity",
                            both == comb(N, 2) - m1 * comb(m2, 2) - m2 * comb(m1, 2)
                            == SHARP_FACTOR * comb(m1, 2) * comb(m2, 2))
                audit.check("overhead_C(N,2)_strictly_above_sharp_iff_N>=2",
                            (comb(N, 2) > SHARP_FACTOR * comb(m1, 2) * comb(m2, 2)) == (N >= 2))
        import sympy as sp
        a, b = sp.symbols("m1 m2", integer=True, nonnegative=True)
        C2 = lambda x: x * (x - 1) / 2
        audit.check("overhead_symbolic_identity",
                    sp.expand(C2(a * b) - a * C2(b) - b * C2(a) - SHARP_FACTOR * C2(a) * C2(b)) == 0)
    section("overhead_formulas", s_overhead)

    hyp = {}

    def s_hyp():
        hyp.update(run_hypothesis(audit, reduce_fn, extract_fn, 40 if quick else 300))
    section("hypothesis", s_hyp)

    if verbose:
        print("=== independent check: MaximumContactMapOverlap -> MaximumClique<SimpleGraph,One> ===")
        print("instances:", dict(n_ex), "hypothesis:", dict(hyp))
        print("--- checks per section")
        for k, v in sections.items():
            print(f"  {k:28s} {v}")
        print("--- checks per claim (count / failures)")
        for k in sorted(audit.count):
            print(f"  {k:48s} {audit.count[k]:8d} {audit.fail[k]:6d}")
        for k, ctx in audit.first.items():
            print("  FIRST FAILURE", k, str(ctx)[:300])
        print(f"TOTAL: {sum(audit.count.values())} checks, {sum(audit.fail.values())} failures")
    return audit


if __name__ == "__main__":
    a = run(quick="--quick" in sys.argv)
    sys.exit(1 if sum(a.fail.values()) else 0)
Cross-comparison script (Python)
#!/usr/bin/env python3
"""Cross-compare MaximumContactMapOverlap -> MaximumClique<SimpleGraph, One> targets.

1. Script vs script: on every shared instance, the constructor's target
   (check_a.py) and the independent check's target (check_b.py)
   must be identical graphs.  The documented isomorphism is the identity: vertex
   p_{ij} has index i*m2 + j in both, i over contacts_1 and j over contacts_2 in
   their stored (canonicalized, list) order.  On a subset, both scripts' optimum
   oracles must also agree (max clique = brute-force alignment optimum).
2. With --pred <binary>: each pred instance is written in the source model's JSON
   format, reduced with `pred reduce --via <the single direct path>`, and the
   bundle's target must be MaximumClique/SimpleGraph/One with unit weights and the
   same vertex count and edge set as both scripts' targets.

Exit status 0 only if everything agrees; non-zero on any mismatch or pred failure.

  python3 cross_compare.py                     # scripts only
  python3 cross_compare.py --pred path/to/pred # scripts + pred
"""
from __future__ import annotations

import argparse
import contextlib
import importlib.util
import io
import itertools
import json
import os
import random
import subprocess
import sys
import tempfile

HERE = os.path.dirname(os.path.abspath(__file__))
SOURCE = "MaximumContactMapOverlap"
TARGET = "MaximumClique/SimpleGraph/One"
TARGET_VARIANT = {"graph": "SimpleGraph", "weight": "One"}
SEED = 12051


def load(name, path):
    spec = importlib.util.spec_from_file_location(name, path)
    mod = importlib.util.module_from_spec(spec)
    with contextlib.redirect_stdout(io.StringIO()):
        spec.loader.exec_module(mod)
    return mod


con = load("constructor_cc", os.path.join(HERE, "constructor.py"))
adv = load("independent check_cc", os.path.join(HERE, "independent check", "independent check.py"))


def canonical(raw):
    """What the source model stores: (min, max) per contact, list order kept."""
    return tuple((min(u, v), max(u, v)) for u, v in raw)


def con_target(inst):
    n1, E1, n2, E2 = inst
    cands, edges = con.reduce(n1, list(E1), n2, list(E2))
    return len(cands), frozenset(edges)


def adv_target(inst):
    return adv.reduce(inst)


# ------------------------------------------------------------- shared instances
def shared_instances():
    """(label, raw instance) - raw contacts may be non-canonical or shuffled."""
    out = []
    # constructor's exhaustive family
    for n1, E1, n2, E2 in con.instances_exhaustive():
        out.append(("constructor-exhaustive", (n1, tuple(E1), n2, tuple(E2))))
    # independent check's exhaustive family
    for inst in adv.exhaustive(False):
        out.append(("independent check-exhaustive", inst))
    # shuffled order and reversed orientation (exercise the stored order)
    rng = random.Random(SEED)
    small = [i for _, i in out if len(i[1]) >= 2 and len(i[3]) >= 2]
    for inst in rng.sample(small, 1500):
        n1, E1, n2, E2 = inst
        E1, E2 = list(E1), list(E2)
        rng.shuffle(E1)
        rng.shuffle(E2)
        E1 = [(v, u) if rng.random() < 0.5 else (u, v) for u, v in E1]
        E2 = [(v, u) if rng.random() < 0.5 else (u, v) for u, v in E2]
        out.append(("shuffled-reversed", (n1, tuple(E1), n2, tuple(E2))))
    # benchmark (contacts as stored in the files, unsorted)
    benchmark = os.path.expanduser("<benchmark>/MaximumContactMapOverlap")
    if os.path.isdir(benchmark):
        for fn in sorted(os.listdir(benchmark)):
            d = json.load(open(os.path.join(benchmark, fn)))["data"]
            out.append(("benchmark", (d["num_vertices_1"], tuple(map(tuple, d["contacts_1"])),
                                   d["num_vertices_2"], tuple(map(tuple, d["contacts_2"])))))
    # worked examples and special cases of the proof
    special = [
        adv.YES, adv.TRIANGLE,
        (0, (), 0, ()), (0, (), 3, ((0, 2),)), (3, ((1, 2),), 0, ()),   # empty sides
        (5, ((0, 4), (1, 2), (3, 4)), 2, ((0, 1),)),                      # n1 > n2
        (3, ((0, 1), (0, 2)), 3, ((0, 1), (0, 2), (1, 2))),               # shared source endpoint
        (4, ((0, 1), (2, 3)), 3, ((0, 1), (1, 2))),                       # disjoint vs shared target
        (4, ((0, 2), (1, 3)), 4, ((0, 3), (1, 2))),                       # crossing vs nested
        (6, ((5, 0), (4, 1), (3, 2)), 6, ((2, 3), (1, 4), (0, 5))),       # reversed orientation
        (7, ((0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)), 7,
         ((0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6))),               # identical backbones
    ]
    out += [("special", s) for s in special]
    return out


def pred_subset(instances):
    rng = random.Random(SEED + 1)
    keep = [(l, i) for l, i in instances if l in ("benchmark", "special")]
    keep += rng.sample([(l, i) for l, i in instances if l == "shuffled-reversed"], 150)
    keep += rng.sample([(l, i) for l, i in instances if l.endswith("exhaustive")], 150)
    return keep


# ---------------------------------------------------------------------- pred
def run(cmd, timeout=60):
    p = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
    return p.returncode, p.stdout, p.stderr


def find_route(pred, workdir):
    code, out, err = run([pred, "path", SOURCE, TARGET, "--limit", "all", "--quiet", "--json"])
    if code != 0:
        return None, f"pred path failed (exit {code}): {err.strip()[:300]}"
    try:
        batch = json.loads(out)
    except json.JSONDecodeError as e:
        return None, f"pred path printed no JSON: {e}"
    direct = [p for p in batch.get("paths", []) if p.get("steps") == 1]
    if len(direct) != 1:
        return None, f"expected one direct {SOURCE} -> {TARGET} path, found {len(direct)}"
    route = os.path.join(workdir, "route.json")
    with open(route, "w") as f:
        json.dump(direct[0], f, sort_keys=True)
    return route, None


def source_json(inst):
    n1, E1, n2, E2 = inst
    return {"type": SOURCE, "variant": {},
            "data": {"num_vertices_1": n1, "contacts_1": [list(e) for e in E1],
                     "num_vertices_2": n2, "contacts_2": [list(e) for e in E2]}}


def parse_target(bundle):
    t = bundle["target"]
    problems = []
    if t.get("type") != "MaximumClique":
        problems.append(f"target type {t.get('type')}")
    if t.get("variant") != TARGET_VARIANT:
        problems.append(f"target variant {t.get('variant')}")
    g = t["data"]["graph"]
    N = g["num_vertices"]
    raw = [tuple(e) for e in g["edges"]]
    edges = frozenset((min(e), max(e)) for e in raw)
    if len(edges) != len(raw):
        problems.append("duplicate edges")
    if any(a == b for a, b in raw):
        problems.append("self-loop")
    w = t["data"].get("weights")
    if w is not None and (len(w) != N or any(x != 1 for x in w)):
        problems.append(f"weights not unit/length N: {w[:10]}")
    return (N, edges), problems


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--pred", help="path to the pred binary")
    ap.add_argument("--keep", help="directory to keep the written instances and bundles")
    args = ap.parse_args()

    instances = shared_instances()
    by = {}
    mism = []
    opt_checked = 0
    rng = random.Random(SEED + 2)
    for label, raw in instances:
        inst = (raw[0], canonical(raw[1]), raw[2], canonical(raw[3]))
        a, c = adv_target(inst), con_target(inst)
        by.setdefault(label, [0, 0])
        by[label][0] += 1
        if a != c:
            by[label][1] += 1
            mism.append((label, raw, "independent check != constructor", a, c))
            continue
        # optimum agreement on a subset (both scripts' independent oracles)
        if label in ("special", "benchmark") or rng.random() < 0.05:
            om_adv = max(len(K) for K in adv.all_cliques(a))
            om_con = con.max_clique_size(*c)
            opt_adv = max(adv.score(inst, f) for f in adv.alignments(inst[0], inst[2]))
            opt_con = max(con.score(f, list(inst[1]), list(inst[3]))
                          for f in con.alignments(inst[0], inst[2]))
            opt_checked += 1
            if not (om_adv == om_con == opt_adv == opt_con):
                by[label][1] += 1
                mism.append((label, raw, "optimum disagreement", (om_adv, om_con), (opt_adv, opt_con)))
    print("script vs script (independent check target == constructor target, identity labelling):")
    for label, (n, bad) in by.items():
        print(f"  {label:24s} {n:7d} instances, {bad} mismatches")
    print(f"  optimum agreement (2 clique oracles, 2 alignment oracles) on {opt_checked} instances")

    pred_bad = 0
    pred_n = 0
    if args.pred:
        tmp = None if args.keep else tempfile.TemporaryDirectory(prefix="cmo_cc_")
        work = args.keep or tmp.name
        os.makedirs(work, exist_ok=True)
        route, err = find_route(args.pred, work)
        if err:
            print(f"PRED FAILURE: {err}")
            return 2
        for k, (label, raw) in enumerate(pred_subset(instances)):
            inst = (raw[0], canonical(raw[1]), raw[2], canonical(raw[3]))
            expect = adv_target(inst)
            assert expect == con_target(inst)
            src = os.path.join(work, f"{k:04d}-src.json")
            dst = os.path.join(work, f"{k:04d}-bundle.json")
            with open(src, "w") as f:
                json.dump(source_json(raw), f)
            code, _out, perr = run([args.pred, "reduce", src, "--via", route, "-o", dst, "-q"])
            pred_n += 1
            if code != 0:
                pred_bad += 1
                mism.append((label, raw, f"pred reduce exit {code}: {perr.strip()[:200]}", None, expect))
                continue
            try:
                got, problems = parse_target(json.load(open(dst)))
            except Exception as e:  # malformed bundle
                pred_bad += 1
                mism.append((label, raw, f"unreadable bundle: {e!r}", None, expect))
                continue
            if problems or got != expect:
                pred_bad += 1
                mism.append((label, raw, f"pred target differs {problems}",
                             (got[0], sorted(got[1])[:12]), (expect[0], sorted(expect[1])[:12])))
        print(f"pred reduce vs both scripts: {pred_n} instances, {pred_bad} mismatches"
              + (f" (instances kept in {work})" if args.keep else ""))
        if tmp:
            tmp.cleanup()
    else:
        print("pred comparison skipped (no --pred given)")

    for m in mism[:15]:
        print("MISMATCH", m[0], m[1], m[2], "got", m[3], "expected", m[4])
    total = sum(n for n, _ in by.values())
    print(f"CROSS-COMPARE: {total} shared instances, {pred_n} via pred, {len(mism)} mismatches")
    return 1 if mism else 0


if __name__ == "__main__":
    sys.exit(main())

@codecov

codecov Bot commented Nov 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.20%. Comparing base (e7bacb3) to head (b0f0ce4).

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #1235    +/-   ##
========================================
  Coverage   97.19%   97.20%            
========================================
  Files        1146     1148     +2     
  Lines      151558   151879   +321     
========================================
+ Hits       147308   147632   +324     
+ Misses       4250     4247     -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Rule] MaximumContactMapOverlap to MaximumClique

1 participant