!pip install -q pymoo import numpy as np import matplotlib.pyplot as plt from pymoo.core.problem import ElementwiseProblem from pymoo.algorithms.moo.nsga2 import NSGA2 from pymoo.factory import get_sampling, get_crossover, get_mutation from pymoo.factory import get_termination from pymoo.optimize import minimize from pymoo.decomposition.asf import ASF from pymoo.mcdm.pseudo_weights import PseudoWeights from pymoo.util.misc import stack from pymoo.optimize import minimize from pymoo.indicators.hv import Hypervolume from pymoo.util.running_metric import RunningMetric from pymoo.util.running_metric import RunningMetric from pymoo.indicators.igd import IGD from pymoo.indicators.igd_plus import IGDPlus X1, X2 = np.meshgrid(np.linspace(-2, 2, 500), np.linspace(-2, 2, 500)) F1 = X1**2 + X2**2 F2 = (X1-1)**2 + X2**2 G = X1**2 - X1 + 3/16 G1 = 2 * (X1[0] - 0.1) * (X1[0] - 0.9) G2 = 20 * (X1[0] - 0.4) * (X1[0] - 0.6) plt.rc('font', family='serif') levels = [0.02, 0.1, 0.25, 0.5, 0.8] plt.figure(figsize=(7, 5)) CS = plt.contour(X1, X2, F1, levels, colors='black', alpha=0.5) CS.collections[0].set_label("$f_1(x)$") CS = plt.contour(X1, X2, F2, levels, linestyles="dashed", colors='black', alpha=0.5) CS.collections[0].set_label("$f_2(x)$") plt.plot(X1[0], G1, linewidth=2.0, color="green", linestyle='dotted') plt.plot(X1[0][G1<0], G1[G1<0], label="$g_1(x)$", linewidth=2.0, color="green") plt.plot(X1[0], G2, linewidth=2.0, color="blue", linestyle='dotted') plt.plot(X1[0][X1[0]>0.6], G2[X1[0]>0.6], label="$g_2(x)$",linewidth=2.0, color="blue") plt.plot(X1[0][X1[0]<0.4], G2[X1[0]<0.4], linewidth=2.0, color="blue") plt.plot(np.linspace(0.1,0.4,100), np.zeros(100),linewidth=3.0, color="orange") plt.plot(np.linspace(0.6,0.9,100), np.zeros(100),linewidth=3.0, color="orange") plt.xlim(-0.5, 1.5) plt.ylim(-0.5, 1) plt.xlabel("$x_1$") plt.ylabel("$x_2$") plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.12), ncol=4, fancybox=True, shadow=False) plt.tight_layout() plt.show() plt.figure(figsize=(7, 5)) f2 = lambda f1: - ((f1/100) ** 0.5 - 1)**2 F1_a, F1_b = np.linspace(1, 16, 300), np.linspace(36, 81, 300) F2_a, F2_b = f2(F1_a), f2(F1_b) plt.rc('font', family='serif') plt.plot(F1_a,F2_a, linewidth=2.0, color="green", label="Pareto-front") plt.plot(F1_b,F2_b, linewidth=2.0, color="green") plt.xlabel("$f_1$") plt.ylabel("$f_2$") plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.10), ncol=4, fancybox=True, shadow=False) plt.tight_layout() plt.show() class MyProblem(ElementwiseProblem): def __init__(self): super().__init__(n_var=2, n_obj=2, n_constr=2, xl=np.array([-2,-2]), xu=np.array([2,2])) def _evaluate(self, x, out, *args, **kwargs): f1 = 100 * (x[0]**2 + x[1]**2) f2 = (x[0]-1)**2 + x[1]**2 g1 = 2*(x[0]-0.1) * (x[0]-0.9) / 0.18 g2 = - 20*(x[0]-0.4) * (x[0]-0.6) / 4.8 out["F"] = [f1, f2] out["G"] = [g1, g2] problem = MyProblem() algorithm = NSGA2( pop_size=40, n_offsprings=10, sampling=get_sampling("real_random"), crossover=get_crossover("real_sbx", prob=0.9, eta=15), mutation=get_mutation("real_pm", eta=20), eliminate_duplicates=True ) termination = get_termination("n_gen", 40) res = minimize(problem, algorithm, termination, seed=1, save_history=True, verbose=True) X = res.X F = res.F xl, xu = problem.bounds() plt.figure(figsize=(7, 5)) plt.scatter(X[:, 0], X[:, 1], s=30, facecolors='none', edgecolors='r') plt.xlim(xl[0], xu[0]) plt.ylim(xl[1], xu[1]) plt.title("Design Space") plt.show() plt.figure(figsize=(7, 5)) plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue') plt.title("Objective Space") plt.show() F = res.F xl, xu = problem.bounds() plt.figure(figsize=(7, 5)) plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue') plt.title("Objective Space") plt.show() fl = F.min(axis=0) fu = F.max(axis=0) print(f"Scale f1: [{fl[0]}, {fu[0]}]") print(f"Scale f2: [{fl[1]}, {fu[1]}]") approx_ideal = F.min(axis=0) approx_nadir = F.max(axis=0) plt.figure(figsize=(7, 5)) plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue') plt.scatter(approx_ideal[0], approx_ideal[1], facecolors='none', edgecolors='red', marker="*", s=100, label="Ideal Point (Approx)") plt.scatter(approx_nadir[0], approx_nadir[1], facecolors='none', edgecolors='black', marker="p", s=100, label="Nadir Point (Approx)") plt.title("Objective Space") plt.legend() plt.show() nF = (F - approx_ideal) / (approx_nadir - approx_ideal) fl = nF.min(axis=0) fu = nF.max(axis=0) print(f"Scale f1: [{fl[0]}, {fu[0]}]") print(f"Scale f2: [{fl[1]}, {fu[1]}]") plt.figure(figsize=(7, 5)) plt.scatter(nF[:, 0], nF[:, 1], s=30, facecolors='none', edgecolors='blue') plt.title("Objective Space") plt.show() weights = np.array([0.2, 0.8]) decomp = ASF() i = decomp.do(nF, 1/weights).argmin() print("Best regarding ASF: Point \ni = %s\nF = %s" % (i, F[i])) plt.figure(figsize=(7, 5)) plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue') plt.scatter(F[i, 0], F[i, 1], marker="x", color="red", s=200) plt.title("Objective Space") plt.show() i = PseudoWeights(weights).do(nF) print("Best regarding Pseudo Weights: Point \ni = %s\nF = %s" % (i, F[i])) plt.figure(figsize=(7, 5)) plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue') plt.scatter(F[i, 0], F[i, 1], marker="x", color="red", s=200) plt.title("Objective Space") plt.show() class MyTestProblem(MyProblem): def _calc_pareto_front(self, flatten=True, *args, **kwargs): f2 = lambda f1: ((f1/100) ** 0.5 - 1)**2 F1_a, F1_b = np.linspace(1, 16, 300), np.linspace(36, 81, 300) F2_a, F2_b = f2(F1_a), f2(F1_b) pf_a = np.column_stack([F1_a, F2_a]) pf_b = np.column_stack([F1_b, F2_b]) return stack(pf_a, pf_b, flatten=flatten) def _calc_pareto_set(self, *args, **kwargs): x1_a = np.linspace(0.1, 0.4, 50) x1_b = np.linspace(0.6, 0.9, 50) x2 = np.zeros(50) a, b = np.column_stack([x1_a, x2]), np.column_stack([x1_b, x2]) return stack(a,b, flatten=flatten) problem = MyTestProblem() pf_a, pf_b = problem.pareto_front(use_cache=False, flatten=False) pf = problem.pareto_front(use_cache=False, flatten=True) plt.figure(figsize=(7, 5)) plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='b', label="Solutions") plt.plot(pf_a[:, 0], pf_a[:, 1], alpha=0.5, linewidth=2.0, color="red", label="Pareto-front") plt.plot(pf_b[:, 0], pf_b[:, 1], alpha=0.5, linewidth=2.0, color="red") plt.title("Objective Space") plt.legend() plt.show() res = minimize(problem, algorithm, ("n_gen", 40), seed=1, save_history=True, verbose=False) X, F = res.opt.get("X", "F") hist = res.history print(len(hist)) n_evals = [] # corresponding number of function evaluations\ hist_F = [] # the objective space values in each generation hist_cv = [] # constraint violation in each generation hist_cv_avg = [] # average constraint violation in the whole population for algo in hist: # store the number of function evaluations n_evals.append(algo.evaluator.n_eval) # retrieve the optimum from the algorithm opt = algo.opt # store the least contraint violation and the average in each population hist_cv.append(opt.get("CV").min()) hist_cv_avg.append(algo.pop.get("CV").mean()) # filter out only the feasible and append and objective space values feas = np.where(opt.get("feasible"))[0] hist_F.append(opt.get("F")[feas]) k = np.where(np.array(hist_cv) <= 0.0)[0].min() print(f"At least one feasible solution in Generation {k} after {n_evals[k]} evaluations.") # replace this line by `hist_cv` if you like to analyze the least feasible optimal solution and not the population vals = hist_cv_avg k = np.where(np.array(vals) <= 0.0)[0].min() print(f"Whole population feasible in Generation {k} after {n_evals[k]} evaluations.") plt.figure(figsize=(7, 5)) plt.plot(n_evals, vals, color='black', lw=0.7, label="Avg. CV of Pop") plt.scatter(n_evals, vals, facecolor="none", edgecolor='black', marker="p") plt.axvline(n_evals[k], color="red", label="All Feasible", linestyle="--") plt.title("Convergence") plt.xlabel("Function Evaluations") plt.ylabel("Constraint Violation") plt.legend() plt.show() approx_ideal = F.min(axis=0) approx_nadir = F.max(axis=0) metric = Hypervolume(ref_point= np.array([1.1, 1.1]), norm_ref_point=False, zero_to_one=True, ideal=approx_ideal, nadir=approx_nadir) hv = [metric.do(_F) for _F in hist_F] plt.figure(figsize=(7, 5)) plt.plot(n_evals, hv, color='black', lw=0.7, label="Avg. CV of Pop") plt.scatter(n_evals, hv, facecolor="none", edgecolor='black', marker="p") plt.title("Convergence") plt.xlabel("Function Evaluations") plt.ylabel("Hypervolume") plt.show() running = RunningMetric(delta_gen=5, n_plots=3, only_if_n_plots=True, key_press=False, do_show=True) for algorithm in res.history[:15]: running.notify(algorithm) running = RunningMetric(delta_gen=10, n_plots=4, only_if_n_plots=True, key_press=False, do_show=True) for algorithm in res.history: running.notify(algorithm) metric = IGD(pf, zero_to_one=True) igd = [metric.do(_F) for _F in hist_F] plt.plot(n_evals, igd, color='black', lw=0.7, label="Avg. CV of Pop") plt.scatter(n_evals, igd, facecolor="none", edgecolor='black', marker="p") plt.axhline(10**-2, color="red", label="10^-2", linestyle="--") plt.title("Convergence") plt.xlabel("Function Evaluations") plt.ylabel("IGD") plt.yscale("log") plt.legend() plt.show() metric = IGDPlus(pf, zero_to_one=True) igd = [metric.do(_F) for _F in hist_F] plt.plot(n_evals, igd, color='black', lw=0.7, label="Avg. CV of Pop") plt.scatter(n_evals, igd, facecolor="none", edgecolor='black', marker="p") plt.axhline(10**-2, color="red", label="10^-2", linestyle="--") plt.title("Convergence") plt.xlabel("Function Evaluations") plt.ylabel("IGD+") plt.yscale("log") plt.legend() plt.show()