Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
77f1166
initial commit making the swap, adjust docs
jlnav Sep 5, 2025
89d07ab
adjust many tests to import/use give_sim_work_first. round 1
jlnav Sep 5, 2025
8dce152
1d_sampling swapped to use persistent_uniform
jlnav Sep 11, 2025
abb5c0b
Merge branch 'develop' into refactor/only_persistent_gens
jlnav Oct 24, 2025
ba507b3
Merge branch 'refactor/only_persistent_gens' into asktell/only_persis…
jlnav Dec 18, 2025
221ad1b
remove a unit test that is frankely redundant. Then in all the ask/te…
jlnav Dec 18, 2025
110c7f4
Merge branch 'experimental/jlnav_plus_shuds_asktell' into asktell/onl…
jlnav Feb 12, 2026
5a0bc55
Merge branch 'experimental/jlnav_plus_shuds_asktell' into asktell/onl…
jlnav Mar 2, 2026
2ba133d
Merge branch 'develop' into asktell/only_persistent_is_default
jlnav Mar 6, 2026
7c33c4b
debugging this made me decide that deciding to support sim_fs with no…
jlnav Mar 6, 2026
6af5e90
Merge branch 'develop' into asktell/only_persistent_is_default
jlnav Mar 11, 2026
8f07e62
Merge branch 'develop' into asktell/only_persistent_is_default
jlnav Mar 11, 2026
4369cf2
Merge branch 'develop' into asktell/only_persistent_is_default
jlnav Mar 12, 2026
ebca858
fix unit test
jlnav Mar 12, 2026
aaf98ca
for 1d_sampling on mpi, need >= procs for 1 manager, 1 gen worker, 1 …
jlnav Mar 12, 2026
dece715
add gest-api as conda-package dependency besides pypi
jlnav Mar 13, 2026
8101c3b
migrate many alloc_f["user"] options to gen_specs. remove correspondi…
jlnav Mar 13, 2026
07e208b
these options still needed by var resources testing gens
jlnav Mar 18, 2026
ed03dc5
fix more resources tests
jlnav Mar 18, 2026
aba3f6e
fix more tests
jlnav Mar 18, 2026
0c972d3
this validator isn't needed after performing the switch
jlnav Mar 18, 2026
8851447
Fixed user in alloc_fs to be the combination of gen_specs and alloc_s…
jlnav Mar 19, 2026
8d7f077
fixes
jlnav Mar 19, 2026
f3a0e59
for classic reg test need the batch size info in gen_specs
jlnav Mar 20, 2026
eb2b004
fix used batch size key
jlnav Mar 20, 2026
c6bad6a
ditto
jlnav Mar 20, 2026
5f74f54
more test fixes
jlnav Mar 20, 2026
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 .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ repos:
rev: v1.19.1
hooks:
- id: mypy
exclude: ^libensemble/utils/(launcher|loc_stack|runners|pydantic|output_directory)\.py$|^libensemble/tests/regression_tests/support\.py$|^libensemble/tests/functionality_tests/
2 changes: 1 addition & 1 deletion docs/data_structures/alloc_specs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Can be constructed and passed to libEnsemble as a Python class or a dictionary.
* libEnsemble uses the following defaults if the user doesn't provide their own ``alloc_specs``:

.. literalinclude:: ../../libensemble/specs.py
:start-at: alloc_f: Callable = give_sim_work_first
:start-at: alloc_f: Callable = start_only_persistent
:end-before: end_alloc_tag
:caption: Default settings for alloc_specs

Expand Down
35 changes: 19 additions & 16 deletions docs/examples/alloc_funcs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,31 @@ Many users use these unmodified.
.. IMPORTANT::
See the API for allocation functions :ref:`here<api_alloc_f>`.

**The default allocation function changed in libEnsemble v2.0 from `give_sim_work_first` to `start_only_persistent `.**

.. note::
The default allocation function (for non-persistent generators) is :ref:`give_sim_work_first<gswf_label>`.

The most commonly used (for persistent generators) is :ref:`start_only_persistent<start_only_persistent_label>`.
The default allocation function for persistent generators is :ref:`start_only_persistent<start_only_persistent_label>`.

The most commonly used allocation function for non-persistent generators is :ref:`give_sim_work_first<gswf_label>`.

.. role:: underline
:class: underline

.. _start_only_persistent_label:

start_only_persistent
---------------------
.. automodule:: start_only_persistent
:members:
:undoc-members:

.. dropdown:: :underline:`start_only_persistent.py`

.. literalinclude:: ../../libensemble/alloc_funcs/start_only_persistent.py
:language: python
:linenos:

.. _gswf_label:

give_sim_work_first
Expand All @@ -44,20 +61,6 @@ fast_alloc
:language: python
:linenos:

.. _start_only_persistent_label:

start_only_persistent
---------------------
.. automodule:: start_only_persistent
:members:
:undoc-members:

.. dropdown:: :underline:`start_only_persistent.py`

.. literalinclude:: ../../libensemble/alloc_funcs/start_only_persistent.py
:language: python
:linenos:

start_persistent_local_opt_gens
-------------------------------
.. automodule:: start_persistent_local_opt_gens
Expand Down
4 changes: 2 additions & 2 deletions docs/function_guides/allocator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ The remaining values above are useful for efficient filtering of H values

Descriptions of included allocation functions can be found :doc:`here<../examples/alloc_funcs>`.
The default allocation function is
``give_sim_work_first``. During its worker ID loop, it checks if there's unallocated
``start_only_persistent``. During its worker ID loop, it checks if there's unallocated
work and assigns simulations for that work. Otherwise, it initializes
generators for up to ``"num_active_gens"`` instances. Other settings like
``batch_mode`` are also supported. See
:ref:`here<gswf_label>` for more information about ``give_sim_work_first``.
:ref:`here<start_only_persistent_label>` for more information.
8 changes: 3 additions & 5 deletions docs/tutorials/calib_cancel_tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,9 @@ The allocation function used in this example is the *only_persistent_gens* funct

alloc_specs = {
"alloc_f": alloc_f,
"user": {
"init_sample_size": init_sample_size,
"async_return": True,
"active_recv_gen": True,
},
"initial_batch_size": init_sample_size,
"async_return": True,
"active_recv_gen": True,
}

**async_return** tells the allocation function to return results to the generator as soon
Expand Down
5 changes: 3 additions & 2 deletions libensemble/alloc_funcs/fast_alloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, li
to evaluate in the simulation function. The fields in ``sim_specs["in"]``
are given. If all entries in `H` have been given a be evaluated, a worker
is told to call the generator function, provided this wouldn't result in
more than ``alloc_specs["user"]["num_active_gen"]`` active generators.
more than ``gen_specs["num_active_gens"]`` or ``alloc_specs["user"]["num_active_gens"]`` active generators.

This fast_alloc variation of give_sim_work_first is useful for cases that
simply iterate through H, issuing evaluations in order and, in particular,
Expand All @@ -23,7 +23,8 @@ def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, li
if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
return {}, persis_info

user = alloc_specs.get("user", {})
user = {**gen_specs, **alloc_specs.get("user", {})}

manage_resources = libE_info["use_resource_sets"]

support = AllocSupport(W, manage_resources, persis_info, libE_info)
Expand Down
11 changes: 7 additions & 4 deletions libensemble/alloc_funcs/fast_alloc_and_pausing.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@ def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, li
if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
return {}, persis_info

user = {**gen_specs, **alloc_specs.get("user", {})}
manage_resources = libE_info["use_resource_sets"]
support = AllocSupport(W, manage_resources, persis_info, libE_info)
Work = {}
gen_count = support.count_gens()

if gen_specs["user"].get("single_component_at_a_time"):
assert alloc_specs["user"]["batch_mode"], "Must be in batch mode when using 'single_component_at_a_time'"
assert alloc_specs["user"].get("batch_mode", False) or gen_specs.get(
"batch_mode", False
), "Must be in batch mode when using 'single_component_at_a_time'"
if len(H) != persis_info["H_len"]:
# Something new is in the history.
persis_info["need_to_give"].update(H["sim_id"][persis_info["H_len"] :].tolist())
Expand Down Expand Up @@ -119,13 +122,13 @@ def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, li
break

while len(idle_gen_workers):
if gen_count < alloc_specs["user"].get("num_active_gens", gen_count + 1):
if gen_count < user.get("num_active_gens", gen_count + 1):
lw = persis_info["last_worker"]

last_size = persis_info.get("last_size")
if len(H):
# Don't give gen instances in batch mode if points are unfinished
if alloc_specs["user"].get("batch_mode") and not all(
if (alloc_specs["user"].get("batch_mode") or gen_specs.get("batch_mode")) and not all(
np.logical_or(H["sim_ended"][last_size:], H["paused"][last_size:])
):
break
Expand All @@ -142,7 +145,7 @@ def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, li
persis_info["last_worker"] = i
persis_info["last_size"] = len(H)

elif gen_count >= alloc_specs["user"].get("num_active_gens", gen_count + 1):
elif gen_count >= user.get("num_active_gens", gen_count + 1):
idle_gen_workers = []

return Work, persis_info
10 changes: 5 additions & 5 deletions libensemble/alloc_funcs/give_sim_work_first.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ def give_sim_work_first(
alloc_specs: dict,
persis_info: dict,
libE_info: dict,
) -> tuple[dict]:
) -> tuple[dict, dict]:
"""
Decide what should be given to workers. This allocation function gives any
available simulation work first, and only when all simulations are
completed or running does it start (at most ``alloc_specs["user"]["num_active_gens"]``)
completed or running does it start (at most ``gen_specs["num_active_gens"]`` or ``alloc_specs["user"]["num_active_gens"]``)
generator instances.

Allows for a ``alloc_specs["user"]["batch_mode"]`` where no generation
Allows for a ``gen_specs["batch_mode"]`` or ``alloc_specs["user"]["batch_mode"]`` where no generation
work is given out unless all entries in ``H`` are returned.

Can give points in highest priority, if ``"priority"`` is a field in ``H``.
If ``alloc_specs["user"]["give_all_with_same_priority"]`` is set to True, then
If ``gen_specs["give_all_with_same_priority"]`` or ``alloc_specs["user"]["give_all_with_same_priority"]`` is set to True, then
all points with the same priority value are given as a batch to the sim.

Workers performing sims will be assigned resources given in H["resource_sets"]
Expand All @@ -40,7 +40,7 @@ def give_sim_work_first(
`test_uniform_sampling.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_uniform_sampling.py>`_ # noqa
"""

user = alloc_specs.get("user", {})
user = {**gen_specs, **alloc_specs.get("user", {})}

if "cancel_sims_time" in user:
# Cancel simulations that are taking too long
Expand Down
3 changes: 2 additions & 1 deletion libensemble/alloc_funcs/persistent_aposmm_alloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def persistent_aposmm_alloc(W, H, sim_specs, gen_specs, alloc_specs, persis_info
if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
return {}, persis_info

init_sample_size = gen_specs["user"]["initial_sample_size"]
user = {**gen_specs, **alloc_specs.get("user", {})}
init_sample_size = user["initial_batch_size"]
manage_resources = libE_info["use_resource_sets"]
support = AllocSupport(W, manage_resources, persis_info, libE_info)
gen_count = support.count_persis_gens()
Expand Down
11 changes: 6 additions & 5 deletions libensemble/alloc_funcs/start_only_persistent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
def only_persistent_gens(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
"""
This allocation function will give simulation work if possible, but
otherwise start up to ``alloc_specs["user"]["num_active_gens"]``
otherwise start up to ``gen_specs["num_active_gens"]`` or ``alloc_specs["user"]["num_active_gens"]``
persistent generators (defaulting to one).

By default, evaluation results are given back to the generator once
all generated points have been returned from the simulation evaluation.
If ``alloc_specs["user"]["async_return"]`` is set to True, then any
If ``gen_specs["async_return"]`` or ``alloc_specs["user"]["async_return"]`` is set to True, then any
returned points are given back to the generator.

If any workers are marked as zero_resource_workers, then these will only
Expand Down Expand Up @@ -56,19 +56,20 @@ def only_persistent_gens(W, H, sim_specs, gen_specs, alloc_specs, persis_info, l
return {}, persis_info

# Initialize alloc_specs["user"] as user.
user = alloc_specs.get("user", {})
user = {**gen_specs, **alloc_specs.get("user", {})}

manage_resources = libE_info["use_resource_sets"]

active_recv_gen = user.get("active_recv_gen", False) # Persistent gen can handle irregular communications
init_sample_size = user.get("init_sample_size", 0) # Always batch return until this many evals complete
initial_batch_size = user.get("initial_batch_size", 0) # Always batch return until this many evals complete
batch_give = user.get("give_all_with_same_priority", False)

support = AllocSupport(W, manage_resources, persis_info, libE_info)
gen_count = support.count_persis_gens()
Work = {}

# Asynchronous return to generator
async_return = user.get("async_return", False) and sum(H["sim_ended"]) >= init_sample_size
async_return = user.get("async_return", False) and sum(H["sim_ended"]) >= initial_batch_size

if gen_count < persis_info.get("num_gens_started", 0):
# When a persistent worker is done, trigger a shutdown (returning exit condition of 1)
Expand Down
24 changes: 16 additions & 8 deletions libensemble/gen_funcs/persistent_sampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
]


def _get_user_params(user_specs):
def _get_user_params(user_specs, gen_specs):
"""Extract user params"""
b = user_specs["initial_batch_size"]
b = gen_specs.get("initial_batch_size") or user_specs.get("initial_batch_size") or gen_specs.get("init_sample_size")
ub = user_specs["ub"]
lb = user_specs["lb"]
n = len(lb) # dimension
Expand All @@ -44,7 +44,7 @@ def persistent_uniform(_, persis_info, gen_specs, libE_info):
`test_persistent_uniform_sampling_async.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_persistent_uniform_sampling_async.py>`_
""" # noqa

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs.get("user", {}), gen_specs)
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)

# Send batches until manager sends stop tag
Expand Down Expand Up @@ -73,7 +73,7 @@ def persistent_uniform_final_update(_, persis_info, gen_specs, libE_info):
`test_persistent_uniform_sampling_running_mean.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_persistent_uniform_sampling_running_mean.py>`_
""" # noqa

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs.get("user", {}), gen_specs)
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)

def generate_corners(x, y):
Expand Down Expand Up @@ -145,7 +145,7 @@ def persistent_request_shutdown(_, persis_info, gen_specs, libE_info):
.. seealso::
`test_persistent_uniform_gen_decides_stop.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_persistent_uniform_gen_decides_stop.py>`_
""" # noqa
b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs.get("user", {}), gen_specs)
shutdown_limit = gen_specs["user"]["shutdown_limit"]
f_count = 0
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)
Expand Down Expand Up @@ -173,7 +173,7 @@ def uniform_nonblocking(_, persis_info, gen_specs, libE_info):
.. seealso::
`test_persistent_uniform_sampling.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_persistent_uniform_sampling.py>`_
""" # noqa
b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs.get("user", {}), gen_specs)
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)

# Send batches until manager sends stop tag
Expand Down Expand Up @@ -223,7 +223,11 @@ def batched_history_matching(_, persis_info, gen_specs, libE_info):
lb = gen_specs["user"]["lb"]

n = len(lb)
b = gen_specs["user"]["initial_batch_size"]
b = (
gen_specs.get("initial_batch_size")
or gen_specs["user"].get("initial_batch_size")
or gen_specs.get("init_sample_size")
)
q = gen_specs["user"]["num_best_vals"]
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)

Expand All @@ -250,7 +254,11 @@ def persistent_uniform_with_cancellations(_, persis_info, gen_specs, libE_info):
ub = gen_specs["user"]["ub"]
lb = gen_specs["user"]["lb"]
n = len(lb)
b = gen_specs["user"]["initial_batch_size"]
b = (
gen_specs.get("initial_batch_size")
or gen_specs["user"].get("initial_batch_size")
or gen_specs.get("init_sample_size")
)

# Start cancelling points from half initial batch onward
cancel_from = b // 2 # Should get at least this many points back
Expand Down
20 changes: 10 additions & 10 deletions libensemble/gen_funcs/persistent_sampling_var_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
]


def _get_user_params(user_specs):
def _get_user_params(gen_specs):
"""Extract user params"""
b = user_specs["initial_batch_size"]
ub = user_specs["ub"]
lb = user_specs["lb"]
b = gen_specs["initial_batch_size"]
ub = gen_specs["user"]["ub"]
lb = gen_specs["user"]["lb"]
n = len(lb) # dimension
return b, n, lb, ub

Expand All @@ -43,7 +43,7 @@ def uniform_sample(_, persis_info, gen_specs, libE_info):
`test_uniform_sampling_with_variable_resources.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_uniform_sampling_with_variable_resources.py>`_
""" # noqa

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs)
rng = persis_info["rand_stream"]
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)
tag = None
Expand Down Expand Up @@ -76,7 +76,7 @@ def uniform_sample_with_var_gpus(_, persis_info, gen_specs, libE_info):
`test_GPU_variable_resources.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_GPU_variable_resources.py>`_
""" # noqa

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs)
rng = persis_info["rand_stream"]
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)
tag = None
Expand Down Expand Up @@ -111,7 +111,7 @@ def uniform_sample_with_procs_gpus(_, persis_info, gen_specs, libE_info):
`test_GPU_variable_resources.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_GPU_variable_resources.py>`_
""" # noqa

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs)
rng = persis_info["rand_stream"]
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)
tag = None
Expand All @@ -137,7 +137,7 @@ def uniform_sample_with_var_priorities(_, persis_info, gen_specs, libE_info):
resource sets and priorities are requested for each point.
"""

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs)
rng = persis_info["rand_stream"]
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)

Expand Down Expand Up @@ -175,7 +175,7 @@ def uniform_sample_diff_simulations(_, persis_info, gen_specs, libE_info):
`test_GPU_variable_resources_multi_task.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_GPU_variable_resources_multi_task.py>`_
""" # noqa

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs)
rng = persis_info["rand_stream"]
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)
tag = None
Expand Down Expand Up @@ -209,7 +209,7 @@ def uniform_sample_with_sim_gen_resources(_, persis_info, gen_specs, libE_info):
`test_GPU_variable_resources.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_GPU_variable_resources.py>`_
""" # noqa

b, n, lb, ub = _get_user_params(gen_specs["user"])
b, n, lb, ub = _get_user_params(gen_specs)
rng = persis_info["rand_stream"]
ps = PersistentSupport(libE_info, EVAL_GEN_TAG)
tag = None
Expand Down
Loading