Skip to content
Merged
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 .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ let model = Model::load("model.json")?;
- [crates/gpx/README.md](../crates/gpx/README.md): Gpx command usage and format options
- [python/src/lib.rs](../python/src/lib.rs): PyO3 module definition
- [python/src/egor.rs](../python/src/egor.rs): Python Egor class wrapping Rust
- [doc/](../doc/): Jupyter notebooks with tutorials
- [notebooks/](../notebooks/): Jupyter notebooks with tutorials

## Testing Notes

Expand Down
115 changes: 115 additions & 0 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: website

on:
pull_request:
branches: [ master ]
paths:
- .github/workflows/website.yml
- crates/ego/examples/**
- crates/gp/examples/**
- website/**
- notebooks/**
- python/egobox/examples/*.py
- python/egobox/tests/*.py
push:
branches: [ master ]
paths:
- .github/workflows/website.yml
- crates/ego/examples/**
- crates/gp/examples/**
- website/**
- notebooks/**
- python/egobox/examples/website_*.py
- python/egobox/tests/test_website_examples.py
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: website-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
env:
UV_CACHE_DIR: /tmp/.uv-cache
RUST_BACKTRACE: 1
MPLBACKEND: Agg

steps:
- uses: actions/checkout@v6

- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'

- name: Set up uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh

- name: Cache folder for uv
uses: actions/cache@v5
with:
path: /tmp/.uv-cache
key: uv-${{ runner.os }}-${{ hashFiles('python/uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-${{ hashFiles('python/uv.lock') }}
uv-${{ runner.os }}

- name: Install the project
working-directory: python
run: uv sync --all-extras --dev --python 3.12

- name: Build the wrapper
uses: PyO3/maturin-action@v1
with:
working-directory: python
command: develop
args: --release

- name: Verify website examples
working-directory: python
run: uv run pytest egobox/tests/test_website_examples.py

- name: Install Zola
uses: taiki-e/install-action@v2
with:
tool: zola

- name: Build site
working-directory: website
run: zola build

- name: Configure Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: actions/configure-pages@v5

- name: Upload Pages artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: actions/upload-pages-artifact@v4
with:
path: website/public

deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ history*

# Output directories
**/*_output/
**/*_out/
**/*_out/

# Generated website
website/public
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ See also some breaking changes description in optimizer options in PR #253
## Version 0.15.0 - 02/01/2024

* `gp`: Implement sparse gaussian process methods (cf. `SparseGaussianProcess`)
* Python binding: `SparseGpMix`, see doc/tutorial
* Python binding: `SparseGpMix`, see notebooks/tutorial
* GP/SGP API
* hyperparameter tuning : initial theta guess and bounds can be specified (`theta_init`, `theta_bounds`)
* `n_start` controls the number of optimization multistart
Expand Down Expand Up @@ -365,7 +365,7 @@ by @relf in <https://github.com/relf/egobox/pull/105>
* `ego`:
* Refactor `Egor` using [`argmin 0.7.0` solver framework](http://argmin-rs.org) `EgorSolver` can be used with `argmin::Executor` and benefit from observers and checkpointing features (#67)
* `Egor` use kriging setting by default (i.e. one cluster with constant mean and squared exponential correlation model)
* Add [notebook on Manuau Loa CO2 example](https://github.com/relf/egobox/blob/master/doc/Gpx_MaunaLoaCO2.ipynb) to show `GpMix`/`Gpx` surrogate model usage (#62)
* Add [notebook on Manuau Loa CO2 example](https://github.com/relf/egobox/blob/master/notebooks/Gpx_MaunaLoaCO2.ipynb) to show `GpMix`/`Gpx` surrogate model usage (#62)
* Use xoshiro instead of isaac random generator (#63)
* Upgrade `ndarray 0.15`, `linfa 0.6.1`, `PyO3 0.17` (#57, #64)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center">
<img
width="100"
src="./doc/LOGO_EGOBOX_v4_100x100.png"
src="./website/static/img/LOGO_EGOBOX_v4_100x100.png"
alt="Efficient Global Optimization toolbox in Rust"
/>
</p>
Expand Down Expand Up @@ -70,7 +70,7 @@ plt.show()

See also [this example written in Rust](crates/gp/examples/kriging.rs)

See the [tutorial notebooks](https://github.com/relf/egobox/tree/master/doc/README.md) and [examples folder](https://github.com/relf/egobox/tree/d9db0248199558f23d966796737d7ffa8f5de589/python/egobox/examples) for more information on the usage of the optimizer and mixture of Gaussian processes surrogate model.
See the [tutorial notebooks](https://github.com/relf/egobox/tree/master/notebooks/README.md) and [examples folder](https://github.com/relf/egobox/tree/d9db0248199558f23d966796737d7ffa8f5de589/python/egobox/examples) for more information on the usage of the optimizer and mixture of Gaussian processes surrogate model.

## The Rust libraries

Expand Down
Binary file removed doc/LOGO_EGOBOX_v4_100x100.png
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 6 additions & 6 deletions doc/README.md → notebooks/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center">
<img
width="100"
src="./LOGO_EGOBOX_v4_100x100.png"
src="../website/static/img/LOGO_EGOBOX_v4_100x100.png"
alt="Efficient Global Optimization toolbox in Rust"
/>
</p>
Expand All @@ -10,23 +10,23 @@

## Surrogate-based adaptative optimization: _Egor_

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/doc/Egor_Tutorial.ipynb)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/notebooks/Egor_Tutorial.ipynb)

## Mixture of Gaussian process surrogates: _Gpx_

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/doc/Gpx_Tutorial.ipynb)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/notebooks/Gpx_Tutorial.ipynb)

## Mixture of Gaussian process surrogates command line tool: _gpx_

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/doc/gpx_cli.ipynb)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/notebooks/gpx_cli.ipynb)

## Gaussian process surrogates: _SparseGpx_

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/doc/SparseGpx_Tutorial.ipynb)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/notebooks/SparseGpx_Tutorial.ipynb)

## _Gpx_ on Mauna Loa CO2 data

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/doc/Gpx_MaunaLoaCO2.ipynb)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/relf/egobox/blob/master/notebooks/Gpx_MaunaLoaCO2.ipynb)

## Solve an analytical MDO problem with a hidden optimum

Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions doc/gpx_cli.ipynb → notebooks/gpx_cli.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"source": [
"## Setup\n",
"\n",
"We keep tutorial artifacts in `doc/gpx_cli_tutorial` and run GPX **directly** with shell commands (`!gpx ...`) in notebook cells."
"We keep tutorial artifacts in `notebooks/gpx_cli_tutorial` and run GPX **directly** with shell commands (`!gpx ...`) in notebook cells."
]
},
{
Expand Down Expand Up @@ -397,7 +397,7 @@
"- `gpx predict <input.csv> --model surrogate_model.gpx --output predictions.csv --with-variance`\n",
"- `gpx py --model surrogate_model.gpx -o gpx.py`\n",
"\n",
"Artifacts are stored in `doc/gpx_cli_tutorial_out`."
"Artifacts are stored in `notebooks/gpx_cli_tutorial_out`."
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ plt.plot(xtrain, ytrain, "o")
plt.show()
```

See the [tutorial notebooks](https://github.com/relf/egobox/tree/master/doc/README.md) and [examples folder](https://github.com/relf/egobox/tree/d9db0248199558f23d966796737d7ffa8f5de589/python/egobox/examples) for more information on the usage of the optimizer and mixture of Gaussian processes surrogate model.
See the [tutorial notebooks](https://github.com/relf/egobox/tree/master/notebooks/README.md) and [examples folder](https://github.com/relf/egobox/tree/d9db0248199558f23d966796737d7ffa8f5de589/python/egobox/examples) for more information on the usage of the optimizer and mixture of Gaussian processes surrogate model.
65 changes: 31 additions & 34 deletions python/egobox/egobox.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ __all__ = [
"Egor",
"EgorOptim",
"ExitStatus",
"FeasibleInfillStrategy",
"FailsafeStrategy",
"FeasibleInfillStrategy",
"GpConfig",
"GpMix",
"Gpx",
Expand Down Expand Up @@ -163,9 +163,9 @@ class Egor:
Infill criteria to decide best next promising point.
Can be either InfillStrategy.LOG_EI, InfillStrategy.EI, InfillStrategy.WB2, InfillStrategy.WB2S

infill_optimizer (InfillOptimizer enum):
Internal optimizer used to optimize infill criteria.
Can be either InfillOptimizer.COBYLA or InfillOptimizer.SLSQP
feasible_infill_strategy (FeasibleInfillStrategy enum):
Strategy to handle feasibility information in the infill criterion.
Can be either FeasibleInfillStrategy.NONE, FeasibleInfillStrategy.EFI_P, or FeasibleInfillStrategy.EFI_FE

cstr_infill (bool):
Activate constrained infill criterion where the product of probability of feasibility of constraints
Expand All @@ -175,6 +175,10 @@ class Egor:
Constraint management either use the mean value or upper bound
Can be either ConstraintStrategy.MeanValue or ConstraintStrategy.UpperTrustedBound.

infill_optimizer (InfillOptimizer enum):
Internal optimizer used to optimize infill criteria.
Can be either InfillOptimizer.COBYLA or InfillOptimizer.SLSQP

qei_config (QEiConfig):
Configuration for parallel (qEI) evaluation also known as batch or multipoint evaluation.
q points are selected at each iteration of the EGO algorithm.
Expand Down Expand Up @@ -207,30 +211,24 @@ class Egor:

seed (int >= 0 or None):
Deprecated: use seed argument in minimize() or suggest() instead.
Random generator seed to allow computation reproducibility.

outdir (String or None):
Deprecated: use outdir argument in minimize() instead.
Directory to write optimization history and used as search path for warm start doe.

warm_start (bool):
Deprecated: use warm_start argument in minimize() instead.
Start by loading initial doe from <outdir> directory.

hot_start (bool, int >= 0 or None):
Deprecated: use hot_start argument in minimize() instead.
When True, hot_start behaves like hot_start=0 with no iteration extension.
When hot_start>=0 saves optimizer state at each iteration and starts from a previous checkpoint.

verbose (int, Verbose enum, or None):
Deprecated: use verbose argument in minimize() instead.
Logging verbosity level for the optimizer.

# Returns

Egor object which can be used to optimize a function using the minimize method.
"""
def __new__(cls, xspecs: typing.Any, gp_config: GpConfig = ..., n_cstr: builtins.int = 0, cstr_tol: typing.Optional[typing.Sequence[builtins.float]] = None, cstr_specs: typing.Optional[typing.Sequence[CstrSpec]] = None, n_start: builtins.int = 20, n_doe: builtins.int = 0, doe: typing.Optional[numpy.typing.NDArray[numpy.float64]] = None, infill_strategy: InfillStrategy = InfillStrategy.LOG_EI, cstr_infill: builtins.bool = False, cstr_strategy: ConstraintStrategy = ConstraintStrategy.MC, qei_config: QEiConfig = ..., infill_optimizer: InfillOptimizer = InfillOptimizer.COBYLA, trego: typing.Optional[typing.Any] = None, coego_n_coop: builtins.int = 0, target: builtins.float = -1.7976931348623157e+308, failsafe_strategy: FailsafeStrategy = FailsafeStrategy.REJECTION, seed: typing.Optional[builtins.int] = None, outdir: typing.Optional[builtins.str] = None, warm_start: builtins.bool = False, hot_start: typing.Optional[typing.Any] = None, verbose: typing.Optional[typing.Any] = None) -> Egor: ...
def __new__(cls, xspecs: typing.Any, gp_config: GpConfig = ..., n_cstr: builtins.int = 0, cstr_tol: typing.Optional[typing.Sequence[builtins.float]] = None, cstr_specs: typing.Optional[typing.Sequence[CstrSpec]] = None, n_start: builtins.int = 20, n_doe: builtins.int = 0, doe: typing.Optional[numpy.typing.NDArray[numpy.float64]] = None, infill_strategy: InfillStrategy = InfillStrategy.LOG_EI, feasible_infill_strategy: FeasibleInfillStrategy = FeasibleInfillStrategy.NONE, cstr_infill: builtins.bool = False, cstr_strategy: ConstraintStrategy = ConstraintStrategy.MC, qei_config: QEiConfig = ..., infill_optimizer: InfillOptimizer = InfillOptimizer.COBYLA, trego: typing.Optional[typing.Any] = None, coego_n_coop: builtins.int = 0, target: builtins.float = -1.7976931348623157e+308, failsafe_strategy: FailsafeStrategy = FailsafeStrategy.REJECTION, seed: typing.Optional[builtins.int] = None, outdir: typing.Optional[builtins.str] = None, warm_start: builtins.bool = False, hot_start: typing.Optional[typing.Any] = None, verbose: typing.Optional[typing.Any] = None) -> Egor: ...
def minimize(self, fun: typing.Any, fcstrs: typing.Sequence[typing.Any] = [], fcstr_specs: typing.Sequence[CstrSpec] = [], max_iters: builtins.int = 20, run_info: typing.Optional[typing.Any] = None, outdir: typing.Optional[builtins.str] = None, warm_start: builtins.bool = False, hot_start: typing.Optional[typing.Any] = None, seed: typing.Optional[builtins.int] = None, timeout: typing.Optional[builtins.float] = None, verbose: typing.Optional[typing.Any] = None) -> EgorOptim:
r"""
This function finds the minimum of a given function "fun"
Expand Down Expand Up @@ -282,14 +280,12 @@ class Egor:
Start by loading initial doe from <outdir> directory

hot_start (bool, int >= 0 or None):
When True, hot_start behaves like hot_start=0 with no iteration extension.
When hot_start>=0 saves optimizer state at each iteration and starts from a previous checkpoint
if any for the given hot_start number of iterations beyond the max_iters nb of iterations.
for the given hot_start number of iterations beyond the max_iters nb of iterations.
In an unstable environment were there can be crashes it allows to restart the optimization
from the last iteration till stopping criterion is reached. Just use hot_start=0 in this case.
When specifying an extended nb of iterations (hot_start > 0) it can allow to continue till max_iters +
hot_start nb of iters is reached (provided the stopping criterion is max_iters)
Checkpoint information is stored in .checkpoint/egor.arg binary file.
When True, hot_start behaves like hot_start=0 with no iteration extension.
Checkpoint information is stored in .checkpoint or under outdir if outdir is specified.

seed (int >= 0):
Random generator seed to allow computation reproducibility.
Expand Down Expand Up @@ -1246,24 +1242,6 @@ class ExitStatus(enum.Enum):
Solver unexpected exit. See logs for details.
"""

@typing.final
class FeasibleInfillStrategy(enum.Enum):
r"""
Expected Feasible Improvement (EFI) strategy for hidden constraints.
"""
NONE = ...
r"""
Do not use feasibility information.
"""
EFI_P = ...
r"""
Expected feasible improvement weighted by full viability probability.
"""
EFI_FE = ...
r"""
Feasibility-enhanced expected feasible improvement (more exploratory).
"""

@typing.final
class FailsafeStrategy(enum.Enum):
r"""
Expand All @@ -1285,6 +1263,25 @@ class FailsafeStrategy(enum.Enum):
to learn which regions of the search space are more likely to fail and avoid them in the future
"""

@typing.final
class FeasibleInfillStrategy(enum.Enum):
r"""
Expected Feasible Improvement (EFI) is an acquisition function that takes into account the feasibility of the points in the optimization process.
It is defined as the product of the Expected Improvement (EI) weighted by the probability of viability
"""
NONE = ...
r"""
Do not use feasibility information
"""
EFI_P = ...
r"""
Use Expected Feasible Improvement with full probability of feasibility
"""
EFI_FE = ...
r"""
Use Expected Feasible Improvement with 0.3 weighted probability of feasibility, which is more exploratory than EfiP
"""

@typing.final
class InfillOptimizer(enum.Enum):
r"""
Expand Down
22 changes: 22 additions & 0 deletions python/egobox/examples/website_egor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import numpy as np

import egobox as egx


def objective(x: np.ndarray) -> np.ndarray:
x = np.atleast_2d(x)
return (x - 3.5) * np.sin((x - 3.5) / np.pi)


def optimize_example() -> egx.EgorOptim:
return egx.Egor([[0.0, 25.0]]).minimize(objective, max_iters=20, seed=42)


def main() -> None:
optim = optimize_example()
print(f"Optimization f={optim.result.y_opt} at {optim.result.x_opt}")
print(f"Status {optim.status.exit} in {optim.status.elapsed_time}s")


if __name__ == "__main__":
main()
Loading
Loading