libEnsemble Tutorial
The following libe_basic_ex.py code is an example of basic ParMOO + libEnsemble usage from the Extras and Plugins section of the User Guide.
import numpy as np
from parmoo.extras.libe import libE_MOOP
from parmoo.searches import LatinHypercube
from parmoo.surrogates import GaussRBF
from parmoo.acquisitions import UniformWeights
from parmoo.optimizers import GlobalSurrogate_PS
# When running with MPI, we need to keep track of which thread is the manager
# using libensemble.tools.parse_args()
from libensemble.tools import parse_args
_, is_manager, _, _ = parse_args()
# All functions are defined below.
def sim_func(x):
sx = np.array([(x["x1"] - 0.2) ** 2, (x["x1"] - 0.8) ** 2])
return 99. - 99. * (x["x2"] == 0) + sx
def obj_f1(x, s):
return s["MySim"][0]
def obj_f2(x, s):
return s["MySim"][1]
def const_c1(x, s):
return 0.1 - x["x1"]
# When using libEnsemble with Python MP, the "solve" command must be enclosed
# in an "if __name__ == '__main__':" block, as shown below
if __name__ == "__main__":
# Create a libE_MOOP -- fix the random seed for reproducibility
my_moop = libE_MOOP(GlobalSurrogate_PS, hyperparams={'np_random_gen': 0})
# Add 2 design variables (one continuous and one categorical)
my_moop.addDesign({'name': "x1",
'des_type': "continuous",
'lb': 0.0, 'ub': 1.0})
my_moop.addDesign({'name': "x2", 'des_type': "categorical",
'levels': 3})
# Add the simulation (note the budget of 20 simulation evaluations
# during search phase)
my_moop.addSimulation({'name': "MySim",
'm': 2,
'sim_func': sim_func,
'search': LatinHypercube,
'surrogate': GaussRBF,
'hyperparams': {'search_budget': 20}})
# Add the objectives
my_moop.addObjective({'name': "f1", 'obj_func': obj_f1})
my_moop.addObjective({'name': "f2", 'obj_func': obj_f2})
# Add the constraint
my_moop.addConstraint({'name': "c1", 'constraint': const_c1})
# Add 3 acquisition functions
for i in range(3):
my_moop.addAcquisition({'acquisition': UniformWeights,
'hyperparams': {}})
# Turn on checkpointing -- creates files parmoo.moop & parmoo.surrogate.1
my_moop.setCheckpoint(True, filename="parmoo")
# Use sim_max = 30 to perform just 30 simulations
my_moop.solve(sim_max=30)
# Display the solution -- this "if" clause is needed when running with MPI
if is_manager:
results = my_moop.getPF(format="pandas")
print(results)
You can run the above script with MPI
mpirun -np N python3 libe_basic_ex.py
or with Python’s built-in multiprocessing module.
python3 libe_basic_ex.py --comms local --nworkers {N+1}
The resulting output is shown below.
x1 x2 f1 f2 c1
0 0.827822 0 0.394161 0.000774 -0.727822
1 0.710334 0 0.260440 0.008040 -0.610334
2 0.695881 0 0.245897 0.010841 -0.595881
3 0.645392 0 0.198374 0.023904 -0.545392
4 0.629416 0 0.184398 0.029099 -0.529416
5 0.624045 0 0.179814 0.030960 -0.524045
6 0.554025 0 0.125334 0.060504 -0.454025
7 0.552853 0 0.124505 0.061082 -0.452853
8 0.441567 0 0.058354 0.128475 -0.341567
9 0.429848 0 0.052830 0.137013 -0.329848
10 0.281606 0 0.006659 0.268733 -0.181606
11 0.261684 0 0.003805 0.289784 -0.161684
12 0.252531 0 0.002760 0.299722 -0.152531
13 0.249211 0 0.002422 0.303369 -0.149211