Plotting the progress and results#

Introduction#

Every optimizer in nextnano optimizers records its run simulations in the Simulation_*.log, Simulation_*.json and Simulation_*.dat files. Using those files, you can visualize the progress of the optimization or analyze the results afterwards. The plotting is available for all optimizers, and the interface is the same for all of them.

In all the examples below, optimizer is an instance of LocalOptimizer, BayesianOptimization or Evolution.

Note

The plotting functionality is optional and requires the additional dependency nextnano_optimizers[plot]. If you do not have it installed, the plotting methods will raise an error.

Watching the optimization in real time#

You can track the progress of optimization while it is running by passing live_progress=True to run().

optimizer.run(
    ...,
    live_progress=True,
)

This opens a separate window showing the objective values and input variables of the evaluated candidates. The figures update automatically as new evaluations are completed.

The window closes automatically when the run finishes.

Analyzing the live progress, one can judge whether the objective improves at all, or whether the search is stuck somewhere. In the latter case, it is advisable to stop the run and reconsider the optimization hyperparameters instead of waiting for the remaining evaluations.

Plotting the results after a run#

After completing an optimization, call plot_all() to generate figures from the recorded results:

optimizer.plot_all()

By default, the figures are saved in the figures subdirectory of the optimization output directory.

To display them in a window instead of saving them:

optimizer.plot_all(show=True, save_dir=False) # only show the figure in a window, do not save

You can also specify a directory where the figures are saved and the file format for the figures:

optimizer.plot_all(save_dir="path/to/the/folder", fig_format="pdf")

The available formats are "png", "svg" and "pdf".

Available plots#

Optimization progress#

The progress figure shows how the objective values and input variables of the evaluated designs change during the optimization.

It contains two groups of plots:

  • Objective values

  • Input variables

Each objective value and input varible has a separate plot on the progress figure. For example, the optimization with 2 objectives and 3 input variables will have 5 plots in total.

For LocalOptimizer and BayesianOptimization, each optimization step corresponds to one design evaluation.

For Evolution, each optimization step contains \(n\) evaluations, where \(n\) is a population size. Therefore the Evolution progress figures will have \(n\) points for each optimization step.

Final population figure for the Evolution#

For evolutionary algorithms, plot_all generates additional figures to show the final population in both objective space and input space.

The input-variable figure shows the final population, with a separate plot for each pair of input variables. For example, the evolution with 3 input variables well_width, barrier_width and doping will have 3 plots with pairs:

  • well_width vs. barrier_width

  • well_width vs. doping

  • barrier_width vs. doping.

Each point represents one candidate, and the Pareto solutions of the final generation are highlighted.

The objectives figure follows the same convetion: each pair of objectives have a separate plot and the Pareto front is highlighted.

Customizing the plots#

You can customize the figures using setup_plotting. The configuration applies to both the live_progress``figrue and figures generated by ``plot_all().

Axis labels#

By default, the plots use the input-variable names as input labels and generic label like objective or fitness for objectives. You can replace them with more descriptive labels and include physical units:

optimizer.setup_plotting(
    param_names=[
        "Well width (nm)",
        "Barrier width (nm)",
        "Alloy fraction",
    ],
    fitness_names=["Energy error (eV)"],
)

The order of param_names must match variable_names in IO. Similarly, fitness_names must contain one label for each objective returned by Metric.

Selecting the progress curves#

By default, the progress figure shows the evaluated candidates together with summary curves describing the optimization.

You can choose which curves to display using progress_series:

optimizer.setup_plotting(
    progress_series=["candidates", "best"],
)

The two curves are:

  • candidates: Values of the evaluated candidates.

  • best: Best objective value found so far.

For evolutionary optimization, additional curves are available (applies both to input variables and objective values):

  • mean: Mean value of the population.

  • median: Median value of the population.

  • min-max: Range between the minimum and maximum values in the population.

These curves help you understand how the population evolves, rather than focusing only on the best candidate.

For example:

optimizer.setup_plotting(
    progress_series=["candidates", "mean", "best"],
)

The complete set of available curves is described in the API reference of the individual optimizers: LocalOptimizer, BayesianOptimization and Evolution.

Plotting a previous run#

The plotting methods described above are called through an optimizer object. You can also create figures directly from a JSON log file. Every optimization writes a Simulation_*.json file containing the evaluated input variables and their objective values.

To plot a previously completed run:

from nextnano_optimizers.plotting import OptimizationPlotter

plotter = OptimizationPlotter.from_json(
    "Simulation_2026_10_19.json", # path to the json file
)
plotter.plot_all(show=True)

This is useful when you want to analyze an earlier optimization or regenerate figures with different settings.

Additional information#

See the individual API reference of every optimizer for the full set of plotting options: LocalOptimizer, BayesianOptimization and Evolution.

See nextnano_optimizers.plotting module for the OptimizationPlotter API.