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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ Example:
>>> VOCS(
variables = {"x1":[0, 1], "x2":[0, 5]},
objectives = {"f1":"MAXIMIZE"},
constants = {"alpha": 0.55},
constraints = {"c1":["LESS_THAN", 0]},
constants = {"alpha": 0.55},
observables = {"o1"}
)
```
Expand Down
3 changes: 1 addition & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@

# General information about the project.
project = "Generator_Standard"
# copyright = str(datetime.now().year) + " Argonne National Laboratory"
# author = "Jeffrey Larson, Stephen Hudson, Stefan M. Wild, David Bindel and John-Luke Navarro"
copyright = str(datetime.now().year) + " Generator Standard Authors"
# today_fmt = "%B %-d, %Y"

# The version info for the project you're documenting, acts as replacement for
Expand Down
4 changes: 4 additions & 0 deletions docs/generator.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
.. _generator:

=============
Generator API
=============

.. autoclass:: generator_standard.generator.Generator
:members:
:private-members:
:special-members:
9 changes: 5 additions & 4 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

======================
README / Specification
======================
==================
Generator Standard
==================

.. include:: ../README.md
:parser: myst_parser.sphinx_
Expand All @@ -13,4 +13,5 @@ README / Specification
:maxdepth: 2

generator
vocs
vocs
patterns
64 changes: 64 additions & 0 deletions docs/patterns.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.. _patterns:

=====================
Common Usage Patterns
=====================

Within Jupyter notebook or interpreter
--------------------------------------

.. code-block:: python

from my_objective import my_objective_function
from generator_standard.tests.test_generator import RandomGenerator
from generator_standard.vocs import VOCS

vocs = VOCS(variables={"x": [0.0, 1.0]}, objectives={"f": "MINIMIZE"})
gen = RandomGenerator(vocs)

points = gen.suggest(10)
for point in points:
point["f"] = my_objective_function(point["x"])

gen.ingest(points)

Within workflow library - libEnsemble
-------------------------------------

`libEnsemble <https://github.com/Libensemble/libensemble>`_ is a Python
workflow toolkit for coordinating asynchronous and dsynamic ensembles
of calculations. It plans to support plugging in standard generators similarly
to the following:

.. code-block:: python

from generator_standard.tests.test_generator import RandomGenerator
from generator_standard.vocs import VOCS

from my_objective import libE_styled_objective_function

from libensemble import Ensemble
from libensemble.specs import GenSpecs, ExitCriteria

vocs = VOCS(variables={"x": [0.0, 1.0]}, objectives={"f": "MINIMIZE"})
gen = RandomGenerator(vocs)

workflow = Ensemble()

workflow.sim_specs = SimSpecs(
sim_f = libE_styled_objective_function,
inputs = ["x"]
outputs = [("f", float)],
)

workflow.gen_specs = GenSpecs(
generator=gen,
persis_in=["f"], # keep passing "f" results to the standard generator
outputs=[("x", float)],
initial_batch_size=10,
batch_size=5
)

workflow.exit_criteria = ExitCriteria(sim_max=500)

results = workflow.run()
4 changes: 4 additions & 0 deletions docs/vocs.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _vocs:

========
VOCS API
========
Expand All @@ -10,3 +12,5 @@ VOCS API
:model-show-config-member: False
:model-show-config-summary: False
:member-order: bysource
:model-show-field-summary: False
:model-signature-prefix:
74 changes: 64 additions & 10 deletions generator_standard/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class Generator(ABC):
"""
Tentative suggest/ingest generator interface
Each standardized generator is a Python class that inherits from this class.

.. code-block:: python

Expand All @@ -31,48 +31,102 @@ def finalize(self):
@abstractmethod
def __init__(self, vocs: VOCS, *args, **kwargs):
"""
Initialize the Generator object on the user-side. Constants, class-attributes,
and preparation goes here.
The mandatory :ref:`VOCS<vocs>` defines the input and output names used inside the generator.

The constructor also accomodates variable positional and keyword arguments so each generator can be customized.

.. code-block:: python

>>> my_generator = MyGenerator(vocs, my_parameter, my_keyword=10)

.. code-block:: python

>>> my_generator = MyGenerator(vocs, my_keyword=10)
>>> generator = NelderMead(VOCS(variables={"x": [-5.0, 5.0], "y": [-3.0, 2.0]}, objectives={"f": "MAXIMIZE"}), adaptive=False)
"""
self._validate_vocs(vocs)

@abstractmethod
def _validate_vocs(self, vocs) -> None:
"""
Validate if the vocs object is compatible with the current generator. Should
raise a ValueError if the vocs object is not compatible with the generator
object
Validate if the ``VOCS`` is compatible with the current generator. Should
raise a ``ValueError`` if it is incompatible.

.. code-block:: python

>>> generator = NelderMead(
VOCS(
variables={"x": [-5.0, 5.0], "y": [-3.0, 2.0]},
objectives={"f": "MAXIMIZE"},
constraints={"c":["LESS_THAN", 0.0]}
)
)

ValueError("NelderMead generator cannot accept constraints")
"""

@abstractmethod
def suggest(self, num_points: int | None) -> list[dict]:
"""
Request the next set of points to evaluate.
Returns set of points in the input space, to be evaluated next.
Each element of the list is a separate point. Keys of the dictionary include the name
of each input variable specified in the constructor. Values of the dictionaries are **scalars**.

When ``num_points`` is passed, the generator should return exactly this number of points, or raise
a error ``ValueError`` if it is unable to.

When ``num_points`` is not passed, the generator decides how many points to return.
Different generators will return different number of points. For instance, the simplex
would return 1 or 3 points. A genetic algorithm could return the whole population.
Batched Bayesian optimization would return the batch size (i.e., number of points that
can be processed in parallel), which would be specified in the constructor.

In addition, some generators can generate a unique identifier for each generated point.
If implemented, this identifier should appear in the dictionary under the key ``"_id"``.
When a generator produces an identifier, it must be included in the corresponding
dictionary passed back to that generator in ``ingest`` (under the same key: ``"_id"``).

.. code-block:: python

>>> points = my_generator.suggest(3)
>>> print(points)
[{"x": 1, "y": 1}, {"x": 2, "y": 2}, {"x": 3, "y": 3}]

>>> generator.suggest(100) # too many points
ValueError

>>> generator.suggest()
[{"x": 1.2, "y": 0.8}, {"x": -0.2, "y": 0.4}, {"x": 4.3, "y": -0.1}]
"""

def ingest(self, results: list[dict]) -> None:
"""
Send the results of evaluations to the generator.
Feeds data (past evaluations) to the generator. Each element of the list is a separate point.
Keys of the dictionary must include each named field specified in the ``VOCS`` provided
to the generator on instantiation.

Any points provided to the generator via ``ingest`` that were not created by the current generator
instance should omit the ``_id`` field. If points are given to ``ingest`` with an ``_id`` value that is
not known internally, a ``ValueError`` error should be raised.

.. code-block:: python

>>> results = [{"x": 0.5, "y": 1.5, "f": 1}, {"x": 2, "y": 3, "f": 4}]
>>> my_generator.ingest(results)
...
>>> point = generator.suggest(1)
>>> point
[{"x": 1, "y": 1}]
>>> point["f"] = objective(point)
>>> point
[{"x": 1, "y": 1, "f": 2}]
>>> generator.ingest(point)
"""

def finalize(self) -> None:
"""
Perform any work required to close down the generator.
**Optional**. Performs any work required to close down the generator. Some generators may need
to close down background processes, files, databases, or dump data to disk. This is similar to calling
``.close()`` on an open file.

.. code-block:: python

Expand Down
18 changes: 18 additions & 0 deletions generator_standard/vocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,24 @@ class VOCS(BaseModel):
Variables, Objectives, Constraints, and other Settings (VOCS) data structure
to describe optimization problems.

Each generator accepts this object as the parameter, and must validate
that it can handle the specified set of variables, objectives, constraints, etc.

.. code-block:: python
:linenos:

from generator_standard.vocs import VOCS

>>> vocs = VOCS(
variables = {"x1":[0, 1], "x2":[0, 5]},
objectives = {"f1":"MAXIMIZE"},
constraints = {"c1":["LESS_THAN", 0]},
constants = {"alpha": 0.55},
observables = {"o1"}
)
...
>>> my_generator = MyGenerator(vocs, parameter=100, my_keyword=10)

.. tab-set::

.. tab-item:: variables
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ name = 'Campa_Generator_Standard'
description = 'An effort to standardize the interface of generators in optimization libraries'
readme = 'README.md'
version = '0.1'
dependencies = ["pydantic"]
requires-python = '>=3.10'
keywords = ['optimization', 'workflows', 'generators']
classifiers = [
Expand Down