fe423dc7ba
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
"""Plane depth sequencing on maximal planar graphs."""
|
|
import random
|
|
from typing import Any, TypedDict
|
|
from sage.all import Graph, graphs # type: ignore[attr-defined] # pylint: disable=no-name-in-module
|
|
from lib.colored_graphs import canonize_and_save_graph
|
|
|
|
|
|
class DeeplyEmbeddedGraph(TypedDict):
|
|
graph: Graph
|
|
outer_cycle: list[Any]
|
|
plane_depth_labelling: dict[Any, int]
|
|
deep_embedding: Graph
|
|
|
|
|
|
def get_plane_depth_labelling(g: Graph, outer_cycle: list[Any]) -> dict[Any, int]:
|
|
"""Return the plane depth of each vertex relative to the given outer cycle."""
|
|
# equivalent to the commented out naive implementation:
|
|
# return {v: min(g.distance(v, u) for u in outer_cycle) for v in g.vertices()}
|
|
source = max(g.vertices()) + 1
|
|
g_prime = g.copy()
|
|
g_prime.add_vertex(source)
|
|
g_prime.add_edges([(source, v) for v in outer_cycle])
|
|
distances = g_prime.breadth_first_search(source, report_distance=True)
|
|
return {v: d - 1 for v, d in distances if v != source}
|
|
|
|
|
|
def deep_embedding(g: Graph, outer_cycle: list[Any], plane_depth_labelling: dict[Any, int] | None = None) -> Graph:
|
|
"""
|
|
Return the deep embedding of g relative to outer_cycle.
|
|
|
|
For every interior triangular face whose three vertices all have the same
|
|
plane depth, a new vertex is added adjacent to each vertex of that face.
|
|
"""
|
|
if plane_depth_labelling is None:
|
|
plane_depth_labelling = get_plane_depth_labelling(g, outer_cycle)
|
|
outer_vertices = set(outer_cycle)
|
|
|
|
embedding = g.get_embedding()
|
|
if embedding is None:
|
|
g.is_planar(set_embedding=True)
|
|
embedding = g.get_embedding()
|
|
|
|
g_prime = g.copy()
|
|
new_vertex = max(g.vertices()) + 1
|
|
|
|
for face in g.faces(embedding):
|
|
face_vertices = [u for u, v in face]
|
|
if set(face_vertices) == outer_vertices:
|
|
continue
|
|
if plane_depth_labelling[face_vertices[0]] == plane_depth_labelling[face_vertices[1]] == plane_depth_labelling[face_vertices[2]]:
|
|
g_prime.add_vertex(new_vertex)
|
|
g_prime.add_edges([(new_vertex, v) for v in face_vertices])
|
|
new_vertex += 1
|
|
|
|
return g_prime
|
|
|
|
|
|
def generate_example(n: int) -> DeeplyEmbeddedGraph:
|
|
"""Generate a random maximal planar graph of size n and return the triangulation, outer cycle, and deep embedding."""
|
|
g = graphs.RandomTriangulation(n)
|
|
g.is_planar(set_embedding=True)
|
|
embedding = g.get_embedding()
|
|
faces = g.faces(embedding)
|
|
outer_cycle = [u for u, v in random.choice(faces)]
|
|
plane_depth_labelling = get_plane_depth_labelling(g, outer_cycle)
|
|
return DeeplyEmbeddedGraph(graph=g, outer_cycle=outer_cycle, plane_depth_labelling=plane_depth_labelling, deep_embedding=deep_embedding(g, outer_cycle, plane_depth_labelling))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
example = generate_example(10)
|
|
canonical, graph_dir = canonize_and_save_graph(example['graph'])
|
|
(graph_dir / "plane_depth_sequence").mkdir(parents=True, exist_ok=True)
|
|
print(canonical)
|