Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e546da7
add bayesian optimization propagator
ju-he Jul 1, 2025
05cd1b2
refactor prediction in acquisition function
ju-he Jul 3, 2025
1b6ad5b
enhance acquisition function creation with rank stretching and parame…
ju-he Aug 22, 2025
8cfe5fa
enhance SingleGPUFitter to support configurable device and optional d…
ju-he Aug 26, 2025
de4e76b
add tests for bayesian optimizer
ju-he Aug 27, 2025
0317554
Add bayesian optimizer documentation
ju-he Aug 27, 2025
bc3f1b5
Add bayesian optimizer example
ju-he Aug 27, 2025
73f9c72
fix(bayesopt): stabilize GP MLE and eliminate sklearn ConvergenceWarning
ju-he Aug 27, 2025
3a21155
fix(bayesopt): add warm start for hyperparameter fitting
ju-he Aug 27, 2025
6aa6d55
adjust default kernel
ju-he Aug 28, 2025
64ecef4
add robust l_bfgs
ju-he Sep 2, 2025
a455acb
added bo classes to propagators.__init__
ju-he Oct 18, 2025
afa1194
Add integer and categorical parameter support to BayesianOptimizer
ju-he Jan 6, 2026
62654ad
udpate defaults for fitting GPs in bayesopt
ju-he Jan 27, 2026
376dcaa
bayesopt: keep BO parts from mixed commit 81b7b76
ju-he Feb 12, 2026
2d58052
bayesopt: apply local BO-only follow-up changes
ju-he Feb 12, 2026
437ac0b
bugfix: improve handling of categorical and ordinal parameters
ju-he Feb 12, 2026
3969223
further improve handling of categorical and mixed parameter spaces
ju-he Feb 12, 2026
cf6973d
bugfix: use correct world size for rank stretching
ju-he Feb 12, 2026
4478f93
minor improvements
ju-he Feb 13, 2026
ec8ae11
bugfix: increase robustness of subsampling when all loss are non-finfite
ju-he Feb 13, 2026
77f6010
improve edge case handling for generation computation with non-finite…
ju-he Feb 13, 2026
db50aab
improve hyperparameter optimization scheduling
ju-he Feb 13, 2026
e2f306d
testing: consolidated BO tests
ju-he Feb 13, 2026
6dc23d0
update documentation
ju-he Feb 13, 2026
1f17f97
clean up public API so stop exposing non implemented fitter stubs
ju-he Feb 13, 2026
6e33725
add vectorized acquisition evaluation
ju-he Feb 13, 2026
abb5215
improve acquisition lfitting restart logic to prevent excessive resta…
ju-he Feb 13, 2026
9466470
clean up redundant assert
ju-he Feb 13, 2026
26c0d0f
reinstate Gpy version
ju-he Feb 13, 2026
b053084
improve bo test parameters to prevent convergence warnings
ju-he Feb 13, 2026
7adf92c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 13, 2026
0f409fd
style: apply ruff formating and lint fixes
ju-he Feb 13, 2026
ea82d5c
Merge origin/pr/bo-only into pr/bo-only
ju-he Feb 13, 2026
fa44b7e
fix: address mypy failures
ju-he Feb 13, 2026
accbee4
update examples
ju-he Mar 7, 2026
c96d2fb
feat(bayesopt): add Thompson Sampling acquisition (TS)
ju-he Mar 7, 2026
7f6c6b3
fix: add explicit worker context API for rank-sensitive propagators
ju-he Mar 9, 2026
1df7de5
test(pso): add multi-rank regression and forward worker context throu…
ju-he Mar 9, 2026
42b2faf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 15, 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 docs/algos_explained.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ in ``Propulate``.
pso
cmaes
nm
bayesian_optimizer
11 changes: 11 additions & 0 deletions docs/autoapi/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
API Reference
=============

This page contains auto-generated API reference documentation [#f1]_.

.. toctree::
:titlesonly:

/autoapi/propulate/index

.. [#f1] Created with `sphinx-autoapi <https://github.com/readthedocs/sphinx-autoapi>`_
435 changes: 435 additions & 0 deletions docs/bayesian_optimizer.rst

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions docs/tut_bayesopt.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.. _tut_bayesopt:

Bayesian Optimizer Tutorial
===========================

This page links the practical BO usage material in ``Propulate``:

- Theory and implementation details: :ref:`Bayesian Optimization page <bayesopt>`
- Runnable comparison script: ``tutorials/bayesian_optimizer_example.py``

Quick run with default EI acquisition from repository root:

.. code-block:: console

mpirun --use-hwthread-cpus -n 4 python tutorials/bayesian_optimizer_example.py \
--function sphere \
--generations 25 \
--checkpoint ./bo_runs

Run with Thompson Sampling instead (no hyperparameters to tune; diversity is automatic):

.. code-block:: console

mpirun --use-hwthread-cpus -n 4 python tutorials/bayesian_optimizer_example.py \
--function sphere \
--generations 25 \
--bo-acq TS \
--checkpoint ./bo_runs_ts

For minimal custom setups with ``BayesianOptimizer`` + ``Propulator``, including
Thompson Sampling and mixed-type examples, see :ref:`the BO documentation page <bayesopt>`.
6 changes: 6 additions & 0 deletions docs/tut_propulator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ optimizer flavors are available, along with example scripts showing how to use t
works. Check out the example script for how to use Nelder-Mead in ``Propulate`` |:dna:|:
https://github.com/Helmholtz-AI-Energy/propulate/blob/master/tutorials/nm_example.py

**Bayesian Optimization (BO)**
Uses a Gaussian-process surrogate and an acquisition function to propose sample-efficient evaluations for expensive
black-box objectives. :ref:`Here<bayesopt>` you can find a more detailed explanation of the implementation in
``Propulate`` |:dna:|. Check out the example script:
https://github.com/Helmholtz-AI-Energy/propulate/blob/master/tutorials/bayesian_optimizer_example.py


[1] *N. Hansen and A. Ostermeier (2001), "Completely Derandomized Self-Adaptation in Evolution Strategies", Evolutionary Computation, 9(2), 159-195.*
https://doi.org/10.1162/106365601750190398
Expand Down
1 change: 1 addition & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ If you want to use ``Propulate`` for your own applications, you can use these sc
tut_surrogate
tut_multi_rank_worker
tut_ddp
tut_bayesopt

.. Links
.. _Github: https://github.com/Helmholtz-AI-Energy/propulate
21 changes: 18 additions & 3 deletions propulate/population.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@
import numpy as np


def _normalize_param_type(value: Any) -> type:
"""Map a limit value to its canonical Python type (float, int, or str).

Handles numpy scalar types (e.g. ``np.int64``, ``np.float32``) so that
downstream ``is`` checks against ``int`` / ``float`` work reliably.
"""
if isinstance(value, str):
return str
if isinstance(value, (int, np.integer)):
return int
if isinstance(value, (float, np.floating)):
return float
raise TypeError(f"Unsupported parameter type: {type(value)}")


class Individual:
"""An individual represents a candidate solution to the considered optimization problem."""

Expand Down Expand Up @@ -42,7 +57,7 @@ def __init__(
if key.startswith("_"):
raise ValueError("Keys starting with '_' are reserved.")
# NOTE keep track of the types of variables for setting and getting
self.types = {key: type(limits[key][0]) for key in limits}
self.types = {key: _normalize_param_type(limits[key][0]) for key in limits}
# NOTE offsets are used to keep track of where each variable is stored in the position field, since a categorical embedding can take up more space than other types of variables
offset = 0
self.offsets = {}
Expand Down Expand Up @@ -119,8 +134,8 @@ def __setitem__(self, key: str, newvalue: Union[float, int, str, Any]) -> None:
elif self.types[key] is str:
assert newvalue in self.limits[key]
offset = self.offsets[key]
upper = len(self.limits[key])
self.position[offset:upper] = np.array([0])
upper = offset + len(self.limits[key])
self.position[offset:upper] = 0.0
self.position[offset + self.limits[key].index(newvalue)] = 1.0
else:
raise ValueError("Unknown type")
Expand Down
30 changes: 30 additions & 0 deletions propulate/propagators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@
SelectUniform,
Stochastic,
)
from .bayesopt import (
AcquisitionFunction,
BayesianOptimizer,
ExpectedImprovement,
FitterType,
LowerConfidenceBound,
MultiStartAcquisitionOptimizer,
ProbabilityImprovement,
SingleCPUFitter,
SurrogateFitter,
UpperConfidenceBound,
create_acquisition,
create_fitter,
expected_improvement,
get_default_kernel_sklearn,
)
from .cmaes import (
ActiveCMA,
BasicCMA,
Expand Down Expand Up @@ -60,4 +76,18 @@
"BasicCMA",
"ActiveCMA",
"ParallelNelderMead",
"BayesianOptimizer",
"expected_improvement",
"create_acquisition",
"AcquisitionFunction",
"ExpectedImprovement",
"ProbabilityImprovement",
"UpperConfidenceBound",
"LowerConfidenceBound",
"MultiStartAcquisitionOptimizer",
"SurrogateFitter",
"FitterType",
"SingleCPUFitter",
"create_fitter",
"get_default_kernel_sklearn",
]
29 changes: 29 additions & 0 deletions propulate/propagators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class Propagator:
-------
__call__()
Apply the propagator.
set_worker_context()
Configure worker-rank context for rank-sensitive propagators.
"""

def __init__(self, parents: int = 0, offspring: int = 0, rng: Optional[random.Random] = None) -> None:
Expand Down Expand Up @@ -95,6 +97,23 @@ def __call__(self, inds: List[Individual]) -> Union[List[Individual], Individual
"""
raise NotImplementedError()

def set_worker_context(self, worker_rank: int, worker_size: int) -> None:
"""Set worker communicator context for rank-sensitive propagators.

Parameters
----------
worker_rank : int
Rank in the worker/island communicator.
worker_size : int
Number of workers in the worker/island communicator.

Notes
-----
Default is a no-op to keep backward compatibility. Propagators that
depend on rank-local state (e.g. BO/PSO) can override this method.
"""
_ = (worker_rank, worker_size)


class Stochastic(Propagator):
"""
Expand Down Expand Up @@ -221,6 +240,11 @@ def __call__(self, inds: List[Individual]) -> Union[List[Individual], Individual
else: # Else apply `false_prop`.
return self.false_prop(inds)

def set_worker_context(self, worker_rank: int, worker_size: int) -> None:
"""Propagate worker context to wrapped propagators."""
self.true_prop.set_worker_context(worker_rank, worker_size)
self.false_prop.set_worker_context(worker_rank, worker_size)


class Compose(Propagator):
"""
Expand Down Expand Up @@ -288,6 +312,11 @@ def __call__(self, inds: List[Individual]) -> Union[List[Individual], Individual
inds = p(inds) # type: ignore
return inds

def set_worker_context(self, worker_rank: int, worker_size: int) -> None:
"""Propagate worker context to all composed propagators."""
for p in self.propagators:
p.set_worker_context(worker_rank, worker_size)


class SelectMin(Propagator):
"""
Expand Down
Loading