"""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)