"""Re-emit cube_view.svg in an academic (white-background, large-font) palette and convert it to PDF for the 1H-codesign-paper Figure 2. Source of truth: docs/diagrams/cube_view.svg (generated by src/kernbench/topology/visualizer.py:_render_cube_view_svg, dark theme). This script does a targeted color/font/size remap on the dark palette so the resulting figure prints well on a white paper page. If the upstream palette or geometry in visualizer.py changes, the maps below must be reviewed. """ from __future__ import annotations import re import shutil import subprocess from pathlib import Path REPO = Path(__file__).resolve().parents[2] SRC_SVG = REPO / "docs" / "diagrams" / "cube_view.svg" OUT_DIR = REPO / "docs" / "report" / "1H-codesign-paper" / "figures" OUT_SVG = OUT_DIR / "cube_architecture.svg" OUT_PDF = OUT_DIR / "cube_architecture.pdf" # ── 1. Legend-icon safety: legend rects whose general color rule below # would turn them white-on-white. Apply BEFORE the wholesale fill rules. LEGEND_FIXUP: list[tuple[str, str]] = [ # "Relay" legend icon (slate-700 router) ('fill="#334155" stroke="#475569" stroke-width="0.5"', 'fill="#94a3b8" stroke="#475569" stroke-width="0.5"'), # "Mesh Link" legend icon (slate-600) ('fill="#475569" stroke="#475569" stroke-width="0.5"', 'fill="#94a3b8" stroke="#475569" stroke-width="0.5"'), ] # ── 2. Color remap: dark theme -> academic (white) theme COLOR_MAP: list[tuple[str, str]] = [ # page background slate-900 -> white ('fill="#0f172a"', 'fill="#ffffff"'), # title text slate-400 -> slate-800 ('fill="#94a3b8"', 'fill="#1f2937"'), # subtitle text slate-500 -> slate-600 ('fill="#64748b"', 'fill="#475569"'), # router has-attach stroke slate-500 -> slate-600 ('stroke="#64748b"', 'stroke="#475569"'), # router has-attach fill slate-600 -> white ('fill="#475569"', 'fill="#ffffff"'), # mesh lines + cube boundary stroke slate-600 -> slate-400 ('stroke="#475569"', 'stroke="#94a3b8"'), # router no-attach fill slate-700 -> white ('fill="#334155"', 'fill="#ffffff"'), # component block dark fills -> white (PE / M_CPU / SRAM / UCIe) ('fill="#2d1f3d"', 'fill="#ffffff"'), ('fill="#451a03"', 'fill="#ffffff"'), ('fill="#1c1917"', 'fill="#ffffff"'), ('fill="#1e1b4b"', 'fill="#ffffff"'), # HBM zone background emerald-950 -> emerald-50 ('fill="#052e16"', 'fill="#ecfdf5"'), # router label text white -> slate-800 ('fill="white"', 'fill="#1f2937"'), # boost low-alpha emerald annotations so PE/HBM BW labels read on light bg ('fill="#10b98188"', 'fill="#047857"'), ('fill="#05966988"', 'fill="#059669"'), ] # ── 3. UCIe palette desaturation: bright violet/indigo/fuchsia is too # attention-grabbing on a white page; remap to a slate gradient. # NOTE: side-effect on PE2/PE3 HBM port-bar colors (which share # #8b5cf6/#a78bfa with UCIe) — PE labels still convey identity. # Applied AFTER the academic color map so the new slate values are # not picked up by the rules above. UCIE_DESATURATE: list[tuple[str, str]] = [ # UCIe block stroke/text (violet-500) -> slate-600 ('"#8b5cf6"', '"#475569"'), # UCIe cell 1 / PE3 port bar (violet-400) -> slate-400 ('"#a78bfa"', '"#94a3b8"'), # UCIe cell 0 (indigo-400) -> slate-300 ('"#818cf8"', '"#cbd5e1"'), # UCIe cell 2 (purple-400) -> slate-500 ('"#c084fc"', '"#64748b"'), # UCIe cell 3 (fuchsia-400) -> gray-700 ('"#e879f9"', '"#374151"'), ] # ── 4. Font-size bumps. The CUBE figure is rendered at half-text-width # (~250pt) inside the side-by-side subfigure in 02-platform.tex, so # native fonts get crushed ~3x by \linewidth scaling. We push the # bumps to the legibility limit of the layout (router-label text # stays inside a slightly enlarged circle; legend items may touch). FONT_MAP: dict[str, str] = { "5": "10", "6": "12", "7": "14", "8": "13", # legend rect text — capped by upstream layout spacing # (advance = 7*len(label)+24 was sized for ~font 8); # font 13 keeps each item's text inside its slot. "9": "14", "10": "15", "11": "16", "14": "18", # title — kept moderate so it does not overflow canvas } # ── 5. Router circle radius bump (only circles use r="8"). Enlarged so # the bumped router labels stay inside the circle. RADIUS_MAP: list[tuple[str, str]] = [ (' r="8"', ' r="17"'), ] # ── 6. Tighten whitespace: move legend just below dashed box and crop # the unused canvas margins so LaTeX's \linewidth scaling does not # shrink the figure text any more than necessary. LAYOUT_FIXUP: list[tuple[str, str]] = [ # Move legend rects up (dashed box bottom is at y=760) (' y="865"', ' y="775"'), # Move legend text baselines up to match (offset = 9 px) (' y="874"', ' y="784"'), # Tight crop: 5 px margin around dashed box + cut top/bottom whitespace ('', ''), ] def _bump_font(m: re.Match) -> str: return f'font-size="{FONT_MAP.get(m.group(1), m.group(1))}"' def main() -> None: if not SRC_SVG.exists(): raise SystemExit(f"source SVG missing: {SRC_SVG}") rsvg = shutil.which("rsvg-convert") if rsvg is None: raise SystemExit("rsvg-convert not found (brew install librsvg)") svg = SRC_SVG.read_text(encoding="utf-8") for old, new in (LEGEND_FIXUP + COLOR_MAP + UCIE_DESATURATE + RADIUS_MAP + LAYOUT_FIXUP): if old not in svg: print(f"warn: pattern not present in source SVG: {old}") svg = svg.replace(old, new) svg = re.sub(r'font-size="(\d+)"', _bump_font, svg) OUT_DIR.mkdir(parents=True, exist_ok=True) OUT_SVG.write_text(svg, encoding="utf-8") subprocess.run( [rsvg, "-f", "pdf", "-o", str(OUT_PDF), str(OUT_SVG)], check=True, ) print(f"wrote {OUT_SVG.relative_to(REPO)}") print(f"wrote {OUT_PDF.relative_to(REPO)}") if __name__ == "__main__": main()