Research-Stack/4-Infrastructure/shim/chinese_postman_demo.py
allaun 475f6319ea chore(repo): push local 768-commit branch state onto clean remote baseline
This squashes all local history (768 commits) onto the scrubbed PR #90
baseline. Individual commits were lost during filter-repo corruption;
the working tree content is preserved intact.

Build: N/A (working tree state only)
2026-06-15 22:46:50 -05:00

126 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Chinese Postman Problem / Route Inspection / "edge-TSP" — Hierholzer +
minimum-weight perfect matching on odd-degree vertices.
Reference: mei-Ko Kwan (1962). For a connected undirected graph with
non-negative edge weights, the minimum closed walk covering every edge
has total weight:
sum_e w(e) + (minimum weight perfect matching on odd-degree vertices)
The first term is forced (you must traverse every edge at least once).
The second term is the minimum augmentation that makes every vertex
even-degree, which is a perfect matching on the odd-degree vertex set
because adding a matching edge is equivalent to duplicating the
shortest path between the matched pair.
BraidStorm connection: the 8-strand BraidStorm crossing graph has
8 vertices each of degree 7. The odd-degree set is the full vertex set
8, so the perfect matching has 4 edges. This prototype verifies the
minimum augmentation on a concrete crossing graph.
"""
from itertools import combinations
from typing import Dict, List, Set, Tuple
Edge = Tuple[int, int]
WeightedGraph = Dict[int, List[Tuple[int, float]]]
def all_shortest_paths(g: WeightedGraph) -> Dict[Tuple[int, int], float]:
"""FloydWarshall; fine for the small (≤ 8 vertex) BraidStorm case."""
n = max(g.keys()) + 1
INF = float("inf")
d = [[INF] * n for _ in range(n)]
for u in range(n):
d[u][u] = 0
for v, w in g[u]:
if w < d[u][v]:
d[u][v] = d[v][u] = w
for k in range(n):
for i in range(n):
for j in range(n):
if d[i][k] + d[k][j] < d[i][j]:
d[i][j] = d[i][k] + d[k][j]
return {(i, j): d[i][j] for i in range(n) for j in range(n)}
def minimum_weight_perfect_matching(
points: List[int], dist: Dict[Tuple[int, int], float]
) -> float:
"""Brute-force over all perfect matchings of an even-sized set.
Returns minimum sum of pairwise distances."""
if not points:
return 0.0
if len(points) == 2:
return dist[(points[0], points[1])]
first, rest = points[0], points[1:]
best = float("inf")
for i, partner in enumerate(rest):
remaining = rest[:i] + rest[i + 1:]
cost = dist[(first, partner)] + minimum_weight_perfect_matching(remaining, dist)
if cost < best:
best = cost
return best
def chinese_postman(g: WeightedGraph) -> Tuple[float, int, List[int]]:
"""Returns (total weight, augmentation edge count, odd-degree vertex list).
The augmentation edge count is the size of the odd-degree vertex
matching — i.e., the number of path duplications needed.
"""
n = max(g.keys()) + 1
total_weight = sum(w for u in range(n) for v, w in g[u] if u < v)
# Compute degree of each vertex
deg = {u: len(g[u]) for u in range(n)}
odd_vertices = sorted([u for u, d in deg.items() if d % 2 == 1])
if not odd_vertices:
return total_weight, 0, []
dist = all_shortest_paths(g)
match_cost = minimum_weight_perfect_matching(odd_vertices, dist)
return total_weight + match_cost, len(odd_vertices) // 2, odd_vertices
def braidstorm_crossing_graph() -> WeightedGraph:
"""8 vertices (strands), edge (i, j) for every distinct i ≠ j.
Weight = residual from a typical FAMM scar pattern: w(i,j) = 2^|i-j|.
Strands labeled 0..7. 8 strands × 7 crossings each = 28 edges.
Every vertex has degree 7 (odd), so the odd-degree set is all 8
vertices. The matching has 4 edges.
"""
g: WeightedGraph = {i: [] for i in range(8)}
for i in range(8):
for j in range(i + 1, 8):
w = 2 ** abs(i - j)
g[i].append((j, float(w)))
g[j].append((i, float(w)))
return g
def main() -> None:
print("=== Chinese Postman / Hierholzer-Euler demo ===\n")
g = braidstorm_crossing_graph()
total, augmentation_count, odd = chinese_postman(g)
print(f"BraidStorm crossing graph: 8 strands, 28 edges")
print(f" base edge-weight sum : {sum(w for u in range(8) for v, w in g[u] if u < v):.0f}")
print(f" odd-degree vertices : {odd}")
print(f" augmentation edge count: {augmentation_count}")
print(f" min-weight matching cost: {total - sum(w for u in range(8) for v, w in g[u] if u < v):.0f}")
print(f" total closed-walk cost : {total:.0f}")
print(f"\nEigensolid scar-pressure bound: 4 duplicated crossings, matching.")
# Smoke check: on an Eulerian graph (4-cycle), no augmentation needed
cycle4: WeightedGraph = {0: [(1, 1.0), (3, 1.0)], 1: [(0, 1.0), (2, 1.0)],
2: [(1, 1.0), (3, 1.0)], 3: [(2, 1.0), (0, 1.0)]}
total_e, aug_e, odd_e = chinese_postman(cycle4)
print(f"\n4-cycle (Eulerian): cost={total_e}, augmentation={aug_e}")
assert aug_e == 0, "Eulerian graph needs no augmentation"
print(" assertion passed: Eulerian graph needs no augmentation ✓")
if __name__ == "__main__":
main()