Source code for mealpy.utils.problem

# !/usr/bin/env python
# Created by "Thieu" at 17:28, 13/10/2021 ----------%
#       Email: nguyenthieu2102@gmail.com            %
#       Github: https://github.com/thieu1995        %
# --------------------------------------------------%

import numpy as np


[docs]class Problem: """ Define the mathematical form of optimization problem Notes ~~~~~ + fit_func (callable): your fitness function + lb (list, int, float): lower bound, should be list of values + ub (list, int, float): upper bound, should be list of values + minmax (str): "min" or "max" problem (Optional, default = "min") + verbose (bool): print out the training process or not (Optional, default = True) + n_dims (int): number of dimensions / problem size (Optional) + obj_weight: list weights for all your objectives (Optional, default = [1, 1, ...1]) + problem (dict): dictionary of the problem (contains at least the parameter 1, 2, 3) (Optional) + amend_position(callable): Depend on your problem, may need to design an amend_position function (Optional for continuous domain, Required for discrete domain) Examples ~~~~~~~~ >>> ## 1st way: >>> import numpy as np >>> from mealpy.swarm_based.PSO import BasePSO >>> >>> def fitness_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "fit_func": fitness_function, >>> "lb": [-10, -15, -4, -2, -8], >>> "ub": [10, 15, 12, 8, 20], >>> "minmax": "min", >>> "verbose": True, >>> } >>> model1 = BasePSO(problem_dict, epoch=1000, pop_size=50) >>> >>> ## 2nd way: >>> from mealpy.utils.problem import Problem >>> >>> problem_obj2 = Problem(fit_func=fitness_function, lb=[-10, -15, -4, -2, -8], ub=[10, 15, 12, 8, 20], minmax="min", verbose=True) >>> model3 = BasePSO(problem_obj2, epoch=1000, pop_size=50) """ ID_MIN_PROB = 0 # min problem ID_MAX_PROB = -1 # max problem DEFAULT_LB = -1 DEFAULT_UB = 1 def __init__(self, **kwargs): self.minmax = "min" self.verbose = True self.n_objs = 1 self.obj_weight = None self.multi_objs = False self.obj_is_list = False self.n_dims, self.lb, self.ub = None, None, None self.__set_keyword_arguments(kwargs) self.__check_problem(kwargs) def __set_keyword_arguments(self, kwargs): for key, value in kwargs.items(): setattr(self, key, value) def __check_problem(self, kwargs): if ("fit_func" not in kwargs) and ("lb" not in kwargs) and ("ub" not in kwargs): if "problem" not in kwargs: print("You need to set up the problem dictionary with at least 'fit_func', 'lb', and 'ub'.") exit(0) else: problem = kwargs["problem"] if type(problem) is not dict: print("You need to set up the problem dictionary with at least 'fit_func', 'lb', and 'ub'.") exit(0) else: if ("fit_func" not in problem) or ("lb" not in problem) or ("ub" not in problem): print("You need to set up the problem dictionary with at least 'fit_func', 'lb', and 'ub'.") exit(0) else: lb, ub, fit_func = problem["lb"], problem["ub"], problem["fit_func"] self.__set_problem(lb, ub, fit_func, problem) elif ("fit_func" in kwargs) and ("lb" in kwargs) and ("ub" in kwargs): lb, ub, fit_func = kwargs["lb"], kwargs["ub"], kwargs["fit_func"] self.__set_problem(lb, ub, fit_func, kwargs) else: print("You need to set up the problem dictionary with at least 'fit_func', 'lb', and 'ub'.") exit(0) def __set_problem(self, lb, ub, fit_func, kwargs): self.__set_domain_range(lb, ub, kwargs) self.__set_fitness_function(fit_func, kwargs) def __set_domain_range(self, lb, ub, kwargs): if (lb is None) or (ub is None): if "n_dims" in kwargs: self.n_dims = self.__check_problem_size(kwargs["n_dims"]) self.lb = self.DEFAULT_LB * np.ones(self.n_dims) self.ub = self.DEFAULT_UB * np.ones(self.n_dims) print(f"Default lb={self.lb}, ub={self.ub}.") else: print("If lb and ub are undefined, then you must set 'n_dims' to be an integer.") exit(0) else: if isinstance(lb, list) and isinstance(ub, list): if len(lb) == len(ub): if len(lb) == 0: if "n_dims" in kwargs: self.n_dims = self.__check_problem_size(kwargs["n_dims"]) self.lb = self.DEFAULT_LB * np.ones(self.n_dims) self.ub = self.DEFAULT_UB * np.ones(self.n_dims) print(f"Default lb={self.lb}, ub={self.ub}.") else: print("Please set up your lower bound and upper bound.") exit(0) elif len(lb) == 1: if "n_dims" in kwargs: self.n_dims = self.__check_problem_size(kwargs["n_dims"]) self.lb = lb[0] * np.ones(self.n_dims) self.ub = ub[0] * np.ones(self.n_dims) else: self.n_dims = len(lb) self.lb = np.array(lb) self.ub = np.array(ub) else: print("Lower bound and Upper bound need to be same length") exit(0) elif type(lb) in [int, float] and type(ub) in [int, float]: self.n_dims = self.__check_problem_size(kwargs["n_dims"]) self.lb = lb * np.ones(self.n_dims) self.ub = ub * np.ones(self.n_dims) else: print("Lower bound and Upper bound need to be a list.") exit(0) def __check_problem_size(self, n_dims): if n_dims is None or n_dims <= 0: print("Problem size must be an int number and > 0.") exit(0) return int(np.ceil(n_dims)) def __set_fitness_function(self, fit_func, kwargs): if callable(fit_func): self.fit_func = fit_func else: print("Please check your fitness function. It needs to return single value or a list of values!") exit(0) tested_solution = np.random.uniform(self.lb, self.ub) if "amend_position" in kwargs: tested_solution = self.amend_position(tested_solution) result = None try: result = self.fit_func(tested_solution) except Exception as err: print(f"Error: {err}\n") print("Please check your fitness function. It needs to return single value or a list of values!") exit(0) if isinstance(result, list) or isinstance(result, np.ndarray): self.n_objs = len(result) self.obj_is_list = True if self.n_objs > 1: self.multi_objs = True if "obj_weight" in kwargs: self.obj_weight = kwargs["obj_weight"] if isinstance(self.obj_weight, list) or isinstance(self.obj_weight, np.ndarray): if self.n_objs != len(self.obj_weight): print(f"Please check your fitness function/weight. N objs = {self.n_objs}, but N weights = {len(self.obj_weight)}") exit(0) print(f"N objs = {self.n_objs} with weights = {self.obj_weight}") else: print( f"Please check your fitness function/weight. N objs = {self.n_objs}, weights must be a list or numpy np.array with same length.") exit(0) else: self.obj_weight = np.ones(self.n_objs) print(f"N objs = {self.n_objs} with default weights = {self.obj_weight}") elif self.n_objs == 1: self.multi_objs = False self.obj_weight = np.ones(1) print(f"N objs = {self.n_objs} with default weights = {self.obj_weight}") else: print(f"Please check your fitness function. It returns nothing!") exit(0) else: if type(result) in (int, float) or isinstance(result, np.floating) or isinstance(result, np.integer): self.multi_objs = False self.obj_is_list = False self.obj_weight = np.ones(1) else: print("Please check your fitness function. It needs to return single value or a list of values!") exit(0)
[docs] def amend_position(self, position=None): """ Depend on what kind of problem are we trying to solve, there will be an different amend_position function to rebound the position of agent into the valid range. Args: position: vector position (location) of the solution. Returns: Amended position (make the position is in bound) """ # return np.maximum(self.problem.lb, np.minimum(self.problem.ub, position)) return np.clip(position, self.lb, self.ub)