Visualization¶
See also: Documentation Index, Knob Importance Analysis, PBT vs BO Comparison
Overview¶
The src/visualization/ package generates publication-quality figures from PBT session and analysis artifacts. It is a self-contained pipeline that loads JSON results, applies a venue-specific matplotlib theme, and renders figures registered through a central registry.
The package is invoked via the CLI python -m src.visualization and is the canonical way to produce the paper figures from a results tree on disk. It is independent of the tuning loop — it never connects to PostgreSQL, never imports from src/tuners/, and runs offline against saved JSON.
results/ src/visualization/
├── sessions/{workload}/ │
│ └── {pbt,bo,lhs}/{tier}/ │ loaders/
│ └── traces/ ────► │ session.py
│ trace_*.json │ baseline.py
├── comparisons/{workload}/ ────► │ comparison.py
│ └── {tier}/ │ ablation.py
│ comparison_*.json │ importance.py
├── analysis/{workload}/ ────► │ multi_seed.py
│ importance_results.json │
└── ... │ plots/
│ knob_importance.py
│ knob_dependence.py
│ knob_interaction_heatmap.py
│
│ registry.py ◄── @register_figure
│ theme.py ◄── PBTuneTheme + VenuePreset
│ export.py
│
▼
figures/{fig_id}.{pdf|png|svg}
Table of Contents¶
- CLI
- The figure registry
- Theme engine
- Loaders
- Built-in plots
- Adding a new figure
- Related documentation
For the design rationale behind the framework (auto-discovery, loader/renderer separation, why the theme owns sizing), see architecture/visualization.
CLI¶
# List every registered figure
python -m src.visualization --list
# Generate one figure by ID
python -m src.visualization --figure knob_importance
# Generate all figures in a category
python -m src.visualization --category importance
# Pick a venue preset (sizing + typography)
python -m src.visualization --venue pvldb # default
python -m src.visualization --venue springer
python -m src.visualization --venue preview # bigger, on-screen review
# Point at a non-default results tree
python -m src.visualization --data-dir results/ --output-dir figures/
Available venues: pvldb (PVLDB / VLDB single+double column widths, 9 pt serif, LaTeX), springer (Springer LNCS, 10 pt serif), preview (larger sans-serif for on-screen review). The widths and typography are encoded as VenuePreset records in theme.py.
The CLI auto-discovers plot modules: importing src.visualization.plots triggers each module's top-level register_figure(...) call. Adding a new module under src/visualization/plots/ is enough to make it visible to --list.
The figure registry¶
Location: src/visualization/registry.py
The registry tracks every figure by a stable string ID along with its category, paper section, generator, and data requirements.
@dataclass
class FigureSpec:
fig_id: str
paper_label: str
title: str
section: str
category: str
size_hint: str # "single" or "double" column
generator: Callable # produces AND exports the figure
data_requirements: list[str] # e.g. ["session_json"]
description: str
A plot module registers itself at import time:
from src.visualization.registry import register_figure
from src.visualization.types import FigureSpec
register_figure(FigureSpec(
fig_id="knob_importance",
paper_label="fig:importance",
title="Knob importance overview",
section="analysis",
category="importance",
size_hint="single",
generator=generate_knob_importance,
data_requirements=["session_json"],
description="fANOVA bars with SHAP beeswarm (top 10 knobs).",
))
Public registry methods:
| Method | Purpose |
|---|---|
register(spec) |
Add a figure (warns on overwrite). |
get(fig_id) |
Retrieve by ID, raises FigureRegistryError if not found. |
list_all() / list_by_category(c) / list_by_section(s) |
Enumerate. |
_discover_plots() |
Walk src.visualization.plots and import every module. Triggered automatically by the CLI. |
Auto-discovery means contributors do not edit the registry directly — they write a module under src/visualization/plots/ and the registration happens at import time.
Theme engine¶
Location: src/visualization/theme.py
PBTuneTheme enforces consistent matplotlib styling across every figure for a chosen venue.
@dataclass
class VenuePreset:
name: str
single_col_width_in: float
double_col_width_in: float
base_font_size_pt: int
font_family: str
use_latex: bool
class PBTuneTheme:
VENUE_PRESETS: dict[str, VenuePreset] = {
"pvldb": VenuePreset(name="pvldb", single_col_width_in=3.33, ...),
"springer": VenuePreset(name="springer", single_col_width_in=3.39, ...),
"preview": VenuePreset(name="preview", ...),
}
def rc_params(self) -> dict[str, Any]: ...
def figure(self, size_hint: str = "single", aspect=None, **kwargs) -> tuple[Figure, Axes]: ...
def subplots(self, nrows, ncols, size_hint="single", aspect=None, **kwargs) -> tuple[Figure, Any]: ...
@contextmanager
def apply(self) -> Iterator[None]: ...
The theme owns:
- Figure sizing — single-column / double-column widths are venue-specific. A generator requests a size by hint (
theme.figure(size_hint="single")or"double"), which the theme resolves into aFigureSize— a frozenwidth_in/height_indataclass built viaFigureSize.single_column(...)/FigureSize.double_column(...). - Typography — base font size + family. With
use_latex=Truethe theme switches to LaTeX rendering for all text; the resulting PDFs embed Type 1 fonts compatible with PVLDB / Springer requirements. - Color palette — the colorblind-friendly palette from src/visualization/colors.py. Generators should pull colors from
colors.py(e.g.get_method_style()/METRIC_COLORS) rather than picking colors directly. - Axes styling — uniform tick formatting, spine styles, grid behaviour.
Generators should never mutate plt.rcParams directly. with theme.apply(): installs the theme's rcParams for the plotting block and restores them on exit.
Loaders¶
Location: src/visualization/loaders/
Each loader knows how to walk a results subtree and build a typed dataclass for the renderer to consume.
| Loader | Reads | Produces |
|---|---|---|
session.py |
results/sessions/{workload}/{strategy}/{tier}/traces/trace_*.json |
TuningSession (per-generation history, best config, score breakdown, metadata) |
baseline.py |
Default-PostgreSQL baseline JSONs under results/{workload}/baselines/ |
Baseline metric distributions |
comparison.py |
results/comparisons/{workload}/{tier}/comparison_*.json |
ComparisonReport from the post-hoc evaluation suite (see EVALUATION_SUITE.md) |
ablation.py |
Multi-config sweeps for ablation tables | Per-condition metric records |
importance.py |
results/analysis/{workload}/importance_results.json |
fANOVA + TreeSHAP per-knob importance + pairwise interactions |
multi_seed.py |
Multiple seed-tagged session JSONs | Per-seed convergence curves with mean / std bands |
Loaders are deliberately schema-tolerant: they accept the current session JSON layout and the legacy fixed_v1 layout from older runs. Version migration goes through the same scoring-policy compatibility branch as the post-hoc evaluation suite, so a figure rendered today can include data from a session recorded months ago.
Built-in plots¶
Location: src/visualization/plots/
| Plot module | fig_id |
What it shows |
|---|---|---|
knob_importance.py |
knob_importance |
Per-knob fANOVA / SHAP importance bar chart with rank-correlation diagnostic. |
knob_dependence.py |
knob_dependence |
SHAP dependence plots for the top-K knobs, with hardware-feature coloring. |
knob_interaction_heatmap.py |
knob_interaction_heatmap |
Pairwise interaction heatmap from fANOVA second-order terms. |
Convergence and Pareto plots for the PBT-vs-BO comparison are produced by src/scripts/pbt_vs_bo_comarison.py — see PBT_VS_BO_COMPARISON.md. They predate the registry and are kept in src/scripts/ for now; migrating them into the registry is tracked as a follow-up.
Adding a new figure¶
- Create a loader under
src/visualization/loaders/. Return a dataclass; do not pass raw dicts into generators. - Create a plot module under
src/visualization/plots/. The module must: - import
register_figureandFigureSpec, - define a
generate_<your_figure>(*, data_dir, output_dir, theme, formats) -> Nonefunction that loads its data, opens awith theme.apply():block, sizes figures viatheme.figure(size_hint=...)/theme.subplots(...), and writes them withexport_figure(...), - call
register_figure(FigureSpec(...))at module top level. - Run
python -m src.visualization --listto confirm registration. - Run
python -m src.visualization --figure <your_id> --venue previewto iterate quickly without LaTeX.
Use the existing knob_importance module as a template — it covers the loader → theme → render → export round-trip in a small file.
Related documentation¶
- architecture/visualization — design rationale for the framework.
- Knob Importance Analysis — what the importance plots draw from.
- PBT vs BO Comparison — convergence / Pareto / resource-efficiency PDFs from the comparison script.
- Evaluation Runbook — generating the comparison JSONs that feed the comparison loader.
File locations¶
- CLI entry: src/visualization/main.py
- Registry: src/visualization/registry.py
- Theme: src/visualization/theme.py
- Types (
FigureSpec,VenuePreset,ExportFormat,FigureSize): src/visualization/types.py - Colors: src/visualization/colors.py
- Export helpers: src/visualization/export.py
- Loaders: src/visualization/loaders/
- Plots: src/visualization/plots/
- Tests: tests/unit/visualization/test_types.py