import hashlib import json from pathlib import Path from typing import Any, TypedDict from sage.all import Graph # type: ignore[attr-defined] # pylint: disable=no-name-in-module from lib.colored_graphs import ColoredGraphId, VertexColoring, plot_colored_graph_to_data_uri class Operation(TypedDict): """Information about a change made to a (colored) graph""" name: Any meta: Any source_graph: Graph source_canon_id: ColoredGraphId result_graph: Graph result_coloring: VertexColoring result_canon_id: ColoredGraphId def colored_graph_id_to_string(cid: ColoredGraphId) -> str: return f"{cid['graph_id']} {cid['coloring_id']}" def operation_to_string(op: Operation) -> str: return f"{colored_graph_id_to_string(op['source_canon_id'])} -> {colored_graph_id_to_string(op['result_canon_id'])}" def operation_sequence_id(ops: list[Operation]) -> str: joined = "\n".join(operation_to_string(op) for op in ops) return hashlib.sha256(joined.encode()).hexdigest() def strip_graphs(obj: Any) -> Any: if isinstance(obj, dict): return {k: strip_graphs(v) for k, v in obj.items() if not isinstance(v, Graph)} if isinstance(obj, list): return [strip_graphs(v) for v in obj] return obj def save_operation_sequence(op_sequence: list[Operation], g: Graph, coloring: VertexColoring, save_dir: Path) -> str: """Save op_sequence as JSON and Markdown under save_dir/data/operations/. Returns the sequence id.""" op_seq_id = operation_sequence_id(op_sequence) op_dir = save_dir / "data" / "operations" / op_seq_id op_dir.mkdir(parents=True, exist_ok=True) (op_dir / "colored_pentagon_contractions.json").write_text(json.dumps(strip_graphs(op_sequence), indent=2)) md_lines = [f"## start\n\n![start]({plot_colored_graph_to_data_uri(g, coloring)})"] for op in op_sequence: meta_json = json.dumps(op['meta']) md_lines.append(f"## {op['name']} {meta_json}\n\n![b]({plot_colored_graph_to_data_uri(op['result_graph'], op['result_coloring'])})") (op_dir / "colored_pentagon_contractions.md").write_text("\n".join(md_lines) + "\n") return op_seq_id