Main libE Module - Calling Script

The libE module is the outer libEnsemble routine.

This module sets up the manager and the team of workers, configured according to the contents of the libE_specs dictionary. The manager/worker communications scheme used in libEnsemble is parsed from the comms key if present, with valid values being mpi, local (for multiprocessing), or tcp. MPI is the default; if a communicator is specified, each call to this module will initiate manager/worker communications on a duplicate of that communicator. Otherwise, a duplicate of COMM_WORLD will be used.

In the vast majority of cases, programming with libEnsemble involves the creation of a calling script, a Python file where libEnsemble is parameterized via the various specification dictionaries (e.g. libE_specs, sim_specs, and gen_specs). The outer libEnsemble routine libE() is imported and called with such dictionaries to initiate libEnsemble. A simple calling script (from the first tutorial) may resemble:

 1import numpy as np
 2from libensemble.libE import libE
 3from generator import gen_random_sample
 4from simulator import sim_find_sine
 5from import add_unique_random_streams
 7nworkers, is_manager, libE_specs, _ = parse_args()
 9libE_specs["save_every_k_gens"] = 20
11gen_specs = {"gen_f": gen_random_sample,
12             "out": [("x", float, (1,))],
13             "user": {
14                "lower": np.array([-3]),
15                "upper": np.array([3]),
16                "gen_batch_size": 5
17                }
18             }
20sim_specs = {"sim_f": sim_find_sine,
21             "in": ["x"],
22             "out": [("y", float)]}
24persis_info = add_unique_random_streams({}, nworkers+1)
26exit_criteria = {"sim_max": 80}
28H, persis_info, flag = libE(sim_specs, gen_specs, exit_criteria, persis_info,
29                            libE_specs=libE_specs)

This will initiate libEnsemble with a Manager and nworkers workers (parsed from the command line), and runs on laptops or supercomputers. If an exception is encountered by the manager or workers, the history array is dumped to file, and MPI abort is called.

An alternative approach to parameterizing and interacting with libEnsemble via Ensemble objects and yaml files is available, but requires pyyaml to be installed. The equivalent of above resembles:

 1import numpy as np
 2from libensemble import Ensemble
 4my_experiment = Ensemble()
 7my_experiment.gen_specs["user"]["lower"] = np.array([-3])
 8my_experiment.gen_specs["user"]["upper"] = np.array([3])
10H, persis_info, flag =

The remaining parameters may be found in a yaml file that resembles:

 2    save_every_k_gens: 20
 3    exit_criteria:
 4        sim_max: 80
 7    function: generator.gen_random_sample
 8    outputs:
 9        x:
10            type: float
11            size: 1
12    user:
13        gen_batch_size: 5
16    function: simulator.sim_find_sine
17    inputs:
18        - x
19    outputs:
20        y:
21            type: float

On macOS (since Python 3.8) and Windows, the default multiprocessing start method is 'spawn' and you must place most calling script code (or just libE() / Ensemble().run() at a minimum) in an ``if __name__ == “__main__:” block.

Therefore a calling script that is universal across all platforms and comms-types may resemble:

 1import numpy as np
 2from libensemble.libE import libE
 3from generator import gen_random_sample
 4from simulator import sim_find_sine
 5from import add_unique_random_streams
 7if __name__ == "__main__:
 9    nworkers, is_manager, libE_specs, _ = parse_args()
11    libE_specs["save_every_k_gens"] = 20
13    gen_specs = {"gen_f": gen_random_sample,
14                "out": [("x", float, (1,))],
15                "user": {
16                    "lower": np.array([-3]),
17                    "upper": np.array([3]),
18                    "gen_batch_size": 5
19                    }
20                }
22    sim_specs = {"sim_f": sim_find_sine,
23                "in": ["x"],
24                "out": [("y", float)]}
26    persis_info = add_unique_random_streams({}, nworkers+1)
28    exit_criteria = {"sim_max": 80}
30    H, persis_info, flag = libE(sim_specs, gen_specs, exit_criteria, persis_info,
31                                libE_specs=libE_specs)

Alternatively, you may set the multiprocesing start method to 'fork' via the following:

from multiprocessing import set_start_method set_start_method(“fork”)

But note that this is incompatible with some libraries.

See below for the complete traditional libE() API.

libE.libE(sim_specs, gen_specs, exit_criteria, persis_info=None, alloc_specs=None, libE_specs=None, H0=None)
  • sim_specs (dict) – Specifications for the simulation function (example)

  • gen_specs (dict) – Specifications for the generator function (example)

  • exit_criteria (dict) – Tell libEnsemble when to stop a run (example)

  • persis_info (dict, optional) – Persistent information to be passed between user functions (example)

  • alloc_specs (dict, optional) – Specifications for the allocation function (example)

  • libE_specs (dict, optional) – Specifications for libEnsemble (example)

  • H0 (NumPy structured array, optional) – A previous libEnsemble history to be prepended to the history in the current libEnsemble run (example)


  • H (NumPy structured array) – History array storing rows for each point. (example)

  • persis_info (dict) – Final state of persistent information (example)

  • exit_flag (int) – Flag containing final task status

    0 = No errors
    1 = Exception occurred
    2 = Manager timed out and ended simulation
    3 = Current process is not in libEnsemble MPI communicator