Source code for opfunu.benchmark

#!/usr/bin/env python
# Created by "Thieu" at 16:47, 28/06/2022 ----------%                                                                               
#       Email: nguyenthieu2102@gmail.com            %                                                    
#       Github: https://github.com/thieu1995        %                         
# --------------------------------------------------%

import numpy as np
from opfunu.utils.visualize import draw_2d, draw_3d, draw_latex


[docs]class Benchmark: """ Defines an abstract class for optimization benchmark problem. All subclasses should implement the ``evaluate`` method for a particular optimization problem. Attributes ---------- bounds : list The lower/upper bounds of the problem. This a 2D-matrix of [lower, upper] array that contain the lower and upper bounds. By default, each problem has its own bounds. But user can try to put different bounds to test the problem. ndim : int The dimensionality of the problem. It is calculated from bounds lb : np.ndarray The lower bounds for the problem ub : np.ndarray The upper bounds for the problem f_global : float The global optimum of the evaluated function. x_global : np.ndarray A list of vectors that provide the locations of the global minimum. Note that some problems have multiple global minima, not all of which may be listed. n_fe : int The number of function evaluations that the object has been asked to calculate. dim_changeable : bool Whether we can change the benchmark function `x` variable length (i.e., the dimensionality of the problem) """ name = "Benchmark name" latex_formula = r'f(\mathbf{x})' latex_formula_dimension = r'd \in \mathbb{N}_{+}^{*}' latex_formula_bounds = r'x_i \in [-2\pi, 2\pi], \forall i \in \llbracket 1, d\rrbracket' latex_formula_global_optimum = r'f(0, ..., 0)=-1, \text{ for}, m=5, \beta=15' continuous = True linear = False convex = True unimodal = False separable = False differentiable = True scalable = True randomized_term = False parametric = True modality = True # Number of ambiguous peaks, unknown # peaks # n_basins = 1 # n_valleys = 1 def __init__(self): self._bounds = None self._ndim = None self.dim_changeable = False self.dim_default = 2 self.dim_supported = [] self.f_global = None self.x_global = None self.n_fe = 0 self.paras = {} self.epsilon = 1e-8
[docs] def check_ndim_and_bounds(self, ndim=None, bounds=None, default_bounds=None): """ Check the bounds when initializing the object. Parameters ---------- ndim : int The number of dimensions (variables) bounds : list, tuple, np.ndarray List of lower bound and upper bound, should use default None value default_bounds : np.ndarray List of initial lower bound and upper bound values """ if ndim is None: self._bounds = default_bounds if bounds is None else np.array(bounds).T self._ndim = self._bounds.shape[0] else: if bounds is None: if self.dim_changeable: if type(ndim) is int and ndim > 1: self._ndim = int(ndim) self._bounds = np.array([default_bounds[0] for _ in range(self._ndim)]) else: raise ValueError('ndim must be an integer and > 1!') else: self._ndim = self.dim_default self._bounds = default_bounds print(f"{self.__class__.__name__} is fixed problem with {self.dim_default} variables!") else: if self.dim_changeable: self._bounds = np.array(bounds).T self._ndim = self._bounds.shape[0] print(f"{self.__class__.__name__} problem is set with {self._ndim} variables!") else: self._bounds = np.array(bounds).T if self._bounds.shape[0] != self.dim_default: raise ValueError(f"{self.__class__.__name__} is fixed problem with {self._ndim} variables. Please setup the correct bounds!") else: self._ndim = self.dim_default
[docs] def check_solution(self, x): """ Raise the error if the problem size is not equal to the solution length Parameters ---------- x : np.ndarray The solution """ if not self.dim_changeable and (len(x) != self._ndim): raise ValueError(f"The length of solution should have {self._ndim} variables!")
[docs] def get_paras(self): """ Return the parameters of the problem. Depended on function """ default = {"bounds": self._bounds, "ndim": self._ndim, } return {**default, **self.paras}
[docs] def evaluate(self, x): """ Evaluation of the benchmark function. Parameters ---------- x : np.ndarray The candidate vector for evaluating the benchmark problem. Must have ``len(x) == self.ndim``. Returns ------- val : float the evaluated benchmark function """ raise NotImplementedError
[docs] def is_ndim_compatible(self, ndim): """ Method to support searching the functions with input ndim Parameters ---------- ndim : int The number of dimensions Returns ------- val: bool Always true if dim_changeable = True, Else return ndim == self.ndim """ assert (ndim is None) or (isinstance(ndim, int) and (not ndim < 0)), "The dimension ndim must be None or a positive integer" if ndim is None: return True else: if self.dim_changeable: return ndim > 0 else: return ndim == self.ndim
[docs] def is_succeed(self, x, tol=1.e-5): """ Check if a candidate solution at the global minimum. Parameters ---------- x : np.ndarray The candidate vector for testing if the global minimum has been reached. Must have ``len(x) == self.ndim`` tol : float The evaluated function and known global minimum must differ by less than this amount to be at a global minimum. Returns ------- is_succeed : bool Answer the question: is the candidate vector at the global minimum? """ # the solution should still be in bounds, otherwise immediate fail. if np.any(x > self.ub) or np.any(x < self.lb): return False val = self.evaluate(np.squeeze(x)) if np.abs(val - self.f_global) < tol: return True # you found a lower global minimum. This shouldn't happen. if val < self.f_global: raise ValueError("Found a lower global minimum", x, val, self.f_global) return False
@property def bounds(self): """ The lower/upper bounds to be used for optimization problem. This a 2D-matrix of [lower, upper] array that contain the lower and upper bounds for the problem. The problem should not be asked for evaluation outside these bounds. ``len(bounds) == ndim``. """ return self._bounds @property def ndim(self): """ The dimensionality of the problem. Returns ------- ndim : int The dimensionality of the problem """ return self._ndim @property def lb(self): """ The lower bounds for the problem Returns ------- lb : 1D-vector The lower bounds for the problem """ return np.array([x[0] for x in self.bounds]) @property def ub(self): """ The upper bounds for the problem Returns ------- ub : 1D-vector The upper bounds for the problem """ return np.array([x[1] for x in self.bounds])
[docs] def create_solution(self) -> np.ndarray: """ Create a random solution for the current problem Returns ------- solution: 1D-vector The random solution """ return np.random.uniform(self.lb, self.ub)
[docs] def plot_latex(self, latex, title="Latex equation", figsize=(8, 3), dpi=500, filename=None, exts=(".png", ".pdf"), verbose=True): """ Draw latex equation. Parameters ---------- latex : equation Your latex equation, you can test on the website: https://latex.codecogs.com/ title : str Title for the figure figsize : tuple, default=(10, 8) The figure size with format of tuple (width, height) dpi : int, default=500 The dot per inches (DPI) - indicate the number of dots per inch. filename : str, default = None Set the file name, If None, the file will not be saved exts : list, tuple, np.ndarray The list of extensions to save file, for example: (".png", ".pdf", ".jpg") verbose : bool Show the figure or not. It will not show on linux system. """ draw_latex(latex, title=title, figsize=figsize, dpi=dpi, filename=filename, exts=exts, verbose=verbose)
[docs] def plot_2d(self, selected_dims=None, n_points=500, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, fixed_strategy="mean", fixed_values=None, title="Contour map of the function", x_label=None, y_label=None, figsize=(10, 8), filename=None, exts=(".png", ".pdf"), verbose=True): """ Draw 2D contour of the function. Parameters ---------- selected_dims : list, tuple, np.ndarray The selected dimensions you want to draw. If your function has only 2 dimensions, it will select (1, 2) automatically. n_points : int The number of points that will be used to draw the contour ct_cmap : str The cmap of matplotlib.pyplot.contourf function (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html) ct_levels : int The levels parameter of contourf function ct_alpha : float The alpha parameter of contourf function fixed_strategy : str The selected strategy to set values for other dimensions. When your function has > 2 dimensions, you need to set a fixed value for other dimensions to be able to calculate value. List of available strategy: ["min", "max", "mean', "values", "zero"] + min: Set the other dimensions by its lower bound + max: Set the other dimensions by its upper bound + mean: Set the other dimensions by it average value (lower bound + upper bound) / 2 + zero: Set the other dimensions by 0 + values: Set the other dimensions by your passed values through the parameter: `fixed_values`. fixed_values : list, tuple, np.ndarray Fixed values for all dimensions (length should be the same as lower bound), the selected dimensions will be replaced in the drawing process. title : str Title for the figure x_label : str Set the x label y_label : str Set the y label figsize : tuple, default=(10, 8) The figure size with format of tuple (width, height) filename : str, default = None Set the file name, If None, the file will not be saved exts : list, tuple, np.ndarray The list of extensions to save file, for example: (".png", ".pdf", ".jpg") verbose : bool Show the figure or not. It will not show on linux system. """ draw_2d(self.evaluate, self.lb, self.ub, selected_dims=selected_dims, n_points=n_points, ct_cmap=ct_cmap, ct_levels=ct_levels, ct_alpha=ct_alpha, fixed_strategy=fixed_strategy, fixed_values=fixed_values, title=title, x_label=x_label, y_label=y_label, figsize=figsize, filename=filename, exts=exts, verbose=verbose)
[docs] def plot_3d(self, selected_dims=None, n_points=500, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, fixed_strategy="mean", fixed_values=None, title="3D visualization of the function", x_label=None, y_label=None, figsize=(10, 8), filename=None, exts=(".png", ".pdf"), verbose=True): """ Draw 3D of the function. Parameters ---------- selected_dims : list, tuple, np.ndarray The selected dimensions you want to draw. If your function has only 2 dimensions, it will select (1, 2) automatically. n_points : int The number of points that will be used to draw the contour ct_cmap : str The cmap of matplotlib.pyplot.contourf function (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html) ct_levels : int The levels parameter of contourf function ct_alpha : float The alpha parameter of contourf function fixed_strategy : str The selected strategy to set values for other dimensions. When your function has > 2 dimensions, you need to set a fixed value for other dimensions to be able to calculate value. List of available strategy: ["min", "max", "mean', "values", "zero"] + min: Set the other dimensions by its lower bound + max: Set the other dimensions by its upper bound + mean: Set the other dimensions by it average value (lower bound + upper bound) / 2 + zero: Set the other dimensions by 0 + values: Set the other dimensions by your passed values through the parameter: `fixed_values`. fixed_values : list, tuple, np.ndarray Fixed values for all dimensions (length should be the same as lower bound), the selected dimensions will be replaced in the drawing process. title : str Title for the figure x_label : str Set the x label y_label : str Set the y label figsize : tuple, default=(10, 8) The figure size with format of tuple (width, height) filename : str, default = None Set the file name, If None, the file will not be saved exts : list, tuple, np.ndarray The list of extensions to save file, for example: (".png", ".pdf", ".jpg") verbose : bool Show the figure or not. It will not show on linux system. """ draw_3d(self.evaluate, self.lb, self.ub, selected_dims=selected_dims, n_points=n_points, ct_cmap=ct_cmap, ct_levels=ct_levels, ct_alpha=ct_alpha, fixed_strategy=fixed_strategy, fixed_values=fixed_values, title=title, x_label=x_label, y_label=y_label, figsize=figsize, filename=filename, exts=exts, verbose=verbose)