Add operation tracking, graph saving, and markdown output for pentagon reduction
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"""Example: colored pentagon reduction on a random 20-vertex triangulation."""
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Any, cast, TypedDict, Literal
|
||||
@@ -29,6 +31,16 @@ class CanonicalColoredGraph(TypedDict):
|
||||
graph: Graph
|
||||
coloring: VertexColoring
|
||||
|
||||
def colored_graph_id_to_string(cid: ColoredGraphId) -> str:
|
||||
return f"{cid['graph_id']} {cid['coloring_id']}"
|
||||
|
||||
def op_to_transform_id(op: Operation) -> str:
|
||||
return f"{colored_graph_id_to_string(op['before'])} -> {colored_graph_id_to_string(op['after'])}"
|
||||
|
||||
def operation_sequence_id(ops: list[Operation]) -> str:
|
||||
joined = "\n".join(op_to_transform_id(op) for op in ops)
|
||||
return hashlib.sha256(joined.encode()).hexdigest()
|
||||
|
||||
def canonize_colored_graph(g: Graph, coloring: VertexColoring) -> ColoredGraphId:
|
||||
"""Mutate g and coloring to canonical labels and return a canonical ColoredGraphId"""
|
||||
canonical, cert = cast(
|
||||
@@ -56,7 +68,7 @@ def save_colored_graph(g: Graph, coloring: VertexColoring) -> tuple[Graph, Verte
|
||||
If already saved, load and return the cached graph.
|
||||
"""
|
||||
cid = canonize_colored_graph(g, coloring)
|
||||
out_dir = DIR / "data" / cid['graph_id'] / cid['coloring_id']
|
||||
out_dir = DIR / "data" / "graphs" / cid['graph_id'] / cid['coloring_id']
|
||||
if (out_dir / "graph.sobj").exists():
|
||||
g_canon = cast(Graph, load(str(out_dir / 'graph')))
|
||||
return g_canon, coloring, cid
|
||||
@@ -97,7 +109,7 @@ def pluck(g: Graph, coloring: VertexColoring, v0: Any) -> tuple[Graph, VertexCol
|
||||
class SquishMeta(TypedDict):
|
||||
"""Meta information about the squish operation"""
|
||||
v0: Any
|
||||
v_merged: set[Any]
|
||||
v_merged: list[Any]
|
||||
|
||||
class SquishOperation(Operation):
|
||||
"""
|
||||
@@ -128,21 +140,12 @@ def squish(g: Graph, coloring: VertexColoring, v0: Any) -> tuple[Graph, VertexCo
|
||||
return g_prime, coloring_prime, v1, v2
|
||||
|
||||
|
||||
Step = tuple[str, str] # (name, title)
|
||||
|
||||
def reduction_operation_to_string(op: SquishOperation | PluckOperation):
|
||||
"""String representation of the given operation"""
|
||||
if op['name'] == 'squish':
|
||||
meta = op['meta']
|
||||
vm = list(sorted(op['meta']['v_merged']))
|
||||
return f"squish_(v0={meta['v0']}, v1={vm[0]}, v2={vm[1]}"
|
||||
if op['name'] == 'pluck':
|
||||
meta = op['meta']
|
||||
return f"pluck_(v0={meta['v0']}"
|
||||
Step = SquishOperation | PluckOperation
|
||||
|
||||
def reduce(
|
||||
g: Graph,
|
||||
coloring: VertexColoring,
|
||||
before_cid: ColoredGraphId,
|
||||
step: int = 1,
|
||||
steps: list[Step] | None = None,
|
||||
) -> list[Step]:
|
||||
@@ -159,10 +162,9 @@ def reduce(
|
||||
if g.degree(v) == 3 and _neighbors_form_cycle(g, v):
|
||||
g_prime, coloring_prime = pluck(g, coloring, v)
|
||||
print(f"\nG' (after pluck v0={v}): {g_prime.order()} vertices, {g_prime.size()} edges")
|
||||
name, title = f"step_{step:04d}_(triangle)", f"G' (after pluck for v0={v})"
|
||||
steps.append((name, title))
|
||||
save_colored_graph(g_prime, coloring_prime)
|
||||
return reduce(g_prime, coloring_prime, step + 1, steps)
|
||||
_, _, after_cid = save_colored_graph(g_prime, coloring_prime)
|
||||
steps.append(PluckOperation(name='pluck', meta=PluckMeta(v0=v), before=before_cid, after=after_cid))
|
||||
return reduce(g_prime, coloring_prime, after_cid, step + 1, steps)
|
||||
if g.degree(v) == 4 and _neighbors_form_cycle(g, v):
|
||||
degree_4_candidates.append(v)
|
||||
elif g.degree(v) == 5 and _neighbors_form_cycle(g, v):
|
||||
@@ -173,20 +175,18 @@ def reduce(
|
||||
g_prime, coloring_prime, v1, v2 = squish(g, coloring, v0)
|
||||
print(f"Shared-color neighbors: v1={v1}, v2={v2} (color {coloring[v1]})")
|
||||
print(f"\nG' (after squish v0={v0}): {g_prime.order()} vertices, {g_prime.size()} edges")
|
||||
name, title = f"step_{step:04d}_(square)", f"G' (after squish for v0={v0})"
|
||||
steps.append((name, title))
|
||||
save_colored_graph(g_prime, coloring_prime)
|
||||
return reduce(g_prime, coloring_prime, step + 1, steps)
|
||||
_, _, after_cid = save_colored_graph(g_prime, coloring_prime)
|
||||
steps.append(SquishOperation(name='squish', meta=SquishMeta(v0=v0, v_merged=[v1, v2]), before=before_cid, after=after_cid))
|
||||
return reduce(g_prime, coloring_prime, after_cid, step + 1, steps)
|
||||
|
||||
if degree_5_candidates:
|
||||
v0 = degree_5_candidates[0]
|
||||
g_prime, coloring_prime, v1, v2 = squish(g, coloring, v0)
|
||||
print(f"Shared-color neighbors: v1={v1}, v2={v2} (color {coloring[v1]})")
|
||||
print(f"\nG' (after squish v0={v0}): {g_prime.order()} vertices, {g_prime.size()} edges")
|
||||
name, title = f"step_{step:04d}_(pentagon)", f"G' (after squish for v0={v0})"
|
||||
steps.append((name, title))
|
||||
save_colored_graph(g_prime, coloring_prime)
|
||||
return reduce(g_prime, coloring_prime, step + 1, steps)
|
||||
_, _, after_cid = save_colored_graph(g_prime, coloring_prime)
|
||||
steps.append(SquishOperation(name='squish', meta=SquishMeta(v0=v0, v_merged=[v1, v2]), before=before_cid, after=after_cid))
|
||||
return reduce(g_prime, coloring_prime, after_cid, step + 1, steps)
|
||||
|
||||
print("DONE")
|
||||
return steps
|
||||
@@ -197,6 +197,25 @@ print(f"G: {G.order()} vertices, {G.size()} edges")
|
||||
print(f"Degree sequence: {sorted(G.degree_sequence(), reverse=True)}")
|
||||
starting_coloring_classes = G.coloring()
|
||||
starting_coloring = {v: i for i, cls in enumerate(starting_coloring_classes) for v in cls}
|
||||
save_colored_graph(G, starting_coloring)
|
||||
_, _, initial_cid = save_colored_graph(G, starting_coloring)
|
||||
|
||||
reduce(G, starting_coloring)
|
||||
steps = reduce(G, starting_coloring, initial_cid)
|
||||
print("\nSteps:")
|
||||
print(json.dumps(steps, indent=2))
|
||||
|
||||
op_seq_id = operation_sequence_id(steps)
|
||||
op_dir = DIR / "data" / "operations" / op_seq_id
|
||||
op_dir.mkdir(parents=True, exist_ok=True)
|
||||
(op_dir / "colored_pentagon_contractions.json").write_text(json.dumps(steps, indent=2))
|
||||
|
||||
def img_data_uri(cid: ColoredGraphId) -> str:
|
||||
png_bytes = (DIR / "data" / "graphs" / cid['graph_id'] / cid['coloring_id'] / "graph.png").read_bytes()
|
||||
return f"data:image/png;base64,{base64.b64encode(png_bytes).decode()}"
|
||||
|
||||
md_lines = [f"## start\n\n})"]
|
||||
for step in steps:
|
||||
b = step['before']
|
||||
a = step['after']
|
||||
meta_json = json.dumps(step['meta'])
|
||||
md_lines.append(f"## {step['name']} {meta_json}\n\n})")
|
||||
(op_dir / "colored_pentagon_contractions.md").write_text("\n".join(md_lines) + "\n")
|
||||
|
||||
Reference in New Issue
Block a user