Multi-objective optimization#
OMADS can approximate the Pareto front of problems with several objectives. The blackbox returns a list of objective values, and three parameters switch on the Pareto study:
def blackbox(x):
return [[f1, f2], [c1, c2]] # objectives first, then constraints
param = {..., "nobj": 2, "is_pareto": True, "fun_names": ["f1", "f2"]}
Internally the adaptive barrier keeps the whole set of non-dominated points, each with its own mesh, and the poll picks its frame centre where the front has the largest gaps (see Algorithm 2). The quality of the front is measured with the hypervolume indicator.
The Binh and Korn problem#
"""Bi-objective Binh and Korn problem, solved with a Pareto poll study."""
import numpy as np
import pandas as pd
from OMADS import poll
def binh_korn(x):
f1 = 4.0 * x[0] ** 2 + 4.0 * x[1] ** 2
f2 = (x[0] - 5.0) ** 2 + (x[1] - 5.0) ** 2
return [[f1, f2], [0]]
data = {
"evaluator": {"blackbox": binh_korn},
"param": {
"name": "binh_korn",
"baseline": [2.5, 1.5],
"lb": [0.0, 0.0],
"ub": [5.0, 3.0],
"scaling": [5.0, 3.0],
"var_names": ["x1", "x2"],
"fun_names": ["f1", "f2"],
"nobj": 2,
"is_pareto": True,
"ref_point": [140.0, 50.0],
"post_dir": "./post",
},
"options": {"seed": 0, "budget": 1500, "tol": 1e-9,
"check_cache": True, "store_cache": True, "rich_direction": True,
"save_results": True},
}
def pareto_front(csv_path, objectives):
"""Non-dominated feasible rows of an OMADS results file."""
df = pd.read_csv(csv_path, skipinitialspace=True)
df.columns = df.columns.str.strip()
df = df[df["Status"].str.strip() == "FEASIBLE"].drop_duplicates(objectives)
F = df[objectives].to_numpy()
keep = [not np.any(np.all(F <= f, axis=1) & np.any(F < f, axis=1)) for f in F]
return df[keep].sort_values(objectives[0])
if __name__ == "__main__":
out, _ = poll.main(data)
print("hypervolume =", round(out["HV"], 4))
front = pareto_front("post/binh_korn_run/binh_korn_all.out", ["f1", "f2"])
print(len(front), "non-dominated points, e.g.")
print(front[["x1", "x2", "f1", "f2"]].iloc[::len(front) // 5].to_string(index=False))
hypervolume = 0.8435
380 non-dominated points, e.g.
x1 x2 f1 f2
0.000000 0.000000 0.000000 50.000000
0.750000 0.718750 4.316406 36.391602
1.593750 1.515625 19.348633 23.743408
2.500000 2.453125 49.071289 12.736572
3.484375 3.000000 84.563477 6.297119
The front runs from \((0, 50)\) at \(x = (0, 0)\) to the region around
\(x_2 = 3\), where the upper bound becomes active. out["xmin"] and out["fmin"]
hold one point of the front; the whole front is in the results file.
Important
In this release, use poll.main for multi-objective problems. mads.main stops with
an IndexError while writing the Pareto file, and the ACTIVE sampling search
fails on multi-objective problems. Also, <name>_Pareto.out only keeps the starting
point, which is why the script extracts the front from <name>_all.out.
Gallery#
Pareto fronts obtained by OMADS on bi-objective test problems from the literature.
Figure 5 Constrained bi-objective test problems.#
Figure 6 Unconstrained bi-objective test problems.#