Allocation Functions

Below are example allocation functions available in libEnsemble.

Many users use these unmodified.

Important

See the API for allocation functions here.

Note

The default allocation function (for non-persistent generators) is give_sim_work_first.

The most commonly used (for persistent generators) is start_only_persistent.

give_sim_work_first

give_sim_work_first.give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

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"]) generator instances.

Allows for a 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 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”] this field exists, else defaulting to one. Workers performing gens are assigned resource_sets given by persis_info[“gen_resources”] or zero.

This is the default allocation function if one is not defined.

tags: alloc, default, batch, priority

Parameters:
  • W (ndarray[tuple[int, ...], dtype[_ScalarType_co]])

  • H (ndarray[tuple[int, ...], dtype[_ScalarType_co]])

  • sim_specs (dict)

  • gen_specs (dict)

  • alloc_specs (dict)

  • persis_info (dict)

  • libE_info (dict)

Return type:

Tuple[dict]

give_sim_work_first.py
 1import time
 2from typing import Tuple
 3
 4import numpy as np
 5import numpy.typing as npt
 6
 7from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources
 8
 9
10def give_sim_work_first(
11    W: npt.NDArray,
12    H: npt.NDArray,
13    sim_specs: dict,
14    gen_specs: dict,
15    alloc_specs: dict,
16    persis_info: dict,
17    libE_info: dict,
18) -> Tuple[dict]:
19    """
20    Decide what should be given to workers. This allocation function gives any
21    available simulation work first, and only when all simulations are
22    completed or running does it start (at most ``alloc_specs["user"]["num_active_gens"]``)
23    generator instances.
24
25    Allows for a ``alloc_specs["user"]["batch_mode"]`` where no generation
26    work is given out unless all entries in ``H`` are returned.
27
28    Can give points in highest priority, if ``"priority"`` is a field in ``H``.
29    If ``alloc_specs["user"]["give_all_with_same_priority"]`` is set to True, then
30    all points with the same priority value are given as a batch to the sim.
31
32    Workers performing sims will be assigned resources given in H["resource_sets"]
33    this field exists, else defaulting to one. Workers performing gens are
34    assigned resource_sets given by persis_info["gen_resources"] or zero.
35
36    This is the default allocation function if one is not defined.
37
38    tags: alloc, default, batch, priority
39
40    .. seealso::
41        `test_uniform_sampling.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_uniform_sampling.py>`_ # noqa
42    """
43
44    user = alloc_specs.get("user", {})
45
46    if "cancel_sims_time" in user:
47        # Cancel simulations that are taking too long
48        rows = np.where(np.logical_and.reduce((H["sim_started"], ~H["sim_ended"], ~H["cancel_requested"])))[0]
49        inds = time.time() - H["sim_started_time"][rows] > user["cancel_sims_time"]
50        to_request_cancel = rows[inds]
51        for row in to_request_cancel:
52            H[row]["cancel_requested"] = True
53
54    if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
55        return {}, persis_info
56
57    # Initialize alloc_specs["user"] as user.
58    batch_give = user.get("give_all_with_same_priority", False)
59    gen_in = gen_specs.get("in", [])
60
61    manage_resources = libE_info["use_resource_sets"]
62    support = AllocSupport(W, manage_resources, persis_info, libE_info)
63    gen_count = support.count_gens()
64    Work = {}
65
66    points_to_evaluate = ~H["sim_started"] & ~H["cancel_requested"]
67
68    if np.any(points_to_evaluate):
69        for wid in support.avail_worker_ids(gen_workers=False):
70            sim_ids_to_send = support.points_by_priority(H, points_avail=points_to_evaluate, batch=batch_give)
71            try:
72                Work[wid] = support.sim_work(wid, H, sim_specs["in"], sim_ids_to_send, persis_info.get(wid))
73            except InsufficientFreeResources:
74                break
75            points_to_evaluate[sim_ids_to_send] = False
76            if not np.any(points_to_evaluate):
77                break
78    else:
79        for wid in support.avail_worker_ids(gen_workers=True):
80            # Allow at most num_active_gens active generator instances
81            if gen_count >= user.get("num_active_gens", gen_count + 1):
82                break
83
84            # Do not start gen instances in batch mode if workers still working
85            if user.get("batch_mode") and not support.all_sim_ended(H):
86                break
87
88            # Give gen work
89            return_rows = range(len(H)) if gen_in else []
90            try:
91                Work[wid] = support.gen_work(wid, gen_in, return_rows, persis_info.get(wid))
92            except InsufficientFreeResources:
93                break
94            gen_count += 1
95
96    return Work, persis_info

fast_alloc

fast_alloc.give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function gives (in order) entries in H to idle workers 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.

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, is likely to be faster if there will be many short simulation evaluations, given that this function contains fewer column length operations.

tags: alloc, simple, fast

fast_alloc.py
 1from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources
 2
 3
 4def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
 5    """
 6    This allocation function gives (in order) entries in ``H`` to idle workers
 7    to evaluate in the simulation function. The fields in ``sim_specs["in"]``
 8    are given. If all entries in `H` have been given a be evaluated, a worker
 9    is told to call the generator function, provided this wouldn't result in
10    more than ``alloc_specs["user"]["num_active_gen"]`` active generators.
11
12    This fast_alloc variation of give_sim_work_first is useful for cases that
13    simply iterate through H, issuing evaluations in order and, in particular,
14    is likely to be faster if there will be many short simulation evaluations,
15    given that this function contains fewer column length operations.
16
17    tags: alloc, simple, fast
18
19    .. seealso::
20        `test_fast_alloc.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_fast_alloc.py>`_ # noqa
21    """
22
23    if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
24        return {}, persis_info
25
26    user = alloc_specs.get("user", {})
27    manage_resources = libE_info["use_resource_sets"]
28
29    support = AllocSupport(W, manage_resources, persis_info, libE_info)
30
31    gen_count = support.count_gens()
32    Work = {}
33    gen_in = gen_specs.get("in", [])
34
35    # Give sim work if possible
36    for wid in support.avail_worker_ids(gen_workers=False):
37        persis_info = support.skip_canceled_points(H, persis_info)
38        if persis_info["next_to_give"] < len(H):
39            try:
40                Work[wid] = support.sim_work(wid, H, sim_specs["in"], [persis_info["next_to_give"]], [])
41            except InsufficientFreeResources:
42                break
43            persis_info["next_to_give"] += 1
44
45    # Give gen work if possible
46    if persis_info["next_to_give"] >= len(H):
47        for wid in support.avail_worker_ids(gen_workers=True):
48            if wid not in Work and gen_count < user.get("num_active_gens", gen_count + 1):
49                return_rows = range(len(H)) if gen_in else []
50                try:
51                    Work[wid] = support.gen_work(wid, gen_in, return_rows, persis_info.get(wid))
52                except InsufficientFreeResources:
53                    break
54                gen_count += 1
55                persis_info["total_gen_calls"] += 1
56
57    return Work, persis_info

start_only_persistent

start_only_persistent.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"] 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 returned points are given back to the generator.

If any workers are marked as zero_resource_workers, then these will only be used for generators.

If any of the persistent generators has exited, then ensemble shutdown is triggered.

User options:

To be provided in calling script: E.g., alloc_specs["user"]["async_return"] = True

init_sample_size: int, optional

Initial sample size - always return in batch. Default: 0

num_active_gens: int, optional

Maximum number of persistent generators to start. Default: 1

async_return: Boolean, optional

Return results to gen as they come in (after sample). Default: False (batch return).

give_all_with_same_priority: Boolean, optional

If True, then all points with the same priority value are given as a batch to the sim. Default is False

active_recv_gen: Boolean, optional

Create gen in active receive mode. If True, the manager does not need to wait for a return from the generator before sending further returned points. Default: False

tags: alloc, batch, async, persistent, priority

start_only_persistent.py
  1import numpy as np
  2
  3from libensemble.message_numbers import EVAL_GEN_TAG, EVAL_SIM_TAG
  4from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources
  5
  6
  7def only_persistent_gens(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
  8    """
  9    This allocation function will give simulation work if possible, but
 10    otherwise start up to ``alloc_specs["user"]["num_active_gens"]``
 11    persistent generators (defaulting to one).
 12
 13    By default, evaluation results are given back to the generator once
 14    all generated points have been returned from the simulation evaluation.
 15    If ``alloc_specs["user"]["async_return"]`` is set to True, then any
 16    returned points are given back to the generator.
 17
 18    If any workers are marked as zero_resource_workers, then these will only
 19    be used for generators.
 20
 21    If any of the persistent generators has exited, then ensemble shutdown
 22    is triggered.
 23
 24    **User options**:
 25
 26    To be provided in calling script: E.g., ``alloc_specs["user"]["async_return"] = True``
 27
 28    init_sample_size: int, optional
 29        Initial sample size - always return in batch. Default: 0
 30
 31    num_active_gens: int, optional
 32        Maximum number of persistent generators to start. Default: 1
 33
 34    async_return: Boolean, optional
 35        Return results to gen as they come in (after sample). Default: False (batch return).
 36
 37    give_all_with_same_priority: Boolean, optional
 38        If True, then all points with the same priority value are given as a batch to the sim.
 39        Default is False
 40
 41    active_recv_gen: Boolean, optional
 42        Create gen in active receive mode. If True, the manager does not need to wait
 43        for a return from the generator before sending further returned points.
 44        Default: False
 45
 46    tags: alloc, batch, async, persistent, priority
 47
 48    .. seealso::
 49        `test_persistent_uniform_sampling.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_persistent_uniform_sampling.py>`_ # noqa
 50        `test_persistent_uniform_sampling_async.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_persistent_uniform_sampling_async.py>`_ # noqa
 51        `test_persistent_surmise_calib.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_persistent_surmise_calib.py>`_ # noqa
 52        `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
 53    """
 54
 55    if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
 56        return {}, persis_info
 57
 58    # Initialize alloc_specs["user"] as user.
 59    user = alloc_specs.get("user", {})
 60    manage_resources = libE_info["use_resource_sets"]
 61
 62    active_recv_gen = user.get("active_recv_gen", False)  # Persistent gen can handle irregular communications
 63    init_sample_size = user.get("init_sample_size", 0)  # Always batch return until this many evals complete
 64    batch_give = user.get("give_all_with_same_priority", False)
 65
 66    support = AllocSupport(W, manage_resources, persis_info, libE_info)
 67    gen_count = support.count_persis_gens()
 68    Work = {}
 69
 70    # Asynchronous return to generator
 71    async_return = user.get("async_return", False) and sum(H["sim_ended"]) >= init_sample_size
 72
 73    if gen_count < persis_info.get("num_gens_started", 0):
 74        # When a persistent worker is done, trigger a shutdown (returning exit condition of 1)
 75        return Work, persis_info, 1
 76
 77    # Give evaluated results back to a running persistent gen
 78    for wid in support.avail_worker_ids(persistent=EVAL_GEN_TAG, active_recv=active_recv_gen):
 79        gen_inds = H["gen_worker"] == wid
 80        returned_but_not_given = np.logical_and.reduce((H["sim_ended"], ~H["gen_informed"], gen_inds))
 81        if np.any(returned_but_not_given):
 82            if async_return or support.all_sim_ended(H, gen_inds):
 83                point_ids = np.where(returned_but_not_given)[0]
 84                Work[wid] = support.gen_work(
 85                    wid,
 86                    gen_specs["persis_in"],
 87                    point_ids,
 88                    persis_info.get(wid),
 89                    persistent=True,
 90                    active_recv=active_recv_gen,
 91                )
 92                returned_but_not_given[point_ids] = False
 93
 94    # Now the give_sim_work_first part
 95    points_to_evaluate = ~H["sim_started"] & ~H["cancel_requested"]
 96    avail_workers = support.avail_worker_ids(persistent=False, zero_resource_workers=False, gen_workers=False)
 97    if user.get("alt_type"):
 98        avail_workers = list(
 99            set(support.avail_worker_ids(persistent=False, zero_resource_workers=False))
100            | set(support.avail_worker_ids(persistent=EVAL_SIM_TAG, zero_resource_workers=False))
101        )
102    for wid in avail_workers:
103        if not np.any(points_to_evaluate):
104            break
105
106        sim_ids_to_send = support.points_by_priority(H, points_avail=points_to_evaluate, batch=batch_give)
107
108        try:
109            if user.get("alt_type"):
110                Work[wid] = support.sim_work(
111                    wid, H, sim_specs["in"], sim_ids_to_send, persis_info.get(wid), persistent=True
112                )
113            else:
114                Work[wid] = support.sim_work(wid, H, sim_specs["in"], sim_ids_to_send, persis_info.get(wid))
115        except InsufficientFreeResources:
116            break
117
118        points_to_evaluate[sim_ids_to_send] = False
119
120    # Start persistent gens if no worker to give out. Uses zero_resource_workers if defined.
121    if not np.any(points_to_evaluate):
122        avail_workers = support.avail_worker_ids(persistent=False, zero_resource_workers=True, gen_workers=True)
123
124        for wid in avail_workers:
125            if gen_count < user.get("num_active_gens", 1):
126                # Finally, start a persistent generator as there is nothing else to do.
127                try:
128                    Work[wid] = support.gen_work(
129                        wid,
130                        gen_specs.get("in", []),
131                        range(len(H)),
132                        persis_info.get(wid),
133                        persistent=True,
134                        active_recv=active_recv_gen,
135                    )
136                except InsufficientFreeResources:
137                    break
138
139                persis_info["num_gens_started"] = persis_info.get("num_gens_started", 0) + 1
140                gen_count += 1
141
142    return Work, persis_info, 0

start_persistent_local_opt_gens

start_persistent_local_opt_gens.start_persistent_local_opt_gens(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function will do the following:

  • Start up a persistent generator that is a local opt run at the first point identified by APOSMM’s decide_where_to_start_localopt. Note, it will do this only if at least one worker will be left to perform simulation evaluations.

  • If multiple starting points are available, the one with smallest function value is chosen.

  • If no candidate starting points exist, points from existing runs will be evaluated (oldest first).

  • If no points are left, call the generation function.

tags: alloc, persistent, aposmm

fast_alloc_and_pausing

fast_alloc_and_pausing.give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function gives (in order) entries in H to idle workers 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. Also allows for a “batch_mode”.

When there are multiple objective components, this allocation function does not evaluate further components for some point in the following scenarios:

alloc_specs[“user”][“stop_on_NaNs”]: True — after a NaN has been found in returned in some

objective component

alloc_specs[“user”][“stop_partial_fvec_eval”]: True — after the value returned from

combine_component_func is larger than a known upper bound on the objective.

only_one_gen_alloc

only_one_gen_alloc.ensure_one_active_gen(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function gives (in order) entries in H to idle workers to evaluate in the simulation function. The fields in sim_specs["in"] are given. If there is no active generator, then one is started.

start_fd_persistent

start_fd_persistent.finite_diff_alloc(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function will give simulation work if possible, but otherwise start 1 persistent generator. If all points requested by the persistent generator for a given (x_ind, f_ind) pair have been returned from the simulation evaluation, then this information is given back to the persistent generator (where x_ind is in range(n) and f_ind is in range(p))

persistent_aposmm_alloc

persistent_aposmm_alloc.persistent_aposmm_alloc(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function will give simulation work if possible, but otherwise start a persistent APOSMM generator. If all points requested by the persistent generator have been returned from the simulation evaluation, then this information is given back to the persistent generator.

This function assumes that one persistent APOSMM will be started and never stopped (until some exit_criterion is satisfied).

give_pregenerated_work

give_pregenerated_work.give_pregenerated_sim_work(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function gives (in order) entries in alloc_spec[“x”] to idle workers. It is an example use case where no gen_func is used.

inverse_bayes_allocf

inverse_bayes_allocf.only_persistent_gens_for_inverse_bayes(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

Starts up to gen_count number of persistent generators. These persistent generators produce points (x) in batches and subbatches. The points x are given in subbatches to workers to perform a calculation. When all subbatches have returned, their output is given back to the corresponding persistent generator.

The first time called there are no persis_w 1st for loop is not done