Extras and Plugins
Running in Parallel using libEnsemble
libEnsemble is a Python library to “coordinate concurrent evaluation of dynamic ensembles of calculations.” Read more about libEnsemble by visiting the libEnsemble documentation.
The libE_MOOP
class is used to solve
MOOPs using libEnsemble.
The libE_MOOP
class inherits from
MOOP
and supports all of the public methods in its API.
To create an instance of the libE_MOOP
class,
import it from the extras.libe
module and then create a MOOP, just as
you normally would.
The solve()
method has been redefined
to create a libEnsemble
Persistent Generator
function, which libEnsemble can call to generate batches of simulation
evaluations, which it will distribute over available resources.
Below we reproduce the example from the Quickstart
guide, using a libE_MOOP
object.
Note that it is always recommended that you turn on
checkpointing
when using libEnsemble.
Since setCheckpoint
method does not
support the usage of Python lambda
functions, each of the objectives
and constraints is explicitly defined.
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 LocalGPS
# 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):
if x["x2"] == 0:
return np.array([(x["x1"] - 0.2) ** 2, (x["x1"] - 0.8) ** 2])
else:
return np.array([99.9, 99.9])
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__":
# Fix the random seed for reproducibility
np.random.seed(0)
# Create a libE_MOOP
my_moop = libE_MOOP(LocalGPS)
# 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 sim evals 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, checkpoint_data=False, 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)
To run a ParMOO/libEnsemble script, first make sure that libEnsemble is installed. You can find instructions on how to do so under libEnsemble’s Advanced Installation documentation.
Next, run libEnsemble as described in the Running libEnsemble section. Common methods of running a libEnsemble script are with MPI
mpirun -np N python3 libe_basic_ex.py
and with Python’s built-in multiprocessing module.
python3 libe_basic_ex.py --comms local --nworkers N
Note: When running a
libE_MOOP
with Python multiprocessing, MacOS and Windows systems default to using thespawn
method. When using thespawn
method, one must enclose thelibE_MOOP.solve()
command inside anif __name__ == '__main__':
block, as shown in the example above. Read more about the issue here:
The result from running the example is shown below.
x1 x2 f1 f2 c1
0 0.742825 0 0.294659 0.003269 -0.642825
1 0.680283 0 0.230672 0.014332 -0.580283
2 0.616501 0 0.173473 0.033672 -0.516501
3 0.580369 0 0.144680 0.048238 -0.480369
4 0.555222 0 0.126183 0.059916 -0.455222
5 0.518980 0 0.101749 0.078972 -0.418980
6 0.475477 0 0.075888 0.105315 -0.375477
7 0.302503 0 0.010507 0.247503 -0.202503
8 0.201285 0 0.000002 0.358460 -0.101285