コード例 #1
0
This module abstracts various operations in the swarm such as updating
the personal best, finding neighbors, etc. You can use these methods
to specify how the swarm will behave.
"""

# Import standard library
import logging

# Import modules
import numpy as np

from pyswarms.utils.reporter import Reporter
from pyswarms.backend.handlers import BoundaryHandler, VelocityHandler
from functools import partial

rep = Reporter(logger=logging.getLogger(__name__))


def compute_pbest(swarm):
    """Update the personal best score of a swarm instance

    You can use this method to update your personal best positions.

    .. code-block:: python

        import pyswarms.backend as P
        from pyswarms.backend.swarms import Swarm

        my_swarm = P.create_swarm(n_particles, dimensions)

        # Inside the for-loop...
コード例 #2
0
 def set_reporter_name(self, name):
     self.rep = Reporter(log_path=name)
コード例 #3
0
ファイル: traffic.py プロジェクト: Kihy/IDS2-CIC
    def __init__(self, static=True):
        super(Traffic, self).__init__(static)
        self.rep = Reporter(logger=logging.getLogger(__name__))

        # contains information about randomly generated packets
        self.auxiliary_info = []
コード例 #4
0
 def set_reporter_name(self, name):
     self.rep = Reporter(logger=logging.getLogger(name))
コード例 #5
0
class MOPSO(GlobalBestPSO):
    def set_reporter_name(self, name):
        self.rep = Reporter(log_path=name)

    def load_swarm(self, filename):
        with open(filename, "rb") as fin:
            self.swarm = pickle.load(fin)

    def optimize(self, objective_func, iters, n_processes=None, **kwargs):
        self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG)
        self.rep.log(
            "Optimize for {} iters with {}".format(iters, self.options),
            lvl=logging.INFO,
        )
        self.rep.log(
            "Population size: {}".format(self.swarm_size),
            lvl=logging.INFO,
        )
        filename = "CHECKPOINT_" + self.rep.log_path.split(".")[0]
        # Populate memory of the handlers
        self.bh.memory = self.swarm.position
        self.vh.memory = self.swarm.position

        # Setup Pool of processes for parallel evaluation
        pool = None if n_processes is None else mp.Pool(n_processes)
        if type(self.swarm.best_cost) is float:
            self.swarm.best_cost = FitnessObj(0.0, np.inf)
            self.swarm.pbest_cost = np.array(
                [FitnessObj(0.0, np.inf) for i in range(self.swarm_size[0])])
        for i in self.rep.pbar(iters, self.name):
            last_time = time.time()
            # Compute cost for current position and personal best
            # fmt: off
            self.swarm.current_cost = np.array(
                compute_objective_function(self.swarm,
                                           objective_func,
                                           pool=pool))
            self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(
                self.swarm)
            # Set best_cost_yet_found for ftol
            best_cost_yet_found = self.swarm.best_cost
            self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest(
                self.swarm)
            # fmt: on
            self.rep.hook(best_cost=self.swarm.best_cost)
            # Save to history
            hist = self.ToHistory(
                best_cost=self.swarm.best_cost,
                mean_pbest_cost=mean_vector(self.swarm.pbest_cost.tolist()),
                mean_neighbor_cost=self.swarm.best_cost,
                position=self.swarm.position,
                velocity=self.swarm.velocity,
            )
            self._populate_history(hist)
            # Verify stop criteria based on the relative acceptable cost ftol
            relative_measure = self.ftol * (1 + best_cost_yet_found.distance)
            if (np.abs(self.swarm.best_cost.distance -
                       best_cost_yet_found.distance) < relative_measure):
                break
            # Perform velocity and position updates
            self.swarm.velocity = self.top.compute_velocity(
                self.swarm, self.velocity_clamp, self.vh, self.bounds)
            self.swarm.position = self.top.compute_position(
                self.swarm, self.bounds, self.bh)
            self.rep.log(
                "Generation {}: Mean-fitness {}, std_dev_fitness {}, best {}".
                format(i, mean_vector(self.swarm.current_cost.tolist()),
                       std_vector(self.swarm.current_cost.tolist()),
                       self.swarm.best_cost),
                lvl=logging.INFO,
            )
            self.rep.log(
                "Time elapsed {}".format(time.time() - last_time),
                lvl=logging.INFO,
            )
            with open(filename, "wb") as fout:
                pickle.dump(self.swarm, fout)
        # Obtain the final best_cost and the final best_position
        final_best_cost = self.swarm.best_cost.__copy__()
        final_best_pos = self.swarm.pbest_pos[
            self.swarm.pbest_cost.argmin()].copy()
        # Write report in log and return final cost and position
        self.rep.log(
            "Optimization finished | best cost: {}, best pos: {}".format(
                final_best_cost, final_best_pos),
            lvl=logging.INFO,
        )
        return (final_best_cost, final_best_pos)
コード例 #6
0
class MyLocalBestPSO(LocalBestPSO):
    def set_reporter_name(self, name):
        self.rep = Reporter(logger=logging.getLogger(name))

    def load_swarm(self, filename):
        with open(filename, "rb") as fin:
            self.swarm = pickle.load(fin)

    def optimize(self, objective_func, iters, n_processes=None, **kwargs):
        self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG)
        self.rep.log(
            "Optimize for {} iters with {}".format(iters, self.options),
            lvl=logging.INFO,
        )
        self.rep.log(
            "Population size: {}".format(self.swarm_size),
            lvl=logging.INFO,
        )
        filename = "CHECKPOINT_PYSWARMS_"+self.rep.logger.name
        # Populate memory of the handlers
        self.bh.memory = self.swarm.position
        self.vh.memory = self.swarm.position

        # Setup Pool of processes for parallel evaluation
        pool = None if n_processes is None else mp.Pool(n_processes)
        self.swarm.pbest_cost = np.full(self.swarm_size[0], np.inf)
        for i in self.rep.pbar(iters, self.name):
            last_time = time.time()
            # Compute cost for current position and personal best
            self.swarm.current_cost = compute_objective_function(
                self.swarm, objective_func, pool=pool, **kwargs
            )
            self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(
                self.swarm
            )
            best_cost_yet_found = np.min(self.swarm.best_cost)
            # Update gbest from neighborhood
            self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest(
                self.swarm, p=self.p, k=self.k
            )
            self.rep.hook(best_cost=np.min(self.swarm.best_cost))
            # Save to history
            hist = self.ToHistory(
                best_cost=self.swarm.best_cost,
                mean_pbest_cost=np.mean(self.swarm.pbest_cost),
                mean_neighbor_cost=np.mean(self.swarm.best_cost),
                position=self.swarm.position,
                velocity=self.swarm.velocity,
            )
            self._populate_history(hist)
            # Verify stop criteria based on the relative acceptable cost ftol
            relative_measure = self.ftol * (1 + np.abs(best_cost_yet_found))
            if (
                np.abs(self.swarm.best_cost - best_cost_yet_found)
                < relative_measure
            ):
                break
            # Perform position velocity update
            self.swarm.velocity = self.top.compute_velocity(
                self.swarm, self.velocity_clamp, self.vh, self.bounds
            )
            self.swarm.position = self.top.compute_position(
                self.swarm, self.bounds, self.bh
            )
            self.rep.log(
                "Generation {}: Mean-fitness {}, std_dev_fitness {}, best {}".format(i,
                                                                                     np.mean(self.swarm.current_cost),
                                                                                     np.std(self.swarm.current_cost),
                                                                                     self.swarm.best_cost),
                lvl=logging.INFO,
            )
            self.rep.log("Time elapsed {}".format(time.time() - last_time), lvl=logging.INFO, )
            with open(filename, "wb") as fout:
                pickle.dump(self.swarm, fout)
        # Obtain the final best_cost and the final best_position
        final_best_cost = self.swarm.best_cost.copy()
        final_best_pos = self.swarm.pbest_pos[self.swarm.pbest_cost.argmin()].copy()
        # Write report in log and return final cost and position
        self.rep.log(
            "Optimization finished | best cost: {}, best pos: {}".format(
                final_best_cost, final_best_pos
            ),
            lvl=logging.INFO,
        )
        return (final_best_cost, final_best_pos)
コード例 #7
0
ファイル: optimize.py プロジェクト: lixinyuu/sklearn-pso
    def __init__(
        self,
        n_particles,
        dimensions,
        options,
        bounds=None,
        bh_strategy="periodic",
        velocity_clamp=None,
        vh_strategy="unmodified",
        center=1.00,
        ftol=-np.inf,
        init_pos=None,
    ):
        """
        A custom optimizer modified from pyswarms.single.global_best
        https://github.com/ljvmiranda921/pyswarms/blob/master/pyswarms/single/global_best.py
        Attributes
        ----------
        n_particles : int
            number of particles in the swarm.
        dimensions : int
            number of dimensions in the space.
        options : dict with keys :code:`{'c1', 'c2', 'w'}`
            a dictionary containing the parameters for the specific
            optimization technique.
                * c1 : float
                    cognitive parameter
                * c2 : float
                    social parameter
                * w : float
                    inertia parameter
        bounds : tuple of numpy.ndarray, optional
            a tuple of size 2 where the first entry is the minimum bound while
            the second entry is the maximum bound. Each array must be of shape
            :code:`(dimensions,)`.
        bh_strategy : str
            a strategy for the handling of out-of-bounds particles.
        velocity_clamp : tuple, optional
            a tuple of size 2 where the first entry is the minimum velocity and
            the second entry is the maximum velocity. It sets the limits for
            velocity clamping.
        vh_strategy : str
            a strategy for the handling of the velocity of out-of-bounds particles.
        center : list (default is :code:`None`)
            an array of size :code:`dimensions`
        ftol : float
            relative error in objective_func(best_pos) acceptable for
            convergence. Default is :code:`-np.inf`
        init_pos : numpy.ndarray, optional
            option to explicitly set the particles' initial positions. Set to
            :code:`None` if you wish to generate the particles randomly.
        """
        super(PSOoptimizer, self).__init__(
            n_particles=n_particles,
            dimensions=dimensions,
            options=options,
            bounds=bounds,
            velocity_clamp=velocity_clamp,
            center=center,
            ftol=ftol,
            init_pos=init_pos,
        )

        # Initialize logger
        self.rep = Reporter(logger=logging.getLogger(__name__))
        # Initialize the resettable attributes
        self.reset()
        # Initialize the topology
        self.top = Star()
        self.bh = BoundaryHandler(strategy=bh_strategy)
        self.vh = VelocityHandler(strategy=vh_strategy)
        self.name = __name__

        # Populate memory of the handlers
        self.bh.memory = self.swarm.position
        self.vh.memory = self.swarm.position
        self.swarm.pbest_cost = np.full(self.swarm_size[0], np.inf)

        # Set reached requirement
        self.reached_requirement = 0
コード例 #8
0
ファイル: optimize.py プロジェクト: lixinyuu/sklearn-pso
class PSOoptimizer(SwarmOptimizer):
    def __init__(
        self,
        n_particles,
        dimensions,
        options,
        bounds=None,
        bh_strategy="periodic",
        velocity_clamp=None,
        vh_strategy="unmodified",
        center=1.00,
        ftol=-np.inf,
        init_pos=None,
    ):
        """
        A custom optimizer modified from pyswarms.single.global_best
        https://github.com/ljvmiranda921/pyswarms/blob/master/pyswarms/single/global_best.py
        Attributes
        ----------
        n_particles : int
            number of particles in the swarm.
        dimensions : int
            number of dimensions in the space.
        options : dict with keys :code:`{'c1', 'c2', 'w'}`
            a dictionary containing the parameters for the specific
            optimization technique.
                * c1 : float
                    cognitive parameter
                * c2 : float
                    social parameter
                * w : float
                    inertia parameter
        bounds : tuple of numpy.ndarray, optional
            a tuple of size 2 where the first entry is the minimum bound while
            the second entry is the maximum bound. Each array must be of shape
            :code:`(dimensions,)`.
        bh_strategy : str
            a strategy for the handling of out-of-bounds particles.
        velocity_clamp : tuple, optional
            a tuple of size 2 where the first entry is the minimum velocity and
            the second entry is the maximum velocity. It sets the limits for
            velocity clamping.
        vh_strategy : str
            a strategy for the handling of the velocity of out-of-bounds particles.
        center : list (default is :code:`None`)
            an array of size :code:`dimensions`
        ftol : float
            relative error in objective_func(best_pos) acceptable for
            convergence. Default is :code:`-np.inf`
        init_pos : numpy.ndarray, optional
            option to explicitly set the particles' initial positions. Set to
            :code:`None` if you wish to generate the particles randomly.
        """
        super(PSOoptimizer, self).__init__(
            n_particles=n_particles,
            dimensions=dimensions,
            options=options,
            bounds=bounds,
            velocity_clamp=velocity_clamp,
            center=center,
            ftol=ftol,
            init_pos=init_pos,
        )

        # Initialize logger
        self.rep = Reporter(logger=logging.getLogger(__name__))
        # Initialize the resettable attributes
        self.reset()
        # Initialize the topology
        self.top = Star()
        self.bh = BoundaryHandler(strategy=bh_strategy)
        self.vh = VelocityHandler(strategy=vh_strategy)
        self.name = __name__

        # Populate memory of the handlers
        self.bh.memory = self.swarm.position
        self.vh.memory = self.swarm.position
        self.swarm.pbest_cost = np.full(self.swarm_size[0], np.inf)

        # Set reached requirement
        self.reached_requirement = 0

    def get_current_pos(self):
        return self.swarm.position

    def update(self, iters, current_cost, **kwargs):
        """
        Optimize the swarm for one iteration by providing its cost
        manually.
        Parameters
        ----------
        iters : int
            the current iterations
        current_cost : ndarray
            the current cost which should be provided
        """

        self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG)
        self.rep.log(
            "Optimize for {} iters with {}".format(iters, self.options),
            lvl=logging.DEBUG,
        )

        self.swarm.current_cost = current_cost
        self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(self.swarm)
        # Set best_cost_yet_found for ftol
        best_cost_yet_found = self.swarm.best_cost
        self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest(
            self.swarm)
        # fmt: on
        # self.rep.hook(best_cost=self.swarm.best_cost)
        # Save to history
        hist = self.ToHistory(
            best_cost=self.swarm.best_cost,
            mean_pbest_cost=np.mean(self.swarm.pbest_cost),
            mean_neighbor_cost=self.swarm.best_cost,
            position=self.swarm.position,
            velocity=self.swarm.velocity,
        )
        self._populate_history(hist)

        # Verify stop criteria based on the relative acceptable cost ftol
        relative_measure = self.ftol * (1 + np.abs(best_cost_yet_found))
        if (np.abs(self.swarm.best_cost - best_cost_yet_found) <
                relative_measure):
            self.reached_requirement = 1

        # Perform velocity and position updates
        self.swarm.velocity = self.top.compute_velocity(
            self.swarm, self.velocity_clamp, self.vh, self.bounds)
        self.swarm.position = self.top.compute_position(
            self.swarm, self.bounds, self.bh)

    def finalize(self):
        """
        Obtain the final best_cost and the final best_position
        """
        final_best_cost = self.swarm.best_cost.copy()
        final_best_pos = self.swarm.pbest_pos[
            self.swarm.pbest_cost.argmin()].copy()
        # Write report in log and return final cost and position
        self.rep.log(
            "Optimization finished | best cost: {}, best pos: {}".format(
                final_best_cost, final_best_pos),
            lvl=logging.INFO,
        )
        return (final_best_cost, final_best_pos)

    def optimize(self, objective_func, iters, **kwargs):
        #
        for _iter in range(iters):
            swarm_pos = self.get_current_pos()
            current_cost = objective_func(swarm_pos, **kwargs)
            self.update(_iter, current_cost)
        return self.finalize()
コード例 #9
0
    def __init__(
        self,
        n_particles,
        dimensions_discrete,
        options,
        bounds,
        bh_strategy="periodic",
        init_pos=None,
        velocity_clamp=None,
        vh_strategy="unmodified",
        ftol=-np.inf,
        ftol_iter=1,
    ):
        """Initialize the swarm

        Attributes
        ----------
        n_particles : int
            number of particles in the swarm.
        dimensions_discrete : int
            number of discrete dimensions of the search space.
        options : dict with keys :code:`{'c1', 'c2', 'w', 'k', 'p'}`
            a dictionary containing the parameters for the specific
            optimization technique
                * c1 : float
                    cognitive parameter
                * c2 : float
                    social parameter
                * w : float
                    inertia parameter
                * k : int
                    number of neighbors to be considered. Must be a
                    positive integer less than :code:`n_particles`
                * p: int {1,2}
                    the Minkowski p-norm to use. 1 is the
                    sum-of-absolute values (or L1 distance) while 2 is
                    the Euclidean (or L2) distance.
        bounds : tuple of numpy.ndarray
            a tuple of size 2 where the first entry is the minimum bound while
            the second entry is the maximum bound. Each array must be of shape
            :code:`(dimensions,)`.
        init_pos : numpy.ndarray, optional
            option to explicitly set the particles' initial positions. Set to
            :code:`None` if you wish to generate the particles randomly.
        velocity_clamp : tuple, optional
            a tuple of size 2 where the first entry is the minimum velocity
            and the second entry is the maximum velocity. It
            sets the limits for velocity clamping.
        vh_strategy : String
            a strategy for the handling of the velocity of out-of-bounds particles.
            Only the "unmodified" and the "adjust" strategies are allowed.
        ftol : float
            relative error in objective_func(best_pos) acceptable for
            convergence
        ftol_iter : int
            number of iterations over which the relative error in
            objective_func(best_pos) is acceptable for convergence.
            Default is :code:`1`
        """
        # Initialize logger
        self.rep = Reporter(logger=logging.getLogger(__name__))
        # Assign k-neighbors and p-value as attributes
        self.k, self.p = options["k"], options["p"]

        self.dimensions_discrete = dimensions_discrete

        self.bits, self.bounds = self.discretePSO_to_binaryPSO(
            dimensions_discrete, bounds)

        # Initialize parent class
        super(BinaryPSO, self).__init__(
            n_particles=n_particles,
            dimensions=sum(self.bits),
            binary=True,
            options=options,
            init_pos=init_pos,
            velocity_clamp=velocity_clamp,
            ftol=ftol,
            ftol_iter=ftol_iter,
        )
        # self.bounds = bounds
        # Initialize the resettable attributes
        self.reset()
        # Initialize the topology
        self.top = Ring(static=False)
        self.vh = VelocityHandler(strategy=vh_strategy)
        self.bh = BoundaryHandler(strategy=bh_strategy)
        self.name = __name__
コード例 #10
0
class DiscreteBoundedPSO(BinaryPSO):
    """
    This class is based on the Binary PSO class. It extends the BinaryPSO class
    by a function which allows the conversion of discrete optimization variables
    into binary variables, so that discrete optimization problems can be solved 
    """
    def __init__(
        self,
        n_particles,
        dimensions_discrete,
        options,
        bounds,
        bh_strategy="periodic",
        init_pos=None,
        velocity_clamp=None,
        vh_strategy="unmodified",
        ftol=-np.inf,
        ftol_iter=1,
    ):
        """Initialize the swarm

        Attributes
        ----------
        n_particles : int
            number of particles in the swarm.
        dimensions_discrete : int
            number of discrete dimensions of the search space.
        options : dict with keys :code:`{'c1', 'c2', 'w', 'k', 'p'}`
            a dictionary containing the parameters for the specific
            optimization technique
                * c1 : float
                    cognitive parameter
                * c2 : float
                    social parameter
                * w : float
                    inertia parameter
                * k : int
                    number of neighbors to be considered. Must be a
                    positive integer less than :code:`n_particles`
                * p: int {1,2}
                    the Minkowski p-norm to use. 1 is the
                    sum-of-absolute values (or L1 distance) while 2 is
                    the Euclidean (or L2) distance.
        bounds : tuple of numpy.ndarray
            a tuple of size 2 where the first entry is the minimum bound while
            the second entry is the maximum bound. Each array must be of shape
            :code:`(dimensions,)`.
        init_pos : numpy.ndarray, optional
            option to explicitly set the particles' initial positions. Set to
            :code:`None` if you wish to generate the particles randomly.
        velocity_clamp : tuple, optional
            a tuple of size 2 where the first entry is the minimum velocity
            and the second entry is the maximum velocity. It
            sets the limits for velocity clamping.
        vh_strategy : String
            a strategy for the handling of the velocity of out-of-bounds particles.
            Only the "unmodified" and the "adjust" strategies are allowed.
        ftol : float
            relative error in objective_func(best_pos) acceptable for
            convergence
        ftol_iter : int
            number of iterations over which the relative error in
            objective_func(best_pos) is acceptable for convergence.
            Default is :code:`1`
        """
        # Initialize logger
        self.rep = Reporter(logger=logging.getLogger(__name__))
        # Assign k-neighbors and p-value as attributes
        self.k, self.p = options["k"], options["p"]

        self.dimensions_discrete = dimensions_discrete

        self.bits, self.bounds = self.discretePSO_to_binaryPSO(
            dimensions_discrete, bounds)

        # Initialize parent class
        super(BinaryPSO, self).__init__(
            n_particles=n_particles,
            dimensions=sum(self.bits),
            binary=True,
            options=options,
            init_pos=init_pos,
            velocity_clamp=velocity_clamp,
            ftol=ftol,
            ftol_iter=ftol_iter,
        )
        # self.bounds = bounds
        # Initialize the resettable attributes
        self.reset()
        # Initialize the topology
        self.top = Ring(static=False)
        self.vh = VelocityHandler(strategy=vh_strategy)
        self.bh = BoundaryHandler(strategy=bh_strategy)
        self.name = __name__

    def optimize(self,
                 objective_func,
                 iters,
                 n_processes=None,
                 verbose=True,
                 **kwargs):
        """Optimize the swarm for a number of iterations

        Performs the optimization to evaluate the objective
        function :code:`f` for a number of iterations :code:`iter.`

        Parameters
        ----------
        objective_func : function
            objective function to be evaluated
        iters : int
            number of iterations
        n_processes : int, optional
            number of processes to use for parallel particle evaluation
            Defaut is None with no parallelization.
        verbose : bool
            enable or disable the logs and progress bar (default: True = enable logs)
        kwargs : dict
            arguments for objective function

        Returns
        -------
        tuple
            the local best cost and the local best position among the
            swarm.
        """
        # Apply verbosity
        if verbose:
            log_level = logging.INFO
        else:
            log_level = logging.NOTSET

        self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG)
        self.rep.log(
            "Optimize for {} iters with {}".format(iters, self.options),
            lvl=log_level,
        )
        # Populate memory of the handlers
        self.bh.memory = self.swarm.position
        self.vh.memory = self.swarm.position

        # Setup Pool of processes for parallel evaluation
        pool = None if n_processes is None else mp.Pool(n_processes)

        self.swarm.pbest_cost = np.full(self.swarm_size[0], np.inf)
        ftol_history = deque(maxlen=self.ftol_iter)
        for i in self.rep.pbar(iters, self.name) if verbose else range(iters):

            # Compute cost for current position and personal best
            ''' Binary swarm postitions need to be transformed to discrete swarm
                postitions first, because the objective function expects discrete
                values (only positions are transformed!), original binary
                position is saved in binary_swarm_position'''
            binary_swarm_position = self.BinarySwarmPositions_to_DiscreteSwarmPositions(
            )

            # Evaluate Cost Function
            self.swarm.current_cost = compute_objective_function(
                self.swarm, objective_func, pool, **kwargs)
            ''' Transform discrete swarm positions back to binary positions, 
            because the PSO works on binary particles'''
            self.swarm.position = binary_swarm_position

            self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(
                self.swarm)
            best_cost_yet_found = np.min(self.swarm.best_cost)
            # Update gbest from neighborhood
            self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest(
                self.swarm, p=self.p, k=self.k)
            if verbose:
                # Print to console
                self.rep.hook(best_cost=self.swarm.best_cost)
            # Save to history
            hist = self.ToHistory(
                best_cost=self.swarm.best_cost,
                mean_pbest_cost=np.mean(self.swarm.pbest_cost),
                mean_neighbor_cost=np.mean(self.swarm.best_cost),
                position=self.swarm.position,
                velocity=self.swarm.velocity,
            )
            self._populate_history(hist)
            # Verify stop criteria based on the relative acceptable cost ftol
            relative_measure = self.ftol * (1 + np.abs(best_cost_yet_found))
            delta = (np.abs(self.swarm.best_cost - best_cost_yet_found) <
                     relative_measure)
            if i < self.ftol_iter:
                ftol_history.append(delta)
            else:
                ftol_history.append(delta)
                if all(ftol_history):
                    break
            # Perform position velocity update
            self.swarm.velocity = self.top.compute_velocity(
                self.swarm, self.velocity_clamp, self.vh)
            self.swarm.position = self._compute_position(self.swarm)

        # Obtain the final best_cost and the final best_position
        final_best_cost = self.swarm.best_cost.copy()
        final_best_pos = self.swarm.pbest_pos[
            self.swarm.pbest_cost.argmin()].copy()
        self.rep.log(
            "Optimization finished | best cost: {}, best pos: {}".format(
                final_best_cost, final_best_pos),
            lvl=log_level,
        )
        # Close Pool of Processes
        if n_processes is not None:
            pool.close()

        return (final_best_cost, final_best_pos)

    def discretePSO_to_binaryPSO(self, dimensions_discrete, bounds):
        """
        Translate a discrete PSO-problem into a binary PSO-problem by
        calculating the number of bits necessary to represent the discrete
        optimization problem with "dimensions_discrete" number of discrete
        variables as a binary optimization problem. The bounds are encoded in 
        the binary representation and might be tightened.
        
        Parameters
        ----------  
        dimensions_discrete: integer
            dimension of the discrete search space.
        bounds : tuple of numpy.ndarray
            a tuple of size 2 where the first entry is the minimum bound while
            the second entry is the maximum bound. Each array must be of shape
            :code:`(dimensions,)`.
        """

        bits = []

        for n in range(0, dimensions_discrete):

            # Number of bits required rounding down!
            bits.append(
                int(np.log10(bounds[1][n] - bounds[0][n] + 1) / np.log10(2)))

            # Adjust upper bound accordingly
            bounds[1][n] = bounds[0][n] + 2**bits[n] - 1

        return bits, bounds

    def BinarySwarmPositions_to_DiscreteSwarmPositions(self):
        """
        Converts binary self.swarm.position to discrete values. Returns the 
        original binary position, so that it can be used to restore 
        self.swarm.position to the original binary values.
        """

        binary_position = self.swarm.position
        discrete_position = np.zeros(
            (self.n_particles, self.dimensions_discrete))

        cum_sum = 0

        for i in range(0, self.dimensions_discrete):

            bit = self.bits[i]
            lb = self.bounds[0][i]

            discrete_position[:,[i]] = lb + \
            self.bool2int(binary_position[:,cum_sum:cum_sum+bit])

            cum_sum = cum_sum + bit

        # Set swarm position to discrete integer values
        self.swarm.position = discrete_position.astype(int)

        return binary_position

    def bool2int(self, x):
        """
        Converts a binary variable represented by an array x (row vector) into
        an integer value
        """

        x_int = np.zeros((x.shape[0], 1))

        for row in range(0, x.shape[0]):
            row_int = 0

            for i, j in enumerate(x[row, :]):
                row_int += j << i

            x_int[row] = row_int

        return x_int