Top-Level Scripts

Many other examples of top-level scripts can be found in libEnsemble’s regression tests.

Local Sine Tutorial

This example is from the Local Sine Tutorial, meant to run with Python’s multiprocessing as the primary comms method.

examples/tutorials/simple_sine/test_local_sine_tutorial.py
 1import numpy as np
 2from gest_api.vocs import VOCS
 3from sine_gen_std import RandomSample
 4from sine_sim import sim_find_sine
 5
 6from libensemble import Ensemble
 7from libensemble.specs import ExitCriteria, GenSpecs, LibeSpecs, SimSpecs
 8
 9if __name__ == "__main__":  # Python-quirk required on macOS and windows
10    libE_specs = LibeSpecs(nworkers=4, comms="local")
11
12    vocs = VOCS(variables={"x": [-3, 3]}, objectives={"y": "EXPLORE"})  # Configure our generator with this object
13
14    generator = RandomSample(vocs)  # Instantiate our generator
15
16    gen_specs = GenSpecs(
17        generator=generator,  # Pass our generator and config to libEnsemble
18        vocs=vocs,
19        batch_size=4,
20    )
21
22    sim_specs = SimSpecs(
23        sim_f=sim_find_sine,  # Our simulator function
24        inputs=["x"],  # InputArray field names. "x" from gen_f output
25        out=[("y", float)],  # sim_f output. "y" = sine("x")
26    )  # sim_specs_end_tag
27
28    exit_criteria = ExitCriteria(sim_max=80)  # Stop libEnsemble after 80 simulations
29
30    ensemble = Ensemble(sim_specs, gen_specs, exit_criteria, libE_specs)
31    ensemble.run()  # start the ensemble. Blocks until completion.
32
33    history = ensemble.H  # start visualizing our results
34
35    print([i for i in history.dtype.fields])  # (optional) to visualize our history array
36    print(history)
37
38    import matplotlib.pyplot as plt
39
40    colors = ["b", "g", "r", "y", "m", "c", "k", "w"]
41
42    for i in range(1, libE_specs.nworkers + 1):  # type: ignore
43        worker_xy = np.extract(history["sim_worker"] == i, history)
44        x = [entry.tolist() for entry in worker_xy["x"]]
45        y = [entry for entry in worker_xy["y"]]
46        plt.scatter(x, y, label="Worker {}".format(i), c=colors[i - 1])
47
48    plt.title("Sine calculations for a uniformly sampled random distribution")
49    plt.xlabel("x")
50    plt.ylabel("sine(x)")
51    plt.legend(loc="lower right")
52    plt.savefig("tutorial_sines.png")

Electrostatic Forces with Executor

These examples are from a test for evaluating the scaling capabilities of libEnsemble by calculating particle electrostatic forces through a user application. This application is registered with the MPIExecutor, then submitted for execution in the sim_f. Note the use of the parse_args=True which allows reading arguments such as the number of workers from the command line.

Traditional Version

Run using five workers with:

python run_libe_forces.py -n 5

One worker runs a persistent generator and the other four run the forces simulations.

tests/scaling_tests/forces/forces_simple/run_libe_forces.py
 1#!/usr/bin/env python
 2import os
 3import sys
 4from pathlib import Path
 5
 6import numpy as np
 7from forces_simf import run_forces  # Sim func from current dir
 8
 9from libensemble import Ensemble
10from libensemble.executors import MPIExecutor
11from libensemble.gen_funcs.persistent_sampling import persistent_uniform as gen_f
12from libensemble.specs import ExitCriteria, GenSpecs, LibeSpecs, SimSpecs
13
14if __name__ == "__main__":
15    # Initialize MPI Executor
16    exctr = MPIExecutor()
17
18    # Register simulation executable with executor
19    sim_app = Path.cwd() / "../forces_app/forces.x"
20
21    if not os.path.isfile(sim_app):
22        sys.exit("forces.x not found - please build first in ../forces_app dir")
23
24    exctr.register_app(full_path=sim_app, app_name="forces")
25
26    # Parse number of workers, comms type, etc. from arguments
27    ensemble = Ensemble(parse_args=True, executor=exctr)
28    nsim_workers = ensemble.nworkers - 1  # One worker is for persistent generator
29
30    # Persistent gen does not need resources
31    ensemble.libE_specs = LibeSpecs(
32        num_resource_sets=nsim_workers,
33        sim_dirs_make=True,
34    )
35
36    ensemble.sim_specs = SimSpecs(
37        sim_f=run_forces,
38        inputs=["x"],
39        outputs=[("energy", float)],
40    )
41
42    ensemble.gen_specs = GenSpecs(
43        gen_f=gen_f,
44        inputs=[],  # No input when start persistent generator
45        persis_in=["sim_id"],  # Return sim_ids of evaluated points to generator
46        outputs=[("x", float, (1,))],
47        initial_batch_size=nsim_workers,
48        async_return=False,
49        user={
50            "lb": np.array([1000]),  # min particles
51            "ub": np.array([3000]),  # max particles
52        },
53    )
54
55    # Starts one persistent generator. Simulated values are returned in batch.
56
57    # Instruct libEnsemble to exit after this many simulations
58    ensemble.exit_criteria = ExitCriteria(sim_max=8)
59
60    # Run ensemble
61    ensemble.run()
62
63    if ensemble.is_manager:
64        # Note, this will change if changing sim_max, nworkers, lb, ub, etc.
65        print(f'Final energy checksum: {np.sum(ensemble.H["energy"])}')

APOSMM with a Standardized Generator

This example from the regression tests demonstrates the v2.0 gest-api interface: a standardized APOSMM generator class parameterized by a VOCS object, paired with a gest-api simulator callable. The generator runs on the manager thread by default, leaving all workers available for simulations.

tests/regression_tests/test_asktell_aposmm_nlopt.py
 1"""
 2Runs libEnsemble with APOSMM with the NLopt local optimizer.
 3
 4Execute via one of the following commands (e.g. 3 workers):
 5   mpiexec -np 4 python test_persistent_aposmm_nlopt.py
 6   python test_persistent_aposmm_nlopt.py --nworkers 3 --comms local
 7   python test_persistent_aposmm_nlopt.py --nworkers 3 --comms tcp
 8
 9When running with the above commands, the number of concurrent evaluations of
10the objective function will be 2, as one of the three workers will be the
11persistent generator.
12"""
13
14# Do not change these lines - they are parsed by run-tests.sh
15# TESTSUITE_COMMS: local mpi tcp
16# TESTSUITE_NPROCS: 3
17
18from math import gamma, pi, sqrt
19
20import numpy as np
21
22import libensemble.gen_funcs
23from libensemble.executors.mpi_executor import MPIExecutor
24from libensemble.sim_funcs import six_hump_camel
25
26# Import libEnsemble items for this test
27
28libensemble.gen_funcs.rc.aposmm_optimizers = "nlopt"
29from time import time
30
31from gest_api.vocs import VOCS
32
33from libensemble import Ensemble
34from libensemble.gen_classes import APOSMM
35from libensemble.manager import LoggedException
36from libensemble.specs import ExitCriteria, GenSpecs, SimSpecs
37from libensemble.tests.regression_tests.support import six_hump_camel_minima as minima
38
39
40def six_hump_camel_func(x):
41    """
42    Definition of the six-hump camel
43    """
44    x1 = x["core"]
45    x2 = x["edge"]
46    term1 = (4 - 2.1 * x1**2 + (x1**4) / 3) * x1**2
47    term2 = x1 * x2
48    term3 = (-4 + 4 * x2**2) * x2**2
49
50    return {"energy": term1 + term2 + term3}
51
52
53# Main block is necessary only when using local comms with spawn start method (default on macOS and Windows).
54if __name__ == "__main__":
55
56    for run in range(3):
57
58        workflow = Ensemble(parse_args=True)
59
60        if workflow.is_manager:
61            start_time = time()
62
63        n = 2
64
65        vocs = VOCS(
66            variables={"core": [-3, 3], "edge": [-2, 2], "core_on_cube": [-3, 3], "edge_on_cube": [-2, 2]},
67            objectives={"energy": "MINIMIZE"},
68        )
69
70        aposmm = APOSMM(
71            vocs,
72            max_active_runs=workflow.nworkers,  # should this match nworkers always? practically?
73            variables_mapping={"x": ["core", "edge"], "x_on_cube": ["core_on_cube", "edge_on_cube"], "f": ["energy"]},
74            initial_sample_size=100,
75            sample_points=minima,
76            localopt_method="LN_BOBYQA",
77            rk_const=0.5 * ((gamma(1 + (n / 2)) * 5) ** (1 / n)) / sqrt(pi),
78            xtol_abs=1e-6,
79            ftol_abs=1e-6,
80        )
81
82        workflow.gen_specs = GenSpecs(
83            generator=aposmm,
84            vocs=vocs,
85            batch_size=5,
86            initial_batch_size=10,
87        )
88
89        if run == 0:
90            workflow.sim_specs = SimSpecs(simulator=six_hump_camel_func, vocs=vocs)
91            workflow.exit_criteria = ExitCriteria(sim_max=2000)