Source code for iwopy.interfaces.pymoo.problem

import numpy as np

from iwopy.core import SingleObjOptResults, MultiObjOptResults
from . import imports


[docs]class SingleObjProblemTemplate: """ Template for a wrapper around the pymoo problem for a single objective. At the moment this interface only supports pure int or pure float problems (not mixed). Attributes ---------- problem: iwopy.core.Problem The iwopy problem to solve vectorize: bool Switch for vectorized calculations, wrt population individuals is_mixed: bool Flag for mixed integer/float problems is_intprob: bool Flag for integer problems :group: interfaces.pymoo """
[docs] def __init__(self, problem, vectorize): """ Constructor Parameters ---------- problem: iwopy.core.Problem The iwopy problem to solve vectorize: bool, optional Switch for vectorized calculations, wrt population individuals """ self.problem = problem self.vectorize = vectorize if self.problem.n_vars_float > 0 and self.problem.n_vars_int == 0: self.is_mixed = False self.is_intprob = False self._pargs = dict( n_var=self.problem.n_vars_float, n_obj=self.problem.n_objectives, n_ieq_constr=self.problem.n_constraints, xl=self.problem.min_values_float(), xu=self.problem.max_values_float(), elementwise=not vectorize, type_var=np.float64, ) elif self.problem.n_vars_float == 0 and self.problem.n_vars_int > 0: self.is_mixed = False self.is_intprob = True self._pargs = dict( n_var=self.problem.n_vars_int, n_obj=self.problem.n_objectives, n_ieq_constr=self.problem.n_constraints, xl=self.problem.min_values_int(), xu=self.problem.max_values_int(), elementwise=not vectorize, type_var=np.int32, ) else: self.is_mixed = True self.is_intprob = False vars = {} nami = self.problem.var_names_int() inii = self.problem.initial_values_int() mini = self.problem.min_values_int() maxi = self.problem.max_values_int() for i, v in enumerate(nami): vars[v] = imports.Integer(value=inii[i], bounds=(mini[i], maxi[i])) namf = self.problem.var_names_float() inif = self.problem.initial_values_float() minf = self.problem.min_values_float() maxf = self.problem.max_values_float() for i, v in enumerate(namf): vars[v] = imports.Real(value=inif[i], bounds=(minf[i], maxf[i])) self._pargs = dict( vars=vars, n_obj=self.problem.n_objectives, n_ieq_constr=self.problem.n_constraints, elementwise=not vectorize, ) if self.problem.n_constraints: self._cmi = self.problem.min_values_constraints self._cma = self.problem.max_values_constraints cnames = self.problem.cons.component_names sel = np.isinf(self._cmi) & np.isinf(self._cma) if np.any(sel): raise RuntimeError( f"Missing boundaries for constraints {cnames[sel]}" ) sel = (~np.isinf(self._cmi)) & (~np.isinf(self._cma)) if np.any(sel): raise RuntimeError( f"Constraints {cnames[sel]} have both lower and upper bounds" )
def _evaluate(self, x, out, *args, **kwargs): """ Overloading the abstract evaluation function of the pymoo base class. """ # vectorized run: if self.vectorize: if self.is_mixed: xi = np.array( [[dct[v] for v in self.problem.var_names_int()] for dct in x], dtype=np.int32, ) xf = np.array( [[dct[v] for v in self.problem.var_names_float()] for dct in x], dtype=np.float64, ) out["F"], out["G"] = self.problem.evaluate_population(xi, xf) out["F"] *= np.where(self.problem.maximize_objs, -1.0, 1.0)[None, :] else: n_pop = x.shape[0] if self.is_intprob: dummies = np.zeros((n_pop, 0), dtype=np.float64) out["F"], out["G"] = self.problem.evaluate_population( x, dummies ) out["F"] *= np.where(self.problem.maximize_objs, -1.0, 1.0)[ None, : ] else: dummies = np.zeros((n_pop, 0), dtype=np.int32) out["F"], out["G"] = self.problem.evaluate_population( dummies, x ) out["F"] *= np.where(self.problem.maximize_objs, -1.0, 1.0)[ None, : ] if self.problem.n_constraints: sel = ~np.isinf(self._cma) out["G"][:, sel] = out["G"][:, sel] - self._cma[None, sel] sel = ~np.isinf(self._cmi) out["G"][:, sel] = self._cmi[None, sel] - out["G"][:, sel] # individual run: else: if self.is_mixed: xi = np.array( [x[v] for v in self.problem.var_names_int()], dtype=np.int32 ) xf = np.array( [x[v] for v in self.problem.var_names_float()], dtype=np.float64 ) out["F"], out["G"] = self.problem.evaluate_individual(xi, xf) out["F"] *= np.where(self.problem.maximize_objs, -1.0, 1.0) else: n_pop = x.shape[0] if self.is_intprob: dummies = np.zeros(0, dtype=np.float64) out["F"], out["G"] = self.problem.evaluate_individual( x, dummies ) out["F"] *= np.where(self.problem.maximize_objs, -1.0, 1.0) else: dummies = np.zeros(0, dtype=np.int32) out["F"], out["G"] = self.problem.evaluate_individual( dummies, x ) out["F"] *= np.where(self.problem.maximize_objs, -1.0, 1.0) if self.problem.n_constraints: sel = ~np.isinf(self._cma) out["G"][sel] = out["G"][sel] - self._cma[sel] sel = ~np.isinf(self._cmi) out["G"][sel] = self._cmi[sel] - out["G"][sel]
[docs] def finalize(self, pymoo_results, verbosity=1): """ Finalize the problem. Parameters ---------- pymoo_results: pymoo.Results The results from the solver verbosity: int The verbosity level, 0 = silent Returns ------- results: iwopy.SingleObjOptResults The optimization results object """ # prepare: r = pymoo_results suc = True # case no solution from pymoo: if r.X is None: suc = False xi = None xf = None res = None objs = None cons = None # evaluate pymoo final solution: else: if self.is_mixed: xi = np.array( [r.X[v] for v in self.problem.var_names_int()], dtype=np.int32 ) xf = np.array( [r.X[v] for v in self.problem.var_names_float()], dtype=np.float64, ) else: if self.is_intprob: xi = r.X xf = np.zeros(0, dtype=np.float64) else: xi = np.zeros(0, dtype=np.int32) xf = np.array(r.X, dtype=np.float64) if self.vectorize: if self.is_mixed: pxi = np.array( [ [p.X[v] for v in self.problem.var_names_int()] for p in r.pop ], dtype=np.int32, ) pxf = np.array( [ [p.X[v] for v in self.problem.var_names_float()] for p in r.pop ], dtype=np.float64, ) self.problem.finalize_population(pxi, pxf, verbosity) del pxi, pxf else: n_pop = len(r.pop) n_vars = len(r.X) vars = np.zeros((n_pop, n_vars), dtype=np.float64) for pi, p in enumerate(r.pop): vars[pi] = p.X if self.is_intprob: dummies = np.zeros((n_pop, 0), dtype=np.int32) self.problem.finalize_population( vars.astype(np.int32), dummies, verbosity ) else: dummies = np.zeros((n_pop, 0), dtype=np.float64) self.problem.finalize_population(dummies, vars, verbosity) res, objs, cons = self.problem.finalize_individual(xi, xf, verbosity) if verbosity: print() suc = np.all(self.problem.check_constraints_individual(cons, False)) if verbosity: print() return SingleObjOptResults(self.problem, suc, xi, xf, objs, cons, res)
[docs] @classmethod def get_class(cls): """ Creates the class, dynamically derived from pymoo.Problem """ imports.load() attrb = {v: d for v, d in cls.__dict__.items()} init0 = cls.__init__ def init1(self, *args, **kwargs): init0(self, *args, **kwargs) imports.Problem.__init__(self, **self._pargs) attrb["__init__"] = init1 attrb["__doc__"] = attrb["__doc__"].replace("Template for a w", "W") del attrb["get_class"] return type("SingleObjProblem", (imports.Problem,), attrb)
[docs]class MultiObjProblemTemplate: """ Template for a wrapper around the pymoo problem for a multiple objectives problem. At the moment this interface only supports pure int or pure float problems (not mixed). Attributes ---------- problem: iwopy.core.Problem The iwopy problem to solve vectorize: bool Switch for vectorized calculations, wrt population individuals is_intprob: bool Flag for integer problems :group: interfaces.pymoo """
[docs] def __init__(self, problem, vectorize): """ Constructor template, will be overwritten by get_class Parameters ---------- problem: iwopy.core.Problem The iwopy problem to solve vectorize: bool, optional Switch for vectorized calculations, wrt population individuals """ pass
[docs] def finalize(self, pymoo_results, verbosity=1): """ Finalize the problem. Parameters ---------- pymoo_results: pymoo.Results The results from the solver verbosity: int The verbosity level, 0 = silent Returns ------- results: iwopy.SingleObjOptResults The optimization results object """ # prepare: r = pymoo_results suc = True # case no solution from pymoo: if r.X is None: suc = False xi = None xf = None res = None objs = None cons = None # evaluate pymoo final solution: else: n_pop = len(r.pop) if self.is_intprob: xi = r.X xf = np.zeros((n_pop, 0), dtype=np.float64) else: xi = np.zeros((n_pop, 0), dtype=int) xf = r.X res, objs, cons = self.problem.finalize_population(xi, xf, verbosity) if verbosity: print() suc = np.all( self.problem.check_constraints_population(cons, False), axis=1 ) if verbosity: print() return MultiObjOptResults(self.problem, suc, xi, xf, objs, cons, res)
[docs] @classmethod def get_class(cls): """ Creates the class, dynamically derived from SingleObjProblem """ scls = SingleObjProblemTemplate.get_class() attrb = {v: d for v, d in cls.__dict__.items()} def init(self, *args, **kwargs): scls.__init__(self, *args, **kwargs) attrb["__init__"] = init attrb["__doc__"] = attrb["__doc__"].replace("Template for a w", "W") del attrb["get_class"] return type("MultiObjProblem", (scls,), attrb)