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>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user