Getting started#
Installation#
OMADS needs Python 3.10 or newer.
From the Python Package Index:
$ pip install OMADS
The optional plotting dependencies (matplotlib, plotly) are installed with
pip install "OMADS[visualization]". To install from source and run the test suite:
$ git clone https://github.com/Ahmed-Bayoumy/OMADS.git
$ cd OMADS
$ pip install -e ".[test]"
$ pytest tests
OMADS pulls in numpy, scipy, pandas, pydantic, deap, paramiko and
samplersLib (the design-of-experiments and
density-guided samplers used by the search step).
Your first optimization#
A run consists of three steps:
write the blackbox, a Python callable that takes the design vector
xand returns[f, [c1, c2, ...]];describe the problem in a dictionary (or JSON file) with the
evaluator,param,optionsandsearchsections;call a solver:
mads.main(data),poll.main(data)orsearch.main(data).
import numpy as np
from OMADS import mads
def rosenbrock(x):
x = np.asarray(x)
return [np.sum(100.0 * (x[1:] - x[:-1] ** 2) ** 2 + (1 - x[:-1]) ** 2), [0]]
data = {
"evaluator": {"blackbox": rosenbrock},
"param": {
"name": "rosen", # used to name the output folder
"baseline": [-2.0, -2.0], # starting point x0
"lb": [-5, -5], "ub": [10, 10], # bounds
"scaling": [15, 15], # see "Scaling" below
"post_dir": "./post", # log and result files go here
},
"options": {"seed": 0, "budget": 1000, "tol": 1e-9, "rich_direction": True,
"check_cache": True, "store_cache": True},
"search": {"type": "sampling", "s_method": "ACTIVE", "ns": 10},
}
if __name__ == "__main__":
out, poll_step, search_step = mads.main(data)
print(out["xmin"], out["fmin"], out["nbb_evals"])
[1.0, 1.0] [0.0] 223
Important
Guard the call with if __name__ == "__main__": in scripts. OMADS uses
multiprocessing when parallel_mode is on, and on Windows and macOS worker
processes re-import your script.
The blackbox contract#
The evaluator calls your function with a list of floats, ordered like baseline, and
expects back a two-item list:
def blackbox(x):
f = ... # objective: a float, or a list of floats for
# multi-objective problems
c = [c1, c2, ...] # inequality constraints, feasible when c_j(x) <= 0
return [f, c] # unconstrained problems: return [f, [0]]
A bare
floatreturn is accepted and treated as[f, [0]].Add a second positional argument to receive the
evaluator["constants"]list:def blackbox(x, p): ...(see Blackbox evaluation).Return
numpy.inf(ornan) for a failed evaluation; OMADS marks the point infeasible or as an error and keeps going.
Choosing a solver#
Entry point |
What it runs |
Use it when |
|---|---|---|
|
Model search, then poll, plus the sampling search after unsuccessful polls. |
Default choice. Most robust on multimodal and constrained problems. |
|
Poll step only (no search). |
You want the plain, deterministic-step MADS behaviour, a multi-objective Pareto
study, or the poll trajectory ( |
|
Sampling search only, on the mesh. |
Exploring a design space, or comparing search strategies. |
All three take the same input (a dictionary or the path to a JSON file) and return a tuple whose first item is the results dictionary (see Outputs):
out, poll_step, search_step = mads.main(data) # three items
out, poll_step = poll.main(data) # two items
out, search_step = search.main(data) # two items
Using a JSON file#
The same problem can live in a JSON file, with a string naming an executable in
evaluator.blackbox instead of a Python callable:
{
"evaluator": {"blackbox": "my_simulator", "path": "./sim",
"input": "input.inp", "output": "output.out"},
"param": {"name": "sim", "baseline": [0.5, 0.5], "lb": [0, 0], "ub": [1, 1],
"scaling": [1, 1], "post_dir": "./post"},
"options": {"seed": 0, "budget": 500, "check_cache": true, "store_cache": true},
"search": {"type": "sampling", "s_method": "LH", "ns": 10}
}
from OMADS import mads
out, *_ = mads.main("problem.json")
See Blackbox evaluation for the file protocol of external executables.
Scaling and the initial frame#
Poll steps are expressed in units of \((u_i - \ell_i)/s_i\) where \(s_i\) is
param["scaling"] (a scalar or one value per variable). Setting scaling to the
variable ranges, ub - lb, makes the frame size equal to a step in the original units;
options["psize_init"] (default 1.0) is then the size of the first poll step for the
default orthogonal mesh. With "mesh_type": "GMESH" the initial frame is instead one
tenth of each variable range.
Tip
Problems whose variables span very different magnitudes (for example 1e-6 to
1e6) converge much better when scaling is set per variable.
Next steps#
Walk through the Tutorials.
Look up any key in the Configuration reference.
Read how the algorithm works in Background.