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...
def set_reporter_name(self, name): self.rep = Reporter(log_path=name)
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 = []
def set_reporter_name(self, name): self.rep = Reporter(logger=logging.getLogger(name))
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)
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)
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
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()
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__
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