nextnano_optimizers.plotting module#
Visualization of Evolution results. The
module is a read-only consumer of the JSON log a run already writes, so it never
re-runs a simulation and works equally well on a log from last week.
matplotlib is an optional dependency of the package. Importing this module
without it raises a clear error and the rest of nextnano_optimizers keeps
working. Install it with:
pip install nextnano_optimizers[plot]
EvolutionPlotter is the offline figure producer — construct it from a
live Evolution, from a log path, or from
raw arrays, then call one of the plot_* methods. Each returns the matplotlib
Figure, so the caller can compose or restyle further.
LogWatcher is the live counterpart: it owns a window and a timer,
re-reads the log when it changes on disk, and redraws. It is what
Evolution.run(live_progress=True) spawns in a separate process, so the
window’s event loop is never starved by the running simulation.
|
Visualizes the outcome of an |
|
Build a plotter from a finished |
|
Build a plotter from a JSON log file on disk (offline use). |
|
Build a plotter straight from in-memory per-evaluation arrays. |
|
Per-generation progress of objectives and parameters. |
Final population in objective space, Pareto front highlighted. |
|
Final population in input (decision-variable) space, Pareto highlighted. |
|
|
Produce all available figures (progress, objective space, input space). |
|
Live viewer for a (possibly still-running) evolution JSON log. |
|
Open a live-updating progress window for an evolution JSON log (blocking). |
Usage#
Straight after a run — no log path needed:
evolution.run(gen=30)
EvolutionPlotter.from_evolution(evolution).plot_all(save_dir=".", show=True)
Offline, from a log written earlier, with axis labels and outliers dropped:
plotter = EvolutionPlotter.from_json(
"Simulation_2025_09_17.json",
param_names=["well width", "barrier width"],
fitness_names=["gain", "current"],
exclude_failed=True,
)
fig = plotter.plot_pareto()
fitness_limits discards solutions outside per-objective bounds from every
panel, so all panels keep showing the same population:
plotter = EvolutionPlotter.from_json(
"Simulation_2025_09_17.json",
fitness_limits={"fitness2": (None, 1e3)},
)
Command line#
The module also runs as a script. Without --watch it shows the offline
figures for a log; with --watch it opens the live viewer that polls the log
and redraws while a run is still writing it:
python -m nextnano_optimizers.plotting Simulation_2025_09_17.json
python -m nextnano_optimizers.plotting Simulation_2025_09_17.json --watch
Reference#
- class nextnano_optimizers.plotting.EvolutionPlotter(data: dict, log_path: str | None = None, param_names: list | None = None, fitness_names: list | None = None, exclude_failed: bool = False, fitness_limits: list | dict | None = None, progress_series: list | str | None = None)#
Bases:
objectVisualizes the outcome of an
Evolutionrun.Every
plot_*method returns the matplotlibFigure(and accepts style kwargs), so results can be further composed or restyled by the caller.- Parameters:
- datadict
Parsed JSON log, i.e.
{"simulation": {"gen1": [...], ...}, "summary": {...}}as written byEvolution.run().- log_pathstr, optional
Path the
datacame from. Used only to name saved PNGs next to it.- param_names, fitness_nameslist of str, optional
Axis labels for the input variables / objectives. Default to
param1..N/fitness1..M.- exclude_failedbool, optional
Drop evaluations where every fitness component equals
summary["non_execution_penalty"](failed simulations) before plotting. DefaultFalse: opt-in, because it is only a heuristic – a genuine solution whose fitness happens to equal the penalty would be dropped too. Enable it when penalty outliers flatten the axes.- fitness_limitslist or dict, optional
Per-objective
(min, max)bounds; a solution whose fitness falls outside any given bound is discarded from every plot (objective, parameter, objective-space and input-space panels alike), so all panels keep showing the same population. Either a list with one entry per objective (Noneentries mean “no limit on this objective”) or a sparse dict keyed by objective name or index:fitness_limits=[(0, 100), None] fitness_limits={"fitness2": (None, 1e3)}
Noneinside a pair leaves that side unbounded. A NaN fitness in a limited objective is discarded too (NaN never satisfies a bound). Note the Pareto highlights come from the run summary and are not filtered, so a tight limit can leave a highlighted point outside the surviving scatter.- progress_seriessequence of str or str, optional
Default selection of per-panel curves for
plot_progress(), any ofPROGRESS_SERIES(pass"all"for every curve).None(default) drawsDEFAULT_SERIES. Aseriesargument toplot_progress()(orprogress_seriestoplot_all()) overrides it per call.
Methods
from_arrays(generation, params, fits, *[, ...])Build a plotter straight from in-memory per-evaluation arrays.
from_evolution(evolution, **kwargs)Build a plotter from a finished
Evolutioninstance.from_json(json_path, **kwargs)Build a plotter from a JSON log file on disk (offline use).
plot_all([save_dir, show, progress_series])Produce all available figures (progress, objective space, input space).
Final population in input (decision-variable) space, Pareto highlighted.
Final population in objective space, Pareto front highlighted.
plot_progress([fig, series])Per-generation progress of objectives and parameters.
validate_config(*[, expected_n_params, ...])Validate plotting keyword arguments without building a plotter.
- classmethod from_arrays(generation, params, fits, *, summary=None, param_names=None, fitness_names=None, exclude_failed=False, fitness_limits=None, progress_series=None) EvolutionPlotter#
Build a plotter straight from in-memory per-evaluation arrays.
Bypasses the JSON log entirely – used for live plotting during a run, where the accumulated generation/params/fits are already in memory.
- Parameters:
- generationarray-like of int, shape (n,)
Generation index of each evaluated individual.
- paramsarray-like, shape (n, n_params)
Input-variable values of each evaluated individual.
- fitsarray-like, shape (n, n_obj)
Fitness values of each evaluated individual.
- summarydict, optional
Summary dict; only
non_execution_penaltymatters for the progress plot (failed-evaluation masking).- fitness_limitslist or dict, optional
Per-objective
(min, max)row filter; see the class docstring.
- classmethod from_evolution(evolution, **kwargs) EvolutionPlotter#
Build a plotter from a finished
Evolutioninstance.Reads the run’s JSON log directly from the evolution’s manager, so no log path is needed.
- classmethod from_json(json_path, **kwargs) EvolutionPlotter#
Build a plotter from a JSON log file on disk (offline use).
- plot_all(save_dir=None, show=False, progress_series=None)#
Produce all available figures (progress, objective space, input space).
- Parameters:
- save_dirstr, optional
If given, save PNGs there as
<logname>_progress.png/_pareto.png/_inputs.png. Ifsave_diris the string"next_to_log"and alog_pathis known, they are written next to the log.- showbool, optional
Call
plt.show()at the end. DefaultFalse(never blocks in library code).- progress_seriessequence of str or str, optional
Which per-panel curves the progress figure draws, forwarded to
plot_progress()(see itsseriesargument; pass"all"for every curve).None(default) falls back to the plotter’sprogress_series.
- plot_inputs()#
Final population in input (decision-variable) space, Pareto highlighted.
Input-space counterpart of
plot_pareto. ReturnsNonefor a single parameter – an input-space view needs at least two parameters to form a pair, and parameter-vs-generation is already covered byplot_progress. For >= 2 parameters it draws a scatter for every pair of parameters: the final population’s values as points, with the final-generation Pareto solutions highlighted.
- plot_pareto()#
Final population in objective space, Pareto front highlighted.
Returns
Nonefor a single objective – an objective-space view needs at least two objectives, and fitness-vs-generation is already covered byplot_progress. For >= 2 objectives it draws a scatter for every pair of objectives: the final population’s fitnesses as points, with the Pareto-front points highlighted; for exactly 2 objectives the Pareto points are connected into the front line.
- plot_progress(fig=None, series=None)#
Per-generation progress of objectives and parameters.
Returns the
Figure, laid out in two columns sharing the generation x-axis: objectives on the left, parameters on the right. Empty cells (when the objective and parameter counts differ) are hidden. When any evaluations are excluded (exclude_failed/fitness_limits), an extra panel directly below the objective panels charts the per-generation excluded counts, one line per reason (failed / out-of-limit).- Parameters:
- seriessequence of str, optional
Which per-panel curves to draw, any of
PROGRESS_SERIES(candidates,min-max,min,max,median,mean,best).None(default) falls back to the plotter’sprogress_series, which itself defaults toDEFAULT_SERIES(candidates,min-max,mean,best);min,maxandmedianare available but off by default. Pass an explicit list to override, e.g.series=["median", "min", "max"].best(per-generation minimum) is only drawn on the objective panels.- If ``fig`` is given, it is cleared and reused instead of creating a new
- figure – this lets a live view redraw into the same window (far cheaper
- than tearing down and recreating the figure every generation).
- classmethod validate_config(*, expected_n_params=None, expected_n_obj=None, **kwargs)#
Validate plotting keyword arguments without building a plotter.
Lets a caller that configures plotting ahead of time (notably
nextnano_optimizers.optimizer.Evolution.setup_plotting()) reject a bad kwarg at the point it was passed, rather than much later when the figures are drawn – or silently, in the detached live-viewer process whose stderr is discarded. Needs no data and no matplotlib.- Parameters:
- expected_n_params, expected_n_objint, optional
Problem dimensions to check the lengths of
param_names/fitness_namesagainst. LeaveNone(default) when unknown, and that length check is skipped.- **kwargs
The candidate plotting kwargs (the same ones
__init__accepts).
- Raises:
- TypeError
If
kwargsholds a name that is not anEvolutionPlotterkwarg.- ValueError
If
progress_seriesnames an unknown curve, orparam_names/fitness_nameshas a length other than the expected dimension.
- class nextnano_optimizers.plotting.LogWatcher(json_path, interval=1.0, **plotter_kwargs)#
Bases:
objectLive viewer for a (possibly still-running) evolution JSON log.
Owns one matplotlib figure and a GUI timer; each tick re-reads the log if it changed on disk and redraws the progress figure into the same window. Runs in its own process (see the module CLI), so the window’s event loop is never starved by the simulation.
- Parameters:
- json_pathstr
Path to the evolution JSON log to watch. It need not exist yet: the watcher waits for it to appear and for the first population to be logged.
- intervalfloat, optional
Polling interval in seconds. Default
1.0.- seriessequence of str, optional
Which per-panel curves to draw; forwarded to
EvolutionPlotter.plot_progress().Noneuses the default set.- plotter_kwargsdict, optional
Forwarded to
EvolutionPlotter(e.g.param_names,fitness_names,exclude_failed,fitness_limits).
Methods
start()Open the window and block in the GUI event loop until it is closed.
- start()#
Open the window and block in the GUI event loop until it is closed.
In the viewer process the GUI loop is the program, so this blocks. One immediate poll is done before starting the timer so a log that is already complete shows up without waiting a full interval.
- nextnano_optimizers.plotting.watch_progress(json_path, interval=1.0, **plotter_kwargs)#
Open a live-updating progress window for an evolution JSON log (blocking).