Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ per-file-ignores =

# Need to set something before the APOSMM import
libensemble/tests/regression_tests/test_persistent_aposmm*:E402
libensemble/tests/regression_tests/test_aposmm*:E402
libensemble/tests/regression_tests/test_asktell_aposmm_nlopt.py:E402
libensemble/tests/regression_tests/test_persistent_gp_multitask_ax.py:E402
libensemble/tests/functionality_tests/test_uniform_sampling_then_persistent_localopt_runs.py:E402
Expand Down
95 changes: 95 additions & 0 deletions libensemble/tests/regression_tests/test_aposmm_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
Tests the APOSMM generator's ability to handle exceptions.

The periodic_func with LN_BOBYQA generates NLopt roundoff-limited errors,
which should propagate as exceptions to the calling script.

Execute via one of the following commands (e.g. 3 workers):
mpiexec -np 4 python test_aposmm_exception.py
python test_aposmm_exception.py --nworkers 3 --comms local

When running with the above commands, the number of concurrent evaluations of
the objective function will be 3, as the generator runs on the manager.
"""

# Do not change these lines - they are parsed by run-tests.sh
# TESTSUITE_COMMS: local mpi
# TESTSUITE_NPROCS: 4
# TESTSUITE_EXTRA: true

import numpy as np

import libensemble.gen_funcs

libensemble.gen_funcs.rc.aposmm_optimizers = "nlopt"

from gest_api.vocs import VOCS

from libensemble import Ensemble
from libensemble.gen_classes import APOSMM
from libensemble.specs import ExitCriteria, GenSpecs, LibeSpecs, SimSpecs


def periodic_func(x):
"""
Periodic test function (gest-api version of periodic_func.func_wrapper).
"""
from numpy import cos, sin

return {"f": sin(x["x0"]) * cos(x["x1"])}


# Main block is necessary only when using local comms with spawn start method (default on macOS and Windows).
if __name__ == "__main__":
workflow = Ensemble(parse_args=True)

vocs = VOCS(
variables={
"x0": [0, 2 * np.pi],
"x1": [-np.pi / 2, 3 * np.pi / 2],
"x0_on_cube": [0, 1],
"x1_on_cube": [0, 1],
},
objectives={"f": "MINIMIZE"},
)

aposmm = APOSMM(
vocs,
max_active_runs=6,
initial_sample_size=100,
variables_mapping={
"x": ["x0", "x1"],
"x_on_cube": ["x0_on_cube", "x1_on_cube"],
"f": ["f"],
},
localopt_method="LN_BOBYQA",
)

workflow.gen_specs = GenSpecs(
generator=aposmm,
vocs=vocs,
initial_batch_size=100,
)
workflow.sim_specs = SimSpecs(simulator=periodic_func, vocs=vocs)
workflow.exit_criteria = ExitCriteria(sim_max=1000)
workflow.libE_specs = LibeSpecs(abort_on_exception=False)

exception_raised = False
try:
workflow.run()
except Exception:
if workflow.is_manager:
exception_raised = True

if workflow.is_manager:
if workflow.libE_specs.comms == "mpi":
from mpi4py import MPI

if exception_raised:
print("\n\nMPI will be aborted as planned\n\n", flush=True)
MPI.COMM_WORLD.Abort(0)
else:
MPI.COMM_WORLD.Abort(1)
else:
assert exception_raised, "Expected an exception from the NLopt roundoff-limited error"
print("\n\nException received as expected")
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
Runs libEnsemble with APOSMM with the NLopt local optimizer.

Execute via one of the following commands (e.g. 3 workers):
mpiexec -np 4 python test_persistent_aposmm_nlopt.py
python test_persistent_aposmm_nlopt.py --nworkers 3 --comms local
python test_persistent_aposmm_nlopt.py --nworkers 3 --comms tcp
mpiexec -np 4 python test_aposmm_nlopt.py
python test_aposmm_nlopt.py --nworkers 3 --comms local
python test_aposmm_nlopt.py --nworkers 3 --comms tcp

When running with the above commands, the number of concurrent evaluations of
the objective function will be 2, as one of the three workers will be the
persistent generator.
the objective function will be 3, as the generator runs on the manager.
"""

# Do not change these lines - they are parsed by run-tests.sh
Expand All @@ -21,8 +20,6 @@

import libensemble.gen_funcs

# Import libEnsemble items for this test

libensemble.gen_funcs.rc.aposmm_optimizers = "nlopt"
from time import time

Expand All @@ -38,13 +35,12 @@ def six_hump_camel_func(x):
"""
Definition of the six-hump camel
"""
x1 = x["core"]
x2 = x["edge"]
x1 = x["x0"]
x2 = x["x1"]
term1 = (4 - 2.1 * x1**2 + (x1**4) / 3) * x1**2
term2 = x1 * x2
term3 = (-4 + 4 * x2**2) * x2**2

return {"energy": term1 + term2 + term3}
return {"f": term1 + term2 + term3}


# Main block is necessary only when using local comms with spawn start method (default on macOS and Windows).
Expand All @@ -58,39 +54,39 @@ def six_hump_camel_func(x):

vocs = VOCS(
variables={
"core": [-3, 3],
"edge": [-2, 2],
"core_on_cube": [0, 1],
"edge_on_cube": [0, 1],
"x0": [-3, 3],
"x1": [-2, 2],
"x0_on_cube": [0, 1],
"x1_on_cube": [0, 1],
},
objectives={"energy": "MINIMIZE"},
objectives={"f": "MINIMIZE"},
)

aposmm = APOSMM(
vocs,
max_active_runs=6,
variables_mapping={
"x": ["core", "edge"],
"x_on_cube": ["core_on_cube", "edge_on_cube"],
"f": ["energy"],
"x": ["x0", "x1"],
"x_on_cube": ["x0_on_cube", "x1_on_cube"],
"f": ["f"],
},
initial_sample_size=200,
initial_sample_size=100,
sample_points=np.round(minima, 1),
localopt_method="LN_BOBYQA",
rk_const=0.5 * ((gamma(1 + (n / 2)) * 5) ** (1 / n)) / sqrt(pi),
xtol_abs=1e-6,
ftol_abs=1e-6,
dist_to_bound_multiple=0.5,
)

workflow.gen_specs = GenSpecs(
generator=aposmm,
vocs=vocs,
batch_size=5,
initial_batch_size=10,
initial_batch_size=100,
)

workflow.sim_specs = SimSpecs(simulator=six_hump_camel_func, vocs=vocs)
workflow.exit_criteria = ExitCriteria(sim_max=3000, wallclock_max=600)
workflow.exit_criteria = ExitCriteria(sim_max=3000)

# Perform the run
H, _, _ = workflow.run()
Expand Down
96 changes: 96 additions & 0 deletions libensemble/tests/regression_tests/test_aposmm_periodic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""
Tests the 'periodic' domain use case for APOSMM with an NLopt local
optimization method.

Execute via one of the following commands (e.g. 3 workers):
mpiexec -np 4 python test_aposmm_periodic.py
python test_aposmm_periodic.py --nworkers 3 --comms local

When running with the above commands, the number of concurrent evaluations of
the objective function will be 3, as the generator runs on the manager.
"""

# Do not change these lines - they are parsed by run-tests.sh
# TESTSUITE_COMMS: local mpi
# TESTSUITE_NPROCS: 4
# TESTSUITE_EXTRA: true

import numpy as np

import libensemble.gen_funcs

libensemble.gen_funcs.rc.aposmm_optimizers = "nlopt"

from gest_api.vocs import VOCS

from libensemble import Ensemble
from libensemble.gen_classes import APOSMM
from libensemble.specs import ExitCriteria, GenSpecs, SimSpecs


def periodic_func(x):
"""
Periodic test function (gest-api version of periodic_func.func_wrapper).
"""
from numpy import cos, sin

return {"f": sin(x["x0"]) * cos(x["x1"])}


# Main block is necessary only when using local comms with spawn start method (default on macOS and Windows).
if __name__ == "__main__":
workflow = Ensemble(parse_args=True)

vocs = VOCS(
variables={
"x0": [0, 2 * np.pi],
"x1": [-np.pi / 2, 3 * np.pi / 2],
"x0_on_cube": [0, 1],
"x1_on_cube": [0, 1],
},
objectives={"f": "MINIMIZE"},
)

variables_mapping = {
"x": ["x0", "x1"],
"x_on_cube": ["x0_on_cube", "x1_on_cube"],
"f": ["f"],
}

aposmm = APOSMM(
vocs,
max_active_runs=6,
initial_sample_size=100,
variables_mapping=variables_mapping,
localopt_method="LN_BOBYQA",
xtol_abs=1e-8,
ftol_abs=1e-8,
periodic=True,
print=True,
)

workflow.gen_specs = GenSpecs(
generator=aposmm,
vocs=vocs,
initial_batch_size=100,
)
workflow.sim_specs = SimSpecs(simulator=periodic_func, vocs=vocs)
workflow.exit_criteria = ExitCriteria(sim_max=1000)

H, _, _ = workflow.run()

if workflow.is_manager:
min_ids = np.where(H["local_min"])

# The minima are known on this test problem. If the above [lb, ub] domain is
# shifted/scaled to [0,1]^n, they all have value [0.25, 0.75] or [0.75, 0.25]
minima = np.array([[0.25, 0.75], [0.75, 0.25]])
tol = 2e-4

for x in H["x_on_cube"][min_ids]:
print(x)
print(np.linalg.norm(x - minima[0]))
print(np.linalg.norm(x - minima[1]), flush=True)

for x in H["x_on_cube"][min_ids]:
assert np.linalg.norm(x - minima[0]) < tol or np.linalg.norm(x - minima[1]) < tol
Loading
Loading