Plotting
Plotting utilities for the logs pytree returned by Experiment.run()
and Experiment.run_hparam_search(), so training can be inspected without
wandb (see Agent's docstring and issue #60: Experiment.run(log_to_wandb=
False) and reading logs directly is the fast path). A Benchmark-
scored run's own summary/details/diagnostics plots
(Benchmark.plot_summary/plot_details/plot_diagnostics) live in
navix/benchmarks/benchmark.py instead, next to the Benchmark
methods they render - not here, since Benchmark is the one place
that already knows those shapes.
MANDATORY_METRICS is a fixed, deliberately-chosen set of plots, rather
than auto-detecting whatever keys happen to be in logs. Each entry is
derivable purely from the (state, action, reward, done) interaction
stream plus wall-clock time - the one interface every RL algorithm
shares, regardless of its internals - so the set stays meaningful for any
future agent, not just the ones navix ships: agent/episode/returns,
agent/episode/success_rate, agent/episode/length,
experiment/costs/fps, experiment/costs/wall_time.
Identical across every navix agent so results are visually comparable
across algorithms - this is the set the navix leaderboard (#130) is
expected to standardise on.
This module deliberately does not know about "diagnostic" (algorithm-
specific) metrics - an algorithm submitted to a leaderboard won't
necessarily have any navix-specific code to declare which of its own
logged keys are diagnostic. That categorisation belongs to whatever
consumes this module (e.g. a leaderboard's own file mapping algorithm ->
diagnostic keys), not to navix itself. plot_metrics/plot_dashboard
both accept an arbitrary metrics dict for exactly this reason.
navix.agents.agent.derive_episodic_metrics (formerly defined here)
builds the agent/episode/* keys plot_metric/plot_dashboard expect in
logs - it moved out because it's load-bearing for Experiment.
run_hparam_search's fitness computation too, not just plotting, so a
plotting-only module was the wrong home for it.
MANDATORY_METRICS = {'agent/episode/returns': 'Episodic Return', 'agent/episode/success_rate': 'Success Rate', 'agent/episode/length': 'Episode Length', 'experiment/costs/fps': 'Training Throughput (steps/s)', 'experiment/costs/wall_time': 'Wall-clock Training Time (s)'}
module-attribute
The plots every navix agent's logs should support, so results are
directly comparable across algorithms. Kept intentionally small: only
metrics that (a) exist regardless of which algorithm produced logs, and
(b) are actually necessary to tell whether training worked at all.
NON_CURVE_DIAGNOSTICS_KEYS = frozenset({'benchmark/costs/wall_time', 'benchmark/costs/fps', 'benchmark/costs/flops', 'benchmark/costs/memory_bytes', 'benchmark/costs/compile_time_seconds'})
module-attribute
diagnostics.npz's scalar cost fields, not per-update curves - only
submit_entry (navix/benchmarks/benchmark.py) ever adds these, to
the npz-only curves dict, never to what curve_diagnostics() itself
returns (what Benchmark.plot_diagnostics plots) - so this documents
diagnostics.npz's real shape for any future direct consumer of that
file, rather than filtering anything plot_diagnostics itself sees.
format_scalar(value)
Renders one Benchmark.summary() value as a table cell: fixed-
precision for a float scalar, str() otherwise.
is_numeric_sequence(value)
Whether value converts to a rank->=1 float array - a
Benchmark.details() numeric, per-row column (as opposed to
env_ids, a Tuple[str, ...]).
plot_dashboard(logs, metrics=None, x_key='agent/train/frames', xlabel='Frames')
Plots metrics (MANDATORY_METRICS by default) as a single combined
figure, one panel per metric.
This is intentionally agnostic about "mandatory vs. diagnostic" -
navix doesn't know, and shouldn't need to know, what a given algorithm
considers diagnostic (an algorithm submitted to a leaderboard won't
necessarily have any navix-specific code to declare that in). That
categorisation belongs to whatever consumes this module - e.g. a
leaderboard's own file mapping algorithm -> diagnostic keys - which
can merge its own metrics dict with MANDATORY_METRICS and pass the
result here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logs
|
Dict[str, Array]
|
The |
required |
metrics
|
Dict[str, str]
|
A mapping of |
None
|
x_key
|
str
|
The key in |
'agent/train/frames'
|
xlabel
|
str
|
The x-axis label. |
'Frames'
|
Returns:
| Type | Description |
|---|---|
|
matplotlib.figure.Figure: The combined dashboard figure. |
plot_metric(logs, key, title=None, x_key='agent/train/frames', xlabel='Frames', ax=None)
Plots a single metric against x_key, aggregated with a mean line and
a min-max shaded band over any leading batch dimensions (e.g. seeds).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logs
|
Dict[str, Array]
|
The |
required |
key
|
str
|
The key in |
required |
title
|
str
|
The plot title. Defaults to |
None
|
x_key
|
str
|
The key in |
'agent/train/frames'
|
xlabel
|
str
|
The x-axis label. |
'Frames'
|
ax
|
Axes
|
An existing axes to draw
into. If |
None
|
Returns:
| Type | Description |
|---|---|
|
matplotlib.figure.Figure: The figure |
plot_metrics(logs, metrics, x_key='agent/train/frames', xlabel='Frames')
Plots each metric in metrics as its own standalone figure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logs
|
Dict[str, Array]
|
The |
required |
metrics
|
Dict[str, str]
|
A mapping of |
required |
x_key
|
str
|
The key in |
'agent/train/frames'
|
xlabel
|
str
|
The x-axis label. |
'Frames'
|
Returns:
| Type | Description |
|---|---|
Dict[str, Figure]
|
Dict[str, Figure]: One figure per metric, keyed by the same key |
Dict[str, Figure]
|
as |
row_labels(details)
The key in a Benchmark.details() dict that labels each row
(e.g. env_ids) - the first key whose value is a non-empty
sequence of strings. Falls back to positional labels if details
has none (every value is numeric).
Returns:
| Type | Description |
|---|---|
tuple
|
Tuple[Optional[str], list]: |
tuple
|
is |