Source code for EDAspy.optimization.multivariate.speda

#!/usr/bin/env python
# coding: utf-8

from ..eda import EDA
from ..custom.probabilistic_models import SPBN
from ..custom.initialization_models import UniformGenInit

import numpy as np
from typing import Union, List


[docs]class SPEDA(EDA): """ Semiparametric Estimation of Distribution Algorithm [1]. This type of Estimation-of-Distribution Algorithm uses a semiparametric Bayesian network [2] which allows dependencies between variables which have been estimated using KDE with variables which fits a Gaussian distribution. By this way, it avoid the assumption of Gaussianity in the variables of the optimization problem. This multivariate probabilistic model is updated in each iteration with the best individuals of the previous generations. SPEDA has shown to improve the results for more complex optimization problem compared to the univariate EDAs that can be found implemented in this package, multivariate EDAs such as EGNA, or EMNA, and other population-based algorithms. See [1] for numerical results. Example: This example uses some very well-known benchmarks from CEC14 conference to be solved using a Semiparametric Estimation of Distribution Algorithm (SPEDA). .. code-block:: python from EDAspy.optimization import SPEDA from EDAspy.benchmarks import ContinuousBenchmarkingCEC14 benchmarking = ContinuousBenchmarkingCEC14(10) speda = SPEDA(size_gen=300, max_iter=100, dead_iter=20, n_variables=10, lower_bound=-100, upper_bound=100, l=10) eda_result = speda.minimize(benchmarking.cec14_4, True) References: [1]: Vicente P. Soloviev, Concha Bielza and Pedro LarraƱaga. Semiparametric Estimation of Distribution Algorithm for continuous optimization. 2022 [2]: Atienza, D., Bielza, C., & LarraƱaga, P. (2022). PyBNesian: an extensible Python package for Bayesian networks. Neurocomputing, 504, 204-209. """ def __init__(self, size_gen: int, max_iter: int, dead_iter: int, n_variables: int, lower_bound: Union[np.array, List[float], float] = None, upper_bound: Union[np.array, List[float], float] = None, l: float = 10, alpha: float = 0.5, disp: bool = True, black_list: list = None, white_list: list = None, parallelize: bool = False, init_data: np.array = None, w_noise: float = .5): r""" :param size_gen: Population size. Number of individuals in each generation. :param max_iter: Maximum number of iterations during runtime. :param dead_iter: Stopping criteria. Number of iterations with no improvement after which, the algorithm finish. :param n_variables: Number of variables to be optimized. :param lower_bound: lower bound for the uniform distribution sampling. :param upper_bound: lower bound for the uniform distribution sampling. :param alpha: Percentage of population selected to update the probabilistic model. :param l: SPEDA is an archive-base approach. Thus, in each generation updates the probabilistic model with the best solutions of the previous l generations. :param alpha: Percentage of population selected to update the probabilistic model in each generation. :param disp: Set to True to print convergence messages. :param black_list: list of tuples with the forbidden arcs in the SPBN during runtime. :param white_list: list of tuples with the mandatory arcs in the SPBN during runtime. :param parallelize: True if the evaluation of the solutions is desired to be parallelized in multiple cores. :param init_data: Numpy array containing the data the EDA is desired to be initialized from. By default, an initializer is used. :param w_noise: Intensity of the Gaussian white noise added to each generation in order to avoid genetic drift. :type w_noise: float :type lower_bound: List of lower bounds of size equal to number of variables OR single bound to all dimensions. :type upper_bound: List of upper bounds of size equal to number of variables OR single bound to all dimensions. """ super().__init__(size_gen=size_gen, max_iter=max_iter, dead_iter=dead_iter, n_variables=n_variables, alpha=alpha, elite_factor=alpha, disp=disp, parallelize=parallelize, init_data=init_data, w_noise=w_noise) self.vars = [str(i) for i in range(n_variables)] # self.landscape_bounds = landscape_bounds self.pm = SPBN(self.vars, black_list=black_list, white_list=white_list) self.l_len = l*int(size_gen*self.alpha) # maximum number of individuals in the archive self.archive = np.empty((0, self.n_variables)) # In this implementation the individuals of the first generation are sampled from a uniform distribution # to not skew the following estimation of distributions. self.init = UniformGenInit(self.n_variables, lower_bound=lower_bound, upper_bound=upper_bound) def _update_archive(self): self.archive = np.append(self.archive, self.elite_temp, axis=0) self.archive = self.archive[-self.l_len:] def _update_pm(self): """ Learn the probabilistic model from the best individuals of previous generation, using the best solutions of the previous l generations """ self._update_archive() self.pm.learn(dataset=self.archive) def _new_generation(self): # self.generation = np.concatenate([self.pm.sample(size=self.size_gen), [self.best_ind_global]]) self.generation = self.pm.sample(size=self.size_gen)
# as it is not an elitist approach we just add the best individual to show always an improvement in the # history of the best solution costs