Source code for iwopy.interfaces.pymoo.optimizer

import numpy as np
import matplotlib.pyplot as plt

from iwopy.core import Optimizer
from .problem import SingleObjProblemTemplate, MultiObjProblemTemplate
from .factory import Factory
from . import imports

[docs]class DefaultCallbackTemplate: """ Template for the default callback :group: interfaces.pymoo """
[docs] def __init__(self): """ Constructor """ self.data["f_best"] = None self.data["cv_best"] = None
[docs] def notify(self, algorithm): fvals = algorithm.pop.get("F") cvals = algorithm.pop.get("CV") n_obj = fvals.shape[1] n_con = cvals.shape[1] i = np.argmin(fvals, axis=0) if self.data["f_best"] is None: self.data["f_best"] = fvals[None, i, range(n_obj)] self.data["cv_best"] = cvals[None, i, range(n_con)] else: self.data["f_best"] = np.append( self.data["f_best"], fvals[None, i, range(n_obj)], axis=0 ) self.data["cv_best"] = np.append( self.data["cv_best"], cvals[None, i, range(n_con)], axis=0 )
[docs] @classmethod def get_class(cls): """ Creates the class, dynamically derived from pymoo.Callback """ imports.load() attrb = {v: d for v, d in cls.__dict__.items()} init0 = cls.__init__ def init1(self): imports.Callback.__init__(self) init0(self) attrb["__init__"] = init1 attrb["__doc__"] = "The default callback" del attrb["get_class"] return type("DefaultCallback", (imports.Callback,), attrb)()
[docs]class Optimizer_pymoo(Optimizer): """ Interface to the pymoo optimization solver. Attributes ---------- problem_pars: dict Parameters for the problem algo_pars: dict Parameters for the alorithm setup_pars: dict Parameters for the calculation setup term_pars: dict Parameters for the termination conditions pymoo_problem: iwopy.interfaces.pymoo.SingleObjProblem The pygmo problem algo: pygmo.algo The pygmo algorithm :group: interfaces.pymoo """
[docs] def __init__(self, problem, problem_pars, algo_pars, setup_pars={}, term_pars={}): """ Constructor Parameters ---------- problem: iwopy.Problem The problem to optimize problem_pars: dict Parameters for the problem algo_pars: dict Parameters for the alorithm setup_pars: dict Parameters for the calculation setup """ super().__init__(problem) self.problem_pars = problem_pars self.algo_pars = algo_pars self.setup_pars = setup_pars self.term_pars = term_pars self.pymoo_problem = None self.algo = None
[docs] def print_info(self): """ Print solver info, called before solving """ super().print_info() if len(self.problem_pars): print("\nProblem:") print("--------") for k, v in self.problem_pars.items(): if isinstance(v, int) or isinstance(v, float) or isinstance(v, str): print(f" {k}: {v}") if len(self.algo_pars): print("\nAlgorithm:") print("----------") for k, v in self.algo_pars.items(): if isinstance(v, int) or isinstance(v, float) or isinstance(v, str): print(f" {k}: {v}") if len(self.setup_pars): print("\nSetup:") print("------") for k, v in self.setup_pars.items(): if isinstance(v, int) or isinstance(v, float) or isinstance(v, str): print(f" {k}: {v}") if len(self.term_pars): print("\nTermination:") print("------------") if isinstance(self.term_pars, tuple): print(f" {self.term_pars[0]}: {self.term_pars[1]}") else: for k, v in self.term_pars.items(): if isinstance(v, int) or isinstance(v, float) or isinstance(v, str): print(f" {k}: {v}") print()
[docs] def initialize(self, verbosity=1): """ Initialize the object. Parameters ---------- verbosity: int The verbosity level, 0 = silent """ if self.problem.n_objectives <= 1: self.pymoo_problem = SingleObjProblemTemplate.get_class()( self.problem, **self.problem_pars ) else: self.pymoo_problem = MultiObjProblemTemplate.get_class()( self.problem, **self.problem_pars ) if verbosity: print("Initializing", type(self).__name__) factory = Factory(self.pymoo_problem, verbosity) self.algo = factory.get_algorithm(self.algo_pars) self.term = factory.get_termination(self.term_pars) super().initialize(verbosity)
[docs] def solve(self, callback="default", verbosity=1): """ Run the optimization solver. Parameters ---------- callback: pymoo.Callback, optional The callback verbosity: int The verbosity level, 0 = silent Returns ------- results: iwopy.SingleObjOptResults or iwopy.MultiObjOptResults The optimization results object """ if callback == "default": callback = DefaultCallbackTemplate.get_class() # check problem initialization: super().solve() # run pymoo solver: self.callback = callback self.results = imports.minimize( self.pymoo_problem, algorithm=self.algo, termination=self.term, verbose=verbosity > 0, callback=self.callback, **self.setup_pars, ) # transfer callback: if self.callback is not None: self.callback = self.results.algorithm.callback return self.pymoo_problem.finalize(self.results)
[docs] def get_figure_f(self, fig=None, ax=None, valid_dict=None, **kwargs): """ Create a figure that shows the objective function development during optimization. The kwargs are forwarded to the plot command. Parameters ---------- fig: plt.Figure, optional The figure to which to add the plot ax: plt.Axis, optional The axis to which to add the plot valid_dict: dict, optional Settings for the point of first valid solution, forwarded to scatter Returns ------- fig: plt.Figure The figure """ if self.problem.n_objectives() == 1: if fig is None and ax is None: fig, ax = plt.subplots() elif ax is None: ax = fig.add_subplot(111) else: raise TypeError(f"Impossible fig/ax input") fname = self.problem.objs[0].base_name fvals = self.callback.data["f_best"] gens = range(len(fvals)) ax.plot(gens, fvals, label=fname, **kwargs) if self.problem.n_constraints(): cv = np.array(self.callback.data["cv_best"]) sel = cv == 0.0 if np.any(sel): i = np.argwhere(sel)[0][0] vdict = {"label": "first valid", "color": "red"} if valid_dict is not None: vdict.update(valid_dict) ax.scatter(i, fvals[i], **vdict) ax.legend() ax.set_xlabel("n_gen") ax.set_ylabel(fname) plt.tight_layout() return fig else: raise NotImplementedError