Files
math-research/flip_symmetric_census.py
didericis 1749f702cf Add flip-symmetry paper with empirical density census through n=14
Introduces the maximal_planar_graph_edge_flipping paper, motivating
flip-symmetry as a structural restriction on minimum-order
five-chromatic counterexamples, and reports an exhaustive census
showing |F_n|/|T_n| decays geometrically (factor ~1/2 per step from
n=10 to n=14). The census driver lives in flip_symmetric_census.py.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 23:56:38 -04:00

74 lines
3.0 KiB
Python

"""Census of flip-symmetric maximal planar graphs.
A maximal planar graph G is *flip-symmetric* if some admissible edge uv
satisfies G^flip(uv) ~= G, where flip(uv) replaces uv with wx (the third
vertices of the two triangular faces incident to uv), and admissible
means wx is not already an edge of G.
"""
from typing import Iterator
from sage.all import Graph, graphs # type: ignore[attr-defined] # pylint: disable=no-name-in-module
def triangular_face_pairs(g: Graph) -> dict[frozenset, tuple]:
"""For each edge of g (a triangulation), return the two third-vertices
of its incident triangular faces, keyed by frozenset({u, v})."""
g_emb = g.copy()
if not g_emb.is_planar(set_embedding=True):
raise ValueError("graph is not planar")
pairs: dict[frozenset, list] = {}
for face in g_emb.faces():
if len(face) != 3:
raise ValueError("graph is not a triangulation")
a, b, c = face[0][0], face[1][0], face[2][0]
for u, v, third in [(a, b, c), (b, c, a), (c, a, b)]:
pairs.setdefault(frozenset((u, v)), []).append(third)
return {k: tuple(v) for k, v in pairs.items()}
def is_flip_symmetric(g: Graph) -> bool:
"""Return True iff g admits some admissible flip uv -> wx with G^flip ~= G."""
pairs = triangular_face_pairs(g)
for u, v in g.edges(labels=False):
thirds = pairs.get(frozenset((u, v)))
if thirds is None or len(thirds) != 2:
continue
w, x = thirds
if g.has_edge(w, x):
continue
flipped = g.copy()
flipped.delete_edge(u, v)
flipped.add_edge(w, x)
if g.is_isomorphic(flipped):
return True
return False
def census(min_order: int, max_order: int, minimum_degree: int | None = None) -> None:
"""Enumerate every maximal planar graph of order in [min_order, max_order]
(optionally restricted by minimum_degree) and print counts and density
of flip-symmetric graphs at each order."""
for n in range(min_order, max_order + 1):
kwargs = {"minimum_connectivity": 3, "maximum_face_size": 3}
if minimum_degree is not None:
kwargs["minimum_degree"] = minimum_degree
total = 0
flip_sym = 0
gen: Iterator[Graph] = graphs.planar_graphs(n, **kwargs)
for g in gen:
total += 1
if is_flip_symmetric(g):
flip_sym += 1
if total % 1000 == 0:
print(f" order {n}: {total} checked, {flip_sym} flip-symmetric")
density = (flip_sym / total) if total else 0.0
tag = f" (min_degree {minimum_degree})" if minimum_degree is not None else ""
print(f"n={n}{tag}: total={total} flip_symmetric={flip_sym} density={density:.6f}")
if __name__ == "__main__":
import sys
max_order = int(sys.argv[1]) if len(sys.argv) > 1 else 12
min_order = int(sys.argv[2]) if len(sys.argv) > 2 else 4
min_deg = int(sys.argv[3]) if len(sys.argv) > 3 else None
census(min_order, max_order, minimum_degree=min_deg)