def _metric(self, data): ret = super()._metric(data) if not self.sliding_window: data = self.data[-self.metric_window_size:] # get necessary data from the current population current = data[-1] c_F, c_ideal, c_nadir = current["F"], current["ideal"], current[ "nadir"] # normalize all previous generations with respect to current ideal and nadir N = [normalize(e["F"], c_ideal, c_nadir) for e in data] # check if the movement of all points is significant if self.all_to_current: c_N = normalize(c_F, c_ideal, c_nadir) if self.perf_indicator == "igd": delta_f = [IGD(c_N).calc(N[k]) for k in range(len(N))] elif self.perf_indicator == "hv": # delta_f = [IGDPlus(c_N).calc(N[k]) for k in range(len(N))] hv = Hypervolume(ref_point=np.ones(c_F.shape[1])) delta_f = [hv.calc(N[k]) for k in range(len(N))] else: delta_f = [IGD(N[k + 1]).calc(N[k]) for k in range(len(N) - 1)] ret["delta_f"] = delta_f return ret
def __init__(self, min_igd, pf) -> None: super().__init__() if pf is None: raise Exception("You can only use IGD termination criteria if the pareto front is known!") self.obj = IGD(pf) self.igd = min_igd
def _calc_metric(self): # get the current and the last history snapshot current, last = self.history[-1], self.history[-2] # this is the range between the nadir and the ideal point norm = current["nadir"] - current["ideal"] # if the range is degenerated (very close to zero) - disable normalization by dividing by one norm[norm < 1e-32] = 1 # calculate the change from last to current in ideal and nadir point delta_ideal = self._calc_delta_norm(current["ideal"], last["ideal"], norm) max_delta_ideal = max([e["delta_ideal"] for e in self.metrics] + [delta_ideal]) delta_nadir = self._calc_delta_norm(current["nadir"], last["nadir"], norm) max_delta_nadir = max([e["delta_nadir"] for e in self.metrics] + [delta_nadir]) # get necessary data from the current population c_F, c_ideal, c_nadir = current["F"], current["ideal"], current[ "nadir"] c_N = normalize(c_F, c_ideal, c_nadir) if not self.renormalize: l_N = normalize(last["F"], c_ideal, c_nadir) delta_f = IGD(c_N).calc(l_N) max_delta_f = max([e["delta_f"] for e in self.metrics] + [delta_f]) else: # normalize all previous generations with respect to current ideal and nadir N = [normalize(e["F"], c_ideal, c_nadir) for e in self.history] # check if the movement of all points is significant if self.all_to_current: delta_f = [IGD(c_N).calc(N[k]) for k in range(len(N))] else: delta_f = [IGD(N[k + 1]).calc(N[k]) for k in range(len(N) - 1)] max_delta_f = np.array(delta_f).max() return { "delta_ideal": delta_ideal, "max_delta_ideal": max_delta_ideal, "delta_nadir": delta_nadir, "max_delta_nadir": max_delta_nadir, "delta_f": delta_f, "max_delta_f": max_delta_f, "max_delta_all": max(max_delta_ideal, max_delta_nadir, max_delta_f) }
def _metric(self, data): last, current = data[-2], data[-1] # this is the range between the nadir and the ideal point norm = current["nadir"] - current["ideal"] # if the range is degenerated (very close to zero) - disable normalization by dividing by one norm[norm < 1e-32] = 1 # calculate the change from last to current in ideal and nadir point delta_ideal = calc_delta_norm(current["ideal"], last["ideal"], norm) delta_nadir = calc_delta_norm(current["nadir"], last["nadir"], norm) # get necessary data from the current population c_F, c_ideal, c_nadir = current["F"], current["ideal"], current["nadir"] # normalize last and current with respect to most recent ideal and nadir c_N = normalize(c_F, c_ideal, c_nadir) l_N = normalize(last["F"], c_ideal, c_nadir) # calculate IGD from one to another delta_f = IGD(c_N).calc(l_N) return { "delta_ideal": delta_ideal, "delta_nadir": delta_nadir, "delta_f": delta_f }
def disp_multi_objective(problem, evaluator, algorithm, pf=None): attrs = [('n_gen', algorithm.n_gen, 5), ('n_eval', evaluator.n_eval, 7)] F, CV, feasible = algorithm.pop.get("F", "CV", "feasible") feasible = np.where(feasible[:, 0])[0] if isinstance(pf, bool): if pf: pf = pareto_front_if_possible(problem) else: pf = None if problem.n_constr > 0: attrs.append(disp_cv(CV)) if len(feasible) > 0: if pf is not None: attrs.append( ('igd', format_float(IGD(pf).calc(F[feasible])), width)) attrs.append(('gd', format_float(GD(pf).calc(F[feasible])), width)) if problem.n_obj == 2: attrs.append( ('hv', format_float(Hypervolume(pf=pf).calc(F[feasible])), width)) else: attrs.append(('igd', "-", width)) attrs.append(('gd', "-", width)) if problem.n_obj == 2: attrs.append(('hv', "-", width)) return attrs
def _decide(self): H = [normalize(e, x_min=self.xl, x_max=self.xu) for e in self.history] perf = np.full(self.n_last - 1, np.inf) for k in range(self.n_last - 1): current, last = H[k], H[k + 1] perf[k] = IGD(current).calc(last) return perf.std() > self.tol
class IGDTermination(Termination): def __init__(self, min_igd, pf) -> None: super().__init__() if pf is None: raise Exception("You can only use IGD termination criteria if the pareto front is known!") self.obj = IGD(pf) self.igd = min_igd def _do_continue(self, algorithm): F = algorithm.pop.get("F") return self.obj.calc(F) > self.igd
def _do(self, problem, evaluator, algorithm): super()._do(problem, evaluator, algorithm) F, CV, feasible = algorithm.pop.get("F", "CV", "feasible") feasible = np.where(feasible[:, 0])[0] if problem.n_constr > 0: self.output.append("cv (min)", CV.min()) self.output.append("cv (avg)", np.mean(CV)) if self.pareto_front_is_available: igd, gd, hv = "-", "-", "-" if len(feasible) > 0: _F = algorithm.opt.get("F") igd, gd = IGD(self.pf).calc(_F), GD(self.pf).calc(_F) if problem.n_obj == 2: hv = Hypervolume(pf=self.pf).calc(_F) self.output.extend(*[('igd', igd), ('gd', gd)]) if problem.n_obj == 2: self.output.append("hv", hv) else: self.output.append("n_nds", len(algorithm.opt), width=7) self.term.do_continue(algorithm) max_from, eps = "-", "-" if len(self.term.metrics) > 0: metric = self.term.metrics[-1] tol = self.term.tol delta_ideal, delta_nadir, delta_f = metric[ "delta_ideal"], metric["delta_nadir"], metric["delta_f"] if delta_ideal > tol: max_from = "ideal" eps = delta_ideal elif delta_nadir > tol: max_from = "nadir" eps = delta_nadir else: max_from = "f" eps = delta_f self.output.append("eps", eps) self.output.append("indicator", max_from)
def _do(self, problem, evaluator, algorithm): super()._do(problem, evaluator, algorithm) F, CV, feasible = algorithm.pop.get("F", "CV", "feasible") feasible = np.where(feasible[:, 0])[0] if problem.n_constr > 0: self.output.append("cv (min)", CV.min()) self.output.append("cv (avg)", np.mean(CV)) if self.pareto_front_is_available: igd, gd, hv = "-", "-", "-" if len(feasible) > 0: _F = algorithm.opt.get("F") igd, gd = IGD(self.pf).calc(_F), GD(self.pf).calc(_F) if problem.n_obj == 2: hv = Hypervolume(pf=self.pf).calc(_F) self.output.extend(*[('igd', igd), ('gd', gd)]) if problem.n_obj == 2: self.output.append("hv", hv) else: self.output.append("n_nds", len(algorithm.opt), width=7) self.term.do_continue(algorithm) delta_ideal, delta_nadir, delta_f, hist_delta_max = "-", "-", "-", "-" metric = self.term.metric() if metric is not None: delta_ideal = metric["delta_ideal"] delta_nadir = metric["delta_nadir"] delta_f = metric["delta_f"] hist_delta_max = metric["max_delta_all"] self.output.append("delta_ideal", delta_ideal) self.output.append("delta_nadir", delta_nadir) self.output.append("delta_f", delta_f) self.output.append("delta_max", max(delta_ideal, delta_nadir, delta_f)) self.output.append("hist_delta_max", hist_delta_max, width=13)
def _do(self, problem, evaluator, algorithm): super()._do(problem, evaluator, algorithm) F, CV, feasible = algorithm.pop.get("F", "CV", "feasible") feasible = np.where(feasible[:, 0])[0] if problem.n_constr > 0: self.output.append("cv (min)", CV.min()) self.output.append("cv (avg)", np.mean(CV)) if len(feasible) > 0: if self.pareto_front_is_available: _F = F[feasible] self.output.append("igd", IGD(self.pf).calc(_F)) self.output.append("gd", GD(self.pf).calc(_F)) if problem.n_obj == 2: self.output.append("hv", Hypervolume(pf=self.pf).calc(_F)) else: if self.pareto_front_is_available: self.output.extend(*[('igd', "-"), ('gd', "-")]) if problem.n_obj == 2: self.output.append("hv", "-")
def calc(self, F, others=None, calc_hv=True): """ This method calculates the R-IGD and R-HV based off of the values provided. Parameters ---------- F : numpy.ndarray The objective space values others : numpy.ndarray Results from other algorithms which should be used for filtering nds solutions calc_hv : bool Whether the hv is calculate - (None if more than 3 dimensions) Returns ------- rigd : float R-IGD rhv : float R-HV if calc_hv is true and less or equal to 3 dimensions """ self.F, self.others = F, others translated = [] final_PF = [] # 1. Prescreen Procedure - NDS Filtering pop = self._filter() pf = self.pf if pf is None: pf = self.problem.pareto_front() if pf is None: raise Exception( "Please provide the Pareto front to calculate the R-Metric!" ) labels = np.argmin(cdist(pop, self.ref_points), axis=1) for i in range(len(self.ref_points)): cluster = pop[np.where(labels == i)] if len(cluster) != 0: # 2. Representative Point Identification zp = self._preprocess( cluster, self.ref_points[i], w_point=self.w_points[i] )[0] # 3. Filtering Procedure - Filter points trimmed_data = self._trim(cluster, zp, range=self.delta) # 4. Solution Translation pop_t = self._translate( zp, trimmed_data, self.ref_points[i], w_point=self.w_points[i] ) translated.extend(pop_t) # 5. R-Metric Computation target = self._preprocess( data=pf, ref_point=self.ref_points[i], w_point=self.w_points[i] ) PF = self._trim(pf, target) final_PF.extend(PF) translated = np.array(translated) final_PF = np.array(final_PF) rigd, rhv = None, None if len(translated) > 0: # IGD Computation rigd = IGD(final_PF).calc(translated) nadir_point = np.amax(self.w_points, axis=0) front = translated dim = self.ref_points[0].shape[0] if calc_hv: if dim <= 3: try: rhv = Hypervolume(ref_point=nadir_point).calc(front) except: pass if calc_hv: return rigd, rhv else: return rigd
def _metric(self, data): last, current = data[-2], data[-1] return IGD(current).calc(last)
problems = [ (get_problem("wfg1", 6, n_obj, 4), WFG1(n_obj, 6, 4)), (get_problem("wfg2", 6, n_obj, 4), WFG2(n_obj, 6, 4)), (get_problem("wfg3", 6, n_obj, 4), WFG3(n_obj, 6, 4)), (get_problem("wfg4", 6, n_obj, 4), WFG4(n_obj, 6, 4)), (get_problem("wfg5", 6, n_obj, 4), WFG5(n_obj, 6, 4)), (get_problem("wfg6", 6, n_obj, 4), WFG6(n_obj, 6, 4)), (get_problem("wfg7", 6, n_obj, 4), WFG7(n_obj, 6, 4)), (get_problem("wfg8", 6, n_obj, 4), WFG8(n_obj, 6, 4)), (get_problem("wfg9", 6, n_obj, 4), WFG9(n_obj, 6, 4)), ] for my, other in problems: ps = other.get_optimal_solutions(2000) for e in ps: e.objective_values = other.objective_function(e.phenome) pf = np.array([e.objective_values for e in ps]) ps = np.array([e.phenome for e in ps]) _ps = my.pareto_set(n_pareto_points=3000) _pf = my.pareto_front(n_pareto_points=3000) name = my.__class__.__name__ Scatter(title=name).add(pf, s=15, color="green", alpha=0.5).add(_pf, color="red", s=10).show() print(name, IGD(pf).calc(_pf)) print()
plt.plot(n_evals, hv, '-o', markersize=4, linewidth=2) plt.title("Convergence") plt.xlabel("Function Evaluations") plt.ylabel("Hypervolume") plt.show() pf = problem.pareto_front(flatten=True, use_cache=False) import matplotlib.pyplot as plt from pymoo.performance_indicator.igd import IGD if pf is not None: # for this test problem no normalization for post prcessing is needed since similar scales normalize = False metric = IGD(pf=pf, normalize=normalize) # calculate for each generation the HV metric igd = [metric.calc(f) for f in F] # visualze the convergence curve plt.plot(n_evals, igd, '-o', markersize=4, linewidth=2, color="green") plt.yscale("log") # enable log scale if desired plt.title("Convergence") plt.xlabel("Function Evaluations") plt.ylabel("IGD") plt.show() from pymoo.util.running_metric import RunningMetric running = RunningMetric(delta_gen=5,
def calc(self, hyper_volume=True, delta=0.2, pf=None): """ This method calculates the R-IGD and R-HV based off of the population that was provided :return: R-IGD and R-HV """ translated = [] final_PF = [] # 1. Prescreen Procedure - NDS Filtering pop = self._filter() if pf is not None: solution = pf else: solution = self.problem.pareto_front() # solution = calc_PF(1, 10000, 2) labels = np.argmin(cdist(pop, self.ref_points), axis=1) for i in range(len(self.ref_points)): cluster = pop[np.where(labels == i)] if len(cluster) != 0: # 2. Representative Point Identification zp = self._preprocess(cluster, self.ref_points[i], w_point=self.w_points[i])[0] # 3. Filtering Procedure - Filter points trimmed_data = self._trim(cluster, zp, range=delta) # 4. Solution Translation pop_t = self._translate(zp, trimmed_data, self.ref_points[i], w_point=self.w_points[i]) translated.extend(pop_t) # 5. R-Metric Computation target = self._preprocess(data=solution, ref_point=self.ref_points[i], w_point=self.w_points[i]) PF = self._trim(solution, target) final_PF.extend(PF) translated = np.array(translated) if np.size(translated) == 0: igd = -1 volume = -1 else: # IGD Computation from pymoo.performance_indicator.igd import IGD IGD_ = IGD(final_PF) igd = IGD_.calc(translated) # HV Computation nadir_point = np.amax(self.w_points, axis=0) front = translated dim = self.ref_points[0].shape[0] if hyper_volume: if dim < 3: try: # Python from pymoo.performance_indicator.hv import HyperVolume hv = HyperVolume(nadir_point) volume = hv.compute(front) except TypeError: volume = -1 else: # cpp from pymoo.cpp.hypervolume.build import hypervolume volume = hypervolume.calculate(dim, len(front), front, nadir_point) else: volume = np.nan return igd, volume
def _calc_metric(self): last, current = self.history[0], self.history[1] if last is not None and current is not None: return IGD(current).calc(last)