Repository Public
1"""Matplotlib style for paper figures (static PDF). Follows the project dataviz rules: categorical hues in a fixed2order per system (never cycled), one axis per chart, thin marks, recessive grid/axes, text in ink tokens, direct3labels where they help. Palette = the validated reference instance (light mode)."""4from __future__ import annotations56import matplotlib as mpl78SURFACE = "#fcfcfb"9INK = "#0b0b0b"10INK2 = "#52514e"11GRID = "#e1e0d9"12AXIS = "#c3c2b7"13# fixed categorical order (slot → hue); systems are mapped to slots by name, never by rank14CATEGORICAL = ["#2a78d6", "#eb6834", "#1baf7a", "#eda100", "#e87ba4", "#008300"]15SYSTEM_SLOT = {"B0_identity": 0, "B1_generic_paraphrase": 1, "B2": 3, "M1": 2, "M2": 4, "M3": 5}16SEQUENTIAL_BLUE = ["#cde2fb", "#9ec5f4", "#6da7ec", "#3987e5", "#256abf", "#1c5cab", "#104281"]171819_ASSIGNED: dict[str, str] = {}20MARKERS = ["o", "s", "D", "^", "v", "P"]212223def assign_system_colors(systems: list[str]) -> dict[str, str]:24 """Fixed, order-stable assignment: baselines keep their slots (B0 blue, B1 orange, B2 yellow); other systems take25 the remaining slots in the order given. Hues are never generated; beyond 6 systems the extra ones reuse a slot and26 must be distinguished by marker (see MARKERS) and direct labels."""27 fixed = {"B0_identity": 0, "B1_generic_paraphrase": 1}28 used = set()29 out: dict[str, str] = {}30 for s in systems:31 if s in fixed:32 out[s] = CATEGORICAL[fixed[s]]33 used.add(fixed[s])34 elif s.startswith("B2"):35 out[s] = CATEGORICAL[3]36 used.add(3)37 free = [i for i in range(len(CATEGORICAL)) if i not in used]38 k = 039 for s in systems:40 if s not in out:41 out[s] = CATEGORICAL[free[k % len(free)]] if free else INK242 k += 143 _ASSIGNED.update(out)44 return out454647def system_color(name: str) -> str:48 if name in _ASSIGNED:49 return _ASSIGNED[name]50 for key, slot in SYSTEM_SLOT.items():51 if name == key or name.lower().startswith(key.lower()):52 return CATEGORICAL[slot]53 return INK2545556def system_marker(name: str, systems: list[str]) -> str:57 return MARKERS[systems.index(name) % len(MARKERS)] if name in systems else "o"585960def apply() -> None:61 mpl.rcParams.update({62 "figure.facecolor": SURFACE, "axes.facecolor": SURFACE, "savefig.facecolor": SURFACE,63 "font.family": "sans-serif", "font.size": 9, "axes.titlesize": 10, "axes.labelsize": 9, "legend.fontsize": 8,64 "text.color": INK, "axes.labelcolor": INK2, "xtick.color": INK2, "ytick.color": INK2,65 "axes.edgecolor": AXIS, "axes.linewidth": 0.6, "axes.spines.top": False, "axes.spines.right": False,66 "axes.grid": True, "grid.color": GRID, "grid.linewidth": 0.5, "axes.axisbelow": True,67 "lines.linewidth": 1.6, "lines.markersize": 4.5, "legend.frameon": False, "pdf.fonttype": 42,68 })69