Constrained optimization#
OMADS handles inequality constraints \(c_j(x) \le 0\) returned by the blackbox. Each
constraint gets a barrier type in param["constraints_type"]:
- Progressive barrier (
"PB") The constraint is relaxable. Infeasible points are retained and ranked by their violation \(h(x) = \sum_j \max\{c_j(x), 0\}^2\), and the threshold \(h_{\max}\) decreases over the iterations to drive the search towards the feasible region [Audet and Dennis, 2009].
- Extreme barrier (
"EB") The constraint is unrelaxable (for example, the simulation cannot be run when it is violated), and any point that violates it is rejected. This is the default for constraints without a declared type.
Prefer "PB" whenever the blackbox can still be evaluated at points that violate the
constraint. The barrier then also learns from infeasible points, and you can start from an
infeasible baseline.
A thin feasible region#
The feasible region of this problem is a thin band of height \(0.1\) that follows a sine wave, \(\sin(x_1) - 0.1 \le x_2 \le \sin(x_1)\). The objective is the distance to \((20, 1)\), which lies just outside the band, and the starting point \((0, -10)\) is far from feasible.
"""A thin, curved feasible region handled with the progressive barrier."""
import numpy as np
from OMADS import mads
def thin_con(x):
f = np.sqrt((x[0] - 20.0) ** 2 + (x[1] - 1.0) ** 2)
c1 = np.sin(x[0]) - 0.1 - x[1] # x2 >= sin(x1) - 0.1
c2 = x[1] - np.sin(x[0]) # x2 <= sin(x1)
return [f, [c1, c2]]
data = {
"evaluator": {"blackbox": thin_con},
"param": {
"name": "thin_con",
"baseline": [0.0, -10.0], # infeasible start
"lb": [0.0, -10.0],
"ub": [25.0, 10.0],
"scaling": [25.0, 20.0],
"constraints_type": ["PB", "PB"],
"post_dir": "./post",
},
"options": {"seed": 0, "budget": 2000, "tol": 1e-9,
"check_cache": True, "store_cache": True, "rich_direction": True},
"search": {"type": "sampling", "s_method": "ACTIVE", "ns": 10},
}
if __name__ == "__main__":
out, *_ = mads.main(data)
print("xmin =", out["xmin"])
print("fmin =", out["fmin"], " hmin =", out["hmin"])
print("evaluations:", out["nbb_evals"])
xmin = [20.0288661159575, 0.9243430346250534]
fmin = [0.08097671924829766] hmin = 1e-15
evaluations: 536
The run starts in phase one, minimizing \(h\) until a feasible point is found, then
improves the objective along the band. The reported hmin of 1e-15 is the machine
zero, which means the solution is feasible. The best known value is \(f \approx 0.0989\)
at the start of the band, so this run found an even better point near the band’s upper
edge.
Reading the results#
out["hmin"]is the violation of the returned point. A value above the machine zero means OMADS found no feasible point within the budget, andxminis then the least infeasible point.poll.mainalso returnsout["Secondary fmin"]andout["Secondary hmin"], the best infeasible point kept by the progressive barrier.With
options["save_results"]theStatuscolumn of the results file showsFEASIBLEorINFEASIBLEfor each evaluation; see Results post-processing.
Tip
Scale the constraints so that their violations have comparable magnitudes, because \(h\) sums their squares.
Mixing barrier types is allowed:
"constraints_type": ["PB", "EB", "PB"].Set
param["h_max"]to a finite value to reject, from the start, points that are too infeasible.