Skip to content
Draft
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
72 changes: 72 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,75 @@ When modernizing existing libEnsemble scripts (functionality tests, regression t
- **Mandatory Fields**: Ensure `gen_specs["in"]` or `gen_specs["persis_in"]` includes at least one field (e.g., `["sim_id"]`) if feedback is sent back to the generator, to satisfy the allocator's requirements.
- **gest-api Simulators**: The gest-api pattern also applies to simulators. Set `SimSpecs.simulator` to a callable with signature `(input_dict: dict, **kwargs) -> dict` instead of providing a `sim_f`. libEnsemble automatically wraps it with `gest_api_sim` from `libensemble.sim_funcs.gest_api_wrapper` and handles all NumPy conversions. `SimSpecs.inputs` and `SimSpecs.outputs` can be derived automatically when `SimSpecs.vocs` is provided.
- **`safe_mode` is opt-in**: `libE_specs["safe_mode"]` defaults to `False`, meaning protected History fields (`gen_worker`, `gen_started_time`, `gen_ended_time`, `sim_worker`, `sim_started`, `sim_started_time`, `sim_ended`, `sim_ended_time`, `gen_informed`, `gen_informed_time`, `kill_sent`) are freely overwritable by default. Set `safe_mode=True` to enable protection. Overwriting these fields without understanding their purpose may crash libEnsemble.
- **Pre-generated samples**: Scripts that previously used the ``give_pregenerated_work`` allocator (with no generator) should be migrated to use ``PreloadedSampleGenerator`` from ``libensemble.gen_classes.preloaded``. Pass it as ``GenSpecs(generator=PreloadedSampleGenerator(H0))`` and use the default ``AllocSpecs()``. The generator serves the pre-loaded points via ``suggest()`` and returns an empty list when exhausted, triggering normal ensemble shutdown.

Deprecation Policy
------------------

This section describes the standard process for deprecating **any** libEnsemble feature
(allocation functions, generator classes, public API, parameters, etc.).

**Warning category**

Always use ``LibEnsembleDeprecationWarning`` — a custom subclass of ``DeprecationWarning``
importable from ``libensemble._deprecation``. Never emit bare ``DeprecationWarning``
directly. The custom subclass lets users and downstream libraries filter libEnsemble
deprecations independently::

from libensemble._deprecation import LibEnsembleDeprecationWarning
warnings.filterwarnings("error", category=LibEnsembleDeprecationWarning)

**Emit the warning**

Emit the warning at the earliest point of use (module import, class instantiation, or
function call — whichever the user is most likely to see). Use the ``warn_deprecated()``
helper from the same module when the standard message format is sufficient::

import warnings
from libensemble._deprecation import LibEnsembleDeprecationWarning

warnings.warn(
"libensemble.<module>.<name> is deprecated as of libEnsemble X.Y "
"and will be removed in X.Z. Use <replacement> instead. "
"See https://libensemble.readthedocs.io/... for migration guidance.",
LibEnsembleDeprecationWarning,
stacklevel=2, # points to the caller's import/call site
)

**Docstring banner**

Add a ``.. deprecated:: X.Y`` directive at the top of the deprecated object's docstring,
naming the replacement and the removal version::

def my_old_function(...):
"""
.. deprecated:: 2.0
``my_old_function`` is deprecated and will be removed in libEnsemble 2.1.
Use :func:`libensemble.module.my_new_function` instead.
...
"""

**Sphinx docs**

In the relevant ``.rst`` file, move the deprecated item to a "Deprecated" section (or
subsection) at the bottom of the page and prefix its ``automodule``/``autofunction`` block
with a ``.. deprecated:: X.Y`` admonition and a ``.. warning::`` summarising all items
in the section together with migration guidance. See
``docs/function_guides/allocator.rst`` for a reference example.

**Pytest noise suppression**

Add a ``filterwarnings`` rule to ``[tool.pytest.ini_options]`` in ``pyproject.toml`` so
that tests of deprecated-but-not-yet-removed code do not produce noisy output::

[tool.pytest.ini_options]
filterwarnings = [
"ignore::libensemble._deprecation.LibEnsembleDeprecationWarning",
]

Remove this rule when the deprecated code is deleted.

**Timeline**

The standard window is: soft-deprecate in release N, hard-remove (delete code + tests) in N+1.
Tests that exclusively cover deprecated features are deleted in the removal release, not before.
93 changes: 69 additions & 24 deletions docs/function_guides/allocator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ We encourage experimenting with:

.. dropdown:: Example

.. literalinclude:: ../../libensemble/alloc_funcs/fast_alloc.py
:caption: libensemble.alloc_funcs.fast_alloc.give_sim_work_first
.. literalinclude:: ../../libensemble/alloc_funcs/give_sim_work_first.py
:caption: libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first

The ``alloc_f`` function definition resembles::

Expand Down Expand Up @@ -184,56 +184,101 @@ give_sim_work_first
:language: python
:linenos:

fast_alloc
----------
.. automodule:: fast_alloc
:members:
:undoc-members:
persistent_aposmm_alloc
-----------------------
.. automodule:: persistent_aposmm_alloc
:members:
:undoc-members:

give_pregenerated_work
----------------------
.. automodule:: give_pregenerated_work
:members:
:undoc-members:

.. dropdown:: :underline:`fast_alloc.py`
.. _deprecated-alloc-label:

.. literalinclude:: ../../libensemble/alloc_funcs/fast_alloc.py
:language: python
:linenos:
Deprecated Allocation Functions
================================

start_persistent_local_opt_gens
-------------------------------
.. automodule:: start_persistent_local_opt_gens
.. warning::

The following allocation functions are **deprecated as of libEnsemble 2.0** and will be
**removed in libEnsemble 2.1**. They emit a :class:`~libensemble._deprecation.LibEnsembleDeprecationWarning`
on import.

**Migration guidance:**

- Functions that managed non-persistent generators (``fast_alloc``, ``fast_alloc_and_pausing``,
``only_one_gen_alloc``) should be replaced with
:func:`~libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first` or the default
:func:`~libensemble.alloc_funcs.start_only_persistent.only_persistent_gens` with a
persistent generator.
- APOSMM-adjacent functions (``start_persistent_local_opt_gens``, ``start_fd_persistent``)
should migrate to
:func:`~libensemble.alloc_funcs.persistent_aposmm_alloc.persistent_aposmm_alloc`.
- ``inverse_bayes_allocf`` should be replaced with the default ``only_persistent_gens``
combined with a persistent generator that implements the required batch/subbatch logic.

fast_alloc
----------
.. deprecated:: 2.0
Use :func:`~libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first` or the default
:func:`~libensemble.alloc_funcs.start_only_persistent.only_persistent_gens` instead.
Will be removed in libEnsemble 2.1.

.. automodule:: fast_alloc
:members:
:undoc-members:

fast_alloc_and_pausing
----------------------
.. deprecated:: 2.0
Use the default :func:`~libensemble.alloc_funcs.start_only_persistent.only_persistent_gens`
with a persistent generator instead. Will be removed in libEnsemble 2.1.

.. automodule:: fast_alloc_and_pausing
:members:
:undoc-members:

only_one_gen_alloc
------------------
.. deprecated:: 2.0
Use :func:`~libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first` with
``num_active_gens=1``, or the default
:func:`~libensemble.alloc_funcs.start_only_persistent.only_persistent_gens` instead.
Will be removed in libEnsemble 2.1.

.. automodule:: only_one_gen_alloc
:members:
:undoc-members:

start_fd_persistent
-------------------
.. deprecated:: 2.0
Use the default :func:`~libensemble.alloc_funcs.start_only_persistent.only_persistent_gens`
with a persistent generator instead. Will be removed in libEnsemble 2.1.

.. automodule:: start_fd_persistent
:members:
:undoc-members:

persistent_aposmm_alloc
-----------------------
.. automodule:: persistent_aposmm_alloc
:members:
:undoc-members:
start_persistent_local_opt_gens
-------------------------------
.. deprecated:: 2.0
Use :func:`~libensemble.alloc_funcs.persistent_aposmm_alloc.persistent_aposmm_alloc`
instead. Will be removed in libEnsemble 2.1.

give_pregenerated_work
----------------------
.. automodule:: give_pregenerated_work
:members:
:undoc-members:
.. automodule:: start_persistent_local_opt_gens
:members:
:undoc-members:

inverse_bayes_allocf
--------------------
.. deprecated:: 2.0
Use the default :func:`~libensemble.alloc_funcs.start_only_persistent.only_persistent_gens`
with a persistent generator instead. Will be removed in libEnsemble 2.1.

.. automodule:: inverse_bayes_allocf
:members:
:undoc-members:
38 changes: 38 additions & 0 deletions libensemble/_deprecation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Deprecation utilities for libEnsemble.
"""

import warnings


class LibEnsembleDeprecationWarning(DeprecationWarning):
"""Warning category for deprecated libEnsemble features.

Subclass of :class:`DeprecationWarning` so users can filter libEnsemble
deprecations independently::

import warnings
from libensemble._deprecation import LibEnsembleDeprecationWarning
warnings.filterwarnings("error", category=LibEnsembleDeprecationWarning)
"""


def warn_deprecated(name: str, replacement: str, removal_version: str = "2.1") -> None:
"""Emit a :class:`LibEnsembleDeprecationWarning` for a deprecated feature.

Parameters
----------
name:
Dotted module or object path (e.g. ``"libensemble.alloc_funcs.fast_alloc"``).
replacement:
Human-readable description of the recommended replacement.
removal_version:
The libEnsemble version in which the feature will be removed.
"""
warnings.warn(
f"{name} is deprecated as of libEnsemble 2.0 "
f"and will be removed in {removal_version}. "
f"Use {replacement} instead.",
LibEnsembleDeprecationWarning,
stacklevel=3,
)
12 changes: 12 additions & 0 deletions libensemble/alloc_funcs/fast_alloc.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
from libensemble._deprecation import warn_deprecated
from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources

warn_deprecated(
name="libensemble.alloc_funcs.fast_alloc",
replacement="libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first "
"or the default only_persistent_gens with a persistent generator",
)


def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
"""
.. deprecated:: 2.0
``fast_alloc.give_sim_work_first`` is deprecated and will be removed in libEnsemble 2.1.
Use :func:`libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first` or the
default ``only_persistent_gens`` (with a persistent generator) instead.

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
Expand Down
11 changes: 11 additions & 0 deletions libensemble/alloc_funcs/fast_alloc_and_pausing.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import numpy as np

from libensemble._deprecation import warn_deprecated
from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources

warn_deprecated(
name="libensemble.alloc_funcs.fast_alloc_and_pausing",
replacement="the default only_persistent_gens with a persistent generator",
)


def give_sim_work_first(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
"""
.. deprecated:: 2.0
``fast_alloc_and_pausing.give_sim_work_first`` is deprecated and will be removed in
libEnsemble 2.1. Use the default ``only_persistent_gens`` (with a persistent generator)
instead.

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
Expand Down
11 changes: 11 additions & 0 deletions libensemble/alloc_funcs/inverse_bayes_allocf.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import numpy as np

from libensemble._deprecation import warn_deprecated
from libensemble.message_numbers import EVAL_GEN_TAG
from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources

warn_deprecated(
name="libensemble.alloc_funcs.inverse_bayes_allocf",
replacement="the default only_persistent_gens with a persistent generator",
)


def only_persistent_gens_for_inverse_bayes(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
"""
.. deprecated:: 2.0
``inverse_bayes_allocf.only_persistent_gens_for_inverse_bayes`` is deprecated and will
be removed in libEnsemble 2.1. Use the default ``only_persistent_gens`` (with a
persistent generator) instead.

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.
Expand Down
12 changes: 12 additions & 0 deletions libensemble/alloc_funcs/only_one_gen_alloc.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
from libensemble._deprecation import warn_deprecated
from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources

warn_deprecated(
name="libensemble.alloc_funcs.only_one_gen_alloc",
replacement="libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first "
"with num_active_gens=1, or the default only_persistent_gens",
)


def ensure_one_active_gen(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
"""
.. deprecated:: 2.0
``only_one_gen_alloc.ensure_one_active_gen`` is deprecated and will be removed in
libEnsemble 2.1. Use :func:`libensemble.alloc_funcs.give_sim_work_first.give_sim_work_first`
with ``num_active_gens=1``, or the default ``only_persistent_gens`` instead.

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.
Expand Down
11 changes: 11 additions & 0 deletions libensemble/alloc_funcs/start_fd_persistent.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import numpy as np

from libensemble._deprecation import warn_deprecated
from libensemble.message_numbers import EVAL_GEN_TAG
from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources

warn_deprecated(
name="libensemble.alloc_funcs.start_fd_persistent",
replacement="the default only_persistent_gens with a persistent generator",
)


def finite_diff_alloc(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
"""
.. deprecated:: 2.0
``start_fd_persistent.finite_diff_alloc`` is deprecated and will be removed in
libEnsemble 2.1. Use the default ``only_persistent_gens`` (with a persistent generator)
instead.

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
Expand Down
11 changes: 11 additions & 0 deletions libensemble/alloc_funcs/start_persistent_local_opt_gens.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import numpy as np

from libensemble._deprecation import warn_deprecated
from libensemble.gen_funcs.persistent_aposmm import decide_where_to_start_localopt, extract_rk_c, update_history_dist
from libensemble.message_numbers import EVAL_GEN_TAG
from libensemble.tools.alloc_support import AllocSupport, InsufficientFreeResources

warn_deprecated(
name="libensemble.alloc_funcs.start_persistent_local_opt_gens",
replacement="libensemble.alloc_funcs.persistent_aposmm_alloc.persistent_aposmm_alloc",
)


def start_persistent_local_opt_gens(W, H, sim_specs, gen_specs, alloc_specs, persis_info, libE_info):
"""
.. deprecated:: 2.0
``start_persistent_local_opt_gens.start_persistent_local_opt_gens`` is deprecated and
will be removed in libEnsemble 2.1. Use
:func:`libensemble.alloc_funcs.persistent_aposmm_alloc.persistent_aposmm_alloc` instead.

This allocation function will do the following:

- Start up a persistent generator that is a local opt run at the first point
Expand Down
1 change: 1 addition & 0 deletions libensemble/gen_classes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .aposmm import APOSMM # noqa: F401
from .preloaded import PreloadedSampleGenerator # noqa: F401
from .sampling import UniformSample # noqa: F401
Loading