Allocation Functions

Below are example allocation functions available in libEnsemble.

Important

See the API for allocation functions here.

Note

The default alloc_func is give_sim_work_first.

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

See also

test_uniform_sampling.py # noqa

give_sim_work_first.py

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

See also

test_fast_alloc.py # noqa

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/regression_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 = "resource_sets" in H.dtype.names or 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    for wid in support.avail_worker_ids():
36        # Skip any cancelled points
37        while persis_info["next_to_give"] < len(H) and H[persis_info["next_to_give"]]["cancel_requested"]:
38            persis_info["next_to_give"] += 1
39
40        # Give sim work if possible
41        if persis_info["next_to_give"] < len(H):
42            try:
43                Work[wid] = support.sim_work(wid, H, sim_specs["in"], [persis_info["next_to_give"]], [])
44            except InsufficientFreeResources:
45                break
46            persis_info["next_to_give"] += 1
47
48        elif gen_count < user.get("num_active_gens", gen_count + 1):
49
50            # Give gen work
51            return_rows = range(len(H)) if gen_in else []
52            try:
53                Work[wid] = support.gen_work(wid, gen_in, return_rows, persis_info.get(wid))
54            except InsufficientFreeResources:
55                break
56            gen_count += 1
57            persis_info["total_gen_calls"] += 1
58
59    return Work, persis_info

fast_alloc_to_aposmm

fast_alloc_to_aposmm.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 to 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'.

tags: alloc, simple, fast, batch, aposmm

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).

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.only_persistent_workers(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info)

This allocation function will give simulation work if possible to any worker not listed as a zero_resource_worker. On the first call, the worker will be placed into a persistent state that will be maintained until libE is exited.

Otherwise, zero resource workers will be given up to a maximum of 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 of the persistent generators has exited, then ensemble shutdown is triggered.

Note, that an alternative to using zero resource workers would be to set a fixed number of simulation workers in persistent state at the start, allowing at least one worker for the generator - a minor alteration.

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).

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

start_only_persistent.py

  1import numpy as np
  2from libensemble.message_numbers import EVAL_SIM_TAG, EVAL_GEN_TAG
  3from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources
  4
  5
  6def only_persistent_gens(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
  7    """
  8    This allocation function will give simulation work if possible, but
  9    otherwise start up to ``alloc_specs['user']['num_active_gens']``
 10    persistent generators (defaulting to one).
 11
 12    By default, evaluation results are given back to the generator once
 13    all generated points have been returned from the simulation evaluation.
 14    If ``alloc_specs['user']['async_return']`` is set to True, then any
 15    returned points are given back to the generator.
 16
 17    If any workers are marked as zero_resource_workers, then these will only
 18    be used for generators.
 19
 20    If any of the persistent generators has exited, then ensemble shutdown
 21    is triggered.
 22
 23    **User options**:
 24
 25    To be provided in calling script: E.g., ``alloc_specs['user']['async_return'] = True``
 26
 27    init_sample_size: int, optional
 28        Initial sample size - always return in batch. Default: 0
 29
 30    num_active_gens: int, optional
 31        Maximum number of persistent generators to start. Default: 1
 32
 33    async_return: boolean, optional
 34        Return results to gen as they come in (after sample). Default: False (batch return).
 35
 36    active_recv_gen: boolean, optional
 37        Create gen in active receive mode. If True, the manager does not need to wait
 38        for a return from the generator before sending further returned points.
 39        Default: False
 40
 41    tags: alloc, batch, async, persistent, priority
 42
 43    .. seealso::
 44        `test_persistent_sampling.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_persistent_sampling.py>`_ # noqa
 45        `test_persistent_sampling_async.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_persistent_sampling_async.py>`_ # noqa
 46        `test_persistent_surmise_calib.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_persistent_surmise_calib.py>`_ # noqa
 47        `test_persistent_uniform_gen_decides_stop.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_persistent_uniform_gen_decides_stop.py>`_ # noqa
 48    """
 49
 50    if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
 51        return {}, persis_info
 52
 53    # Initialize alloc_specs['user'] as user.
 54    user = alloc_specs.get("user", {})
 55    manage_resources = "resource_sets" in H.dtype.names or libE_info["use_resource_sets"]
 56    active_recv_gen = user.get("active_recv_gen", False)  # Persistent gen can handle irregular communications
 57    init_sample_size = user.get("init_sample_size", 0)  # Always batch return until this many evals complete
 58    batch_give = user.get("give_all_with_same_priority", False)
 59
 60    support = AllocSupport(W, manage_resources, persis_info, libE_info)
 61    gen_count = support.count_persis_gens()
 62    Work = {}
 63
 64    # Asynchronous return to generator
 65    async_return = user.get("async_return", False) and sum(H["sim_ended"]) >= init_sample_size
 66
 67    if gen_count < persis_info.get("num_gens_started", 0):
 68        # When a persistent worker is done, trigger a shutdown (returning exit condition of 1)
 69        return Work, persis_info, 1
 70
 71    # Give evaluated results back to a running persistent gen
 72    for wid in support.avail_worker_ids(persistent=EVAL_GEN_TAG, active_recv=active_recv_gen):
 73        gen_inds = H["gen_worker"] == wid
 74        returned_but_not_given = np.logical_and.reduce((H["sim_ended"], ~H["gen_informed"], gen_inds))
 75        if np.any(returned_but_not_given):
 76            if async_return or support.all_sim_ended(H, gen_inds):
 77                point_ids = np.where(returned_but_not_given)[0]
 78                Work[wid] = support.gen_work(
 79                    wid,
 80                    gen_specs["persis_in"],
 81                    point_ids,
 82                    persis_info.get(wid),
 83                    persistent=True,
 84                    active_recv=active_recv_gen,
 85                )
 86                returned_but_not_given[point_ids] = False
 87
 88    # Now the give_sim_work_first part
 89    points_to_evaluate = ~H["sim_started"] & ~H["cancel_requested"]
 90    avail_workers = support.avail_worker_ids(persistent=False, zero_resource_workers=False)
 91    for wid in avail_workers:
 92
 93        if not np.any(points_to_evaluate):
 94            break
 95
 96        sim_ids_to_send = support.points_by_priority(H, points_avail=points_to_evaluate, batch=batch_give)
 97        try:
 98            Work[wid] = support.sim_work(wid, H, sim_specs["in"], sim_ids_to_send, persis_info.get(wid))
 99        except InsufficientFreeResources:
100            break
101
102        points_to_evaluate[sim_ids_to_send] = False
103
104    # Start persistent gens if no worker to give out. Uses zero_resource_workers if defined.
105    if not np.any(points_to_evaluate):
106        avail_workers = support.avail_worker_ids(persistent=False, zero_resource_workers=True)
107
108        for wid in avail_workers:
109            if gen_count < user.get("num_active_gens", 1):
110                # Finally, start a persistent generator as there is nothing else to do.
111                try:
112                    Work[wid] = support.gen_work(
113                        wid,
114                        gen_specs.get("in", []),
115                        range(len(H)),
116                        persis_info.get(wid),
117                        persistent=True,
118                        active_recv=active_recv_gen,
119                    )
120                except InsufficientFreeResources:
121                    break
122                persis_info["num_gens_started"] = persis_info.get("num_gens_started", 0) + 1
123                gen_count += 1
124
125    return Work, persis_info, 0
126
127
128def only_persistent_workers(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
129    """
130    This allocation function will give simulation work if possible to any worker
131    not listed as a zero_resource_worker. On the first call, the worker will be
132    placed into a persistent state that will be maintained until libE is exited.
133
134    Otherwise, zero resource workers will be given up to a maximum of
135    ``alloc_specs['user']['num_active_gens']`` persistent generators (defaulting to one).
136
137    By default, evaluation results are given back to the generator once
138    all generated points have been returned from the simulation evaluation.
139    If ``alloc_specs['user']['async_return']`` is set to True, then any
140    returned points are given back to the generator.
141
142    If any of the persistent generators has exited, then ensemble shutdown
143    is triggered.
144
145    Note, that an alternative to using zero resource workers would be to set
146    a fixed number of simulation workers in persistent state at the start, allowing
147    at least one worker for the generator - a minor alteration.
148
149    **User options**:
150
151    To be provided in calling script: E.g., ``alloc_specs['user']['async_return'] = True``
152
153    init_sample_size: int, optional
154        Initial sample size - always return in batch. Default: 0
155
156    num_active_gens: int, optional
157        Maximum number of persistent generators to start. Default: 1
158
159    async_return: boolean, optional
160        Return results to gen as they come in (after sample). Default: False (batch return).
161
162    active_recv_gen: boolean, optional
163        Create gen in active receive mode. If True, the manager does not need to wait
164        for a return from the generator before sending further returned points.
165        Default: False
166
167
168    .. seealso::
169        `test_persistent_gensim_uniform_sampling.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/regression_tests/test_persistent_gensim_uniform_sampling.py>`_ # noqa
170    """
171
172    if libE_info["sim_max_given"] or not libE_info["any_idle_workers"]:
173        return {}, persis_info
174
175    # Initialize alloc_specs['user'] as user.
176    user = alloc_specs.get("user", {})
177    manage_resources = "resource_sets" in H.dtype.names or libE_info["use_resource_sets"]
178    active_recv_gen = user.get("active_recv_gen", False)  # Persistent gen can handle irregular communications
179    init_sample_size = user.get("init_sample_size", 0)  # Always batch return until this many evals complete
180    batch_give = user.get("give_all_with_same_priority", False)
181
182    support = AllocSupport(W, manage_resources, persis_info, libE_info)
183    gen_count = support.count_persis_gens()
184    Work = {}
185
186    # Asynchronous return to generator
187    async_return = user.get("async_return", False) and sum(H["sim_ended"]) >= init_sample_size
188
189    if gen_count < persis_info.get("num_gens_started", 0):
190        # When a persistent gen worker is done, trigger a shutdown (returning exit condition of 1)
191        return Work, persis_info, 1
192
193    # Give evaluated results back to a running persistent gen
194    for wid in support.avail_worker_ids(persistent=EVAL_GEN_TAG, active_recv=active_recv_gen):
195        gen_inds = H["gen_worker"] == wid
196        returned_but_not_given = np.logical_and.reduce((H["sim_ended"], ~H["gen_informed"], gen_inds))
197        if np.any(returned_but_not_given):
198            if async_return or support.all_sim_ended(H, gen_inds):
199                point_ids = np.where(returned_but_not_given)[0]
200                Work[wid] = support.gen_work(
201                    wid,
202                    gen_specs["persis_in"],
203                    point_ids,
204                    persis_info.get(wid),
205                    persistent=True,
206                    active_recv=active_recv_gen,
207                )
208                returned_but_not_given[point_ids] = False
209
210    # Now the give_sim_work_first part
211    points_to_evaluate = ~H["sim_started"] & ~H["cancel_requested"]
212    avail_workers = list(
213        set(support.avail_worker_ids(persistent=False, zero_resource_workers=False))
214        | set(support.avail_worker_ids(persistent=EVAL_SIM_TAG, zero_resource_workers=False))
215    )
216    for wid in avail_workers:
217
218        if not np.any(points_to_evaluate):
219            break
220
221        sim_ids_to_send = support.points_by_priority(H, points_avail=points_to_evaluate, batch=batch_give)
222        try:
223            # Note that resources will not change if worker is already persistent.
224            Work[wid] = support.sim_work(
225                wid, H, sim_specs["in"], sim_ids_to_send, persis_info.get(wid), persistent=True
226            )
227        except InsufficientFreeResources:
228            break
229
230        points_to_evaluate[sim_ids_to_send] = False
231
232    # Start persistent gens if no sim work to give out. Uses zero_resource_workers if defined.
233    if not np.any(points_to_evaluate):
234        avail_workers = support.avail_worker_ids(persistent=False, zero_resource_workers=True)
235
236        for wid in avail_workers:
237            if gen_count < user.get("num_active_gens", 1):
238                # Finally, start a persistent generator as there is nothing else to do.
239                try:
240                    Work[wid] = support.gen_work(
241                        wid,
242                        gen_specs.get("in", []),
243                        range(len(H)),
244                        persis_info.get(wid),
245                        persistent=True,
246                        active_recv=active_recv_gen,
247                    )
248                except InsufficientFreeResources:
249                    break
250                persis_info["num_gens_started"] = persis_info.get("num_gens_started", 0) + 1
251                gen_count += 1
252    del support
253    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