Companion Notebook¶
The NSGA2Generator
can be run on multi-objective optimization problems purely from Xopt's YAML configuration files. This notebook demonstrates how to set up the configuration file, how to run the optimization algorithm, and how to load the data into python and plot the results. At the end, we demonstrate how to restart an optimization from one of NSGA2Generator
's checkpoint files.
import matplotlib.pyplot as plt
import pandas as pd
import shutil
Calling the Runner Script¶
We have prepared a python script and YAML configuration file in the example directory accompanying this notebook. The script simply loads the YAMl file into an Xopt object and calls Xopt.run()
on it to perform the optimization until the termination condition is achieved.
# Run the optimizer (will take ~30s)
# We will run from the example directory containing `eval_fun.py` which is imported during our xopt run
! cd assets/yaml_runner_example && python run_xopt.py nsga2_zdt3.yml
Loading and Plotting Data¶
The YAML file specified that data will be output to the directory nsga2_output
within the working directory of the script (ie assets/yaml_runner_example
). In here, there will be several files.
populations.csv
: Each completed population is recorded to this filedata.csv
: Contains all evaluated invidualslog.txt
: A record of all log messages the genreator emitted during its runvocs.txt
: A copy of the variable, objectives, and constraints (VOCs) definitionscheckpoints/
: This directory contains checkpoint files which are used with thecheckpoint_file
key of the generator to restart an optimization.
# Load every generation
df = pd.read_csv("assets/yaml_runner_example/nsga2_output/populations.csv")
df.head()
x1 | x10 | x11 | x12 | x13 | x14 | x15 | x16 | x17 | x18 | ... | x6 | x7 | x8 | x9 | f1 | f2 | xopt_generation | xopt_candidate_idx | xopt_runtime | xopt_error | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.955186 | 0.563216 | 0.047489 | 0.797763 | 0.899631 | 0.929877 | 0.583179 | 0.008645 | 0.918215 | 0.114670 | ... | 0.473533 | 0.640601 | 0.855772 | 0.357572 | 0.955186 | 4.647630 | 1 | 13 | 0.000752 | False |
1 | 0.740723 | 0.318016 | 0.928848 | 0.979018 | 0.865635 | 0.916216 | 0.102883 | 0.228824 | 0.913055 | 0.505996 | ... | 0.738793 | 0.628864 | 0.153988 | 0.182097 | 0.740723 | 4.610105 | 1 | 27 | 0.000752 | False |
2 | 0.726432 | 0.656939 | 0.969461 | 0.057857 | 0.915231 | 0.170427 | 0.014723 | 0.930046 | 0.254713 | 0.870379 | ... | 0.235462 | 0.882605 | 0.706256 | 0.625705 | 0.726432 | 4.609342 | 1 | 12 | 0.000752 | False |
3 | 0.740064 | 0.923005 | 0.382839 | 0.819331 | 0.869693 | 0.888453 | 0.352812 | 0.103660 | 0.389123 | 0.773881 | ... | 0.495696 | 0.459118 | 0.960945 | 0.295776 | 0.740064 | 4.421723 | 1 | 38 | 0.000752 | False |
4 | 0.722345 | 0.555027 | 0.898036 | 0.934059 | 0.808855 | 0.127105 | 0.682440 | 0.439112 | 0.854153 | 0.257991 | ... | 0.553535 | 0.027826 | 0.956472 | 0.185148 | 0.722345 | 4.380401 | 1 | 20 | 0.000752 | False |
5 rows × 36 columns
# Grab just the final generation
last_gen = df[df["xopt_generation"] == df["xopt_generation"].max()]
last_gen.head()
x1 | x10 | x11 | x12 | x13 | x14 | x15 | x16 | x17 | x18 | ... | x6 | x7 | x8 | x9 | f1 | f2 | xopt_generation | xopt_candidate_idx | xopt_runtime | xopt_error | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
4950 | 0.250265 | 0.001119 | 0.008936 | 0.042515 | 0.000933 | 0.002398 | 0.005976 | 0.001093 | 0.014535 | 0.040344 | ... | 0.028752 | 0.005204 | 0.021499 | 0.005882 | 0.250265 | 0.345143 | 100 | 4971 | 0.000644 | False |
4951 | 0.829883 | 0.075591 | 0.004523 | 0.044634 | 0.003351 | 0.019838 | 0.009648 | 0.011639 | 0.006253 | 0.026488 | ... | 0.003473 | 0.031677 | 0.023200 | 0.011868 | 0.829883 | -0.479970 | 100 | 4834 | 0.000624 | False |
4952 | 0.019038 | 0.002404 | 0.002876 | 0.042552 | 0.002634 | 0.003438 | 0.000944 | 0.001305 | 0.013862 | 0.002033 | ... | 0.016838 | 0.065979 | 0.025784 | 0.002900 | 0.019038 | 0.962610 | 100 | 4973 | 0.000644 | False |
4953 | 0.638614 | 0.013705 | 0.004964 | 0.041057 | 0.002468 | 0.002024 | 0.032705 | 0.012701 | 0.012172 | 0.024998 | ... | 0.000960 | 0.005750 | 0.027612 | 0.005228 | 0.638614 | -0.325803 | 100 | 4961 | 0.000644 | False |
4954 | 0.622459 | 0.018980 | 0.000736 | 0.030645 | 0.002453 | 0.002548 | 0.004025 | 0.028325 | 0.013426 | 0.003721 | ... | 0.018244 | 0.005677 | 0.023643 | 0.004490 | 0.622459 | -0.108222 | 100 | 4675 | 0.000636 | False |
5 rows × 36 columns
# Plot the objectives
plt.scatter(last_gen["f1"], last_gen["f2"])
plt.xlabel("f1 (unitless)")
plt.ylabel("f2 (unitless)")
plt.title(f"Generation {last_gen['xopt_generation'].max()}")
Text(0.5, 1.0, 'Generation 100')
Restoring from Checkpoints¶
In this section, we will restart the optimizer from one of its saved checkpoints. This allows us to carry on an optimization that was previously terminated with no loss of information. The checkpoint is specified in the YAML file using the key checkpoint_file
. Any additional settings in the generator will override the settings included in the checkpoint. The evaluation function still needs to be defined and should be identical to what was used during the original run.
# Hack so you do not need to change the checkpoint filename in the config file manually for this tutorial
# In a real optimization, you will set `checkpoint_file` to the file of your choice
! cd assets/yaml_runner_example/nsga2_output/checkpoints && mv $(ls -1 | tail -n 1) 20250805_065102_1.txt
# Run the optimizer for another few generations (will take ~30s)
! cd assets/yaml_runner_example && python run_xopt.py nsga2_from_checkpoint.yml
# Grab the last generation
df = pd.read_csv(
"assets/yaml_runner_example/nsga2_from_checkpoint_output/populations.csv"
)
last_gen_restarted = df[df["xopt_generation"] == df["xopt_generation"].max()]
# Plot the objectives
plt.scatter(
last_gen["f1"],
last_gen["f2"],
label=f"Generation {last_gen['xopt_generation'].max()}",
)
plt.scatter(
last_gen_restarted["f1"],
last_gen_restarted["f2"],
label=f"Generation {last_gen_restarted['xopt_generation'].max()}",
)
plt.xlabel("f1 (unitless)")
plt.ylabel("f2 (unitless)")
plt.legend()
<matplotlib.legend.Legend at 0x7f520bbad7f0>
Cleanup¶
We will now remove the files created in this notebook.
# Clean up the output directoris
shutil.rmtree("assets/yaml_runner_example/nsga2_output")
shutil.rmtree("assets/yaml_runner_example/nsga2_from_checkpoint_output")