diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..cecfe93a --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[report] +exclude_lines = + pragma: no cover + @abc.abstractmethod + pos = init_pos diff --git a/docs/conf.py b/docs/conf.py index a9ad411d..ba7be3b2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,8 +13,12 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys +# Import standard library import os +import sys + +# Import from pyswarms +import pyswarms # If extensions (or modules to document with autodoc) are in another # directory, add these directories to sys.path here. If the directory is @@ -32,7 +36,6 @@ # sys.path.insert(0, project_root) sys.path.insert(0, os.path.abspath("../")) -import pyswarms # -- General configuration --------------------------------------------- diff --git a/pyswarms/backend/generators.py b/pyswarms/backend/generators.py index 91f7dbf6..685615a8 100644 --- a/pyswarms/backend/generators.py +++ b/pyswarms/backend/generators.py @@ -9,8 +9,10 @@ """ +# Import standard library import logging +# Import modules import numpy as np from ..utils.reporter import Reporter @@ -44,6 +46,14 @@ def generate_swarm( ------- numpy.ndarray swarm matrix of shape (n_particles, n_dimensions) + + Raises + ------ + ValueError + When the shapes and values of bounds, dimensions, and init_pos + are inconsistent. + TypeError + When the argument passed to bounds is not an iterable. """ try: if (init_pos is not None) and (bounds is None): @@ -70,12 +80,13 @@ def generate_swarm( low=min_bounds, high=max_bounds, size=(n_particles, dimensions) ) except ValueError: - rep.logger.exception( - "Please check the size and value of bounds and dimensions" - ) + msg = "Bounds and/or init_pos should be of size ({},)" + rep.logger.exception(msg.format(dimensions)) raise except TypeError: - rep.logger.exception("Invalid input type!") + msg = "generate_swarm() takes an int for n_particles and dimensions and an array for bounds" + rep.logger.exception(msg) + raise else: return pos @@ -96,6 +107,18 @@ def generate_discrete_swarm( init_pos : :code:`numpy.ndarray` (default is :code:`None`) option to explicitly set the particles' initial positions. Set to :code:`None` if you wish to generate the particles randomly. + + Returns + ------- + numpy.ndarray + swarm matrix of shape (n_particles, n_dimensions) + + Raises + ------ + ValueError + When init_pos during binary=True does not contain two unique values. + TypeError + When the argument passed to n_particles or dimensions is incorrect. """ try: if (init_pos is not None) and binary: @@ -111,11 +134,12 @@ def generate_discrete_swarm( size=(n_particles, dimensions) ).argsort(axis=1) except ValueError: - rep.logger.exception( - "Please check the size and value of bounds and dimensions" - ) + rep.logger.exception("Please check the size and value of dimensions") + raise except TypeError: - rep.logger.exception("Invalid input type!") + msg = "generate_discrete_swarm() takes an int for n_particles and dimensions" + rep.logger.exception(msg) + raise else: return pos @@ -145,11 +169,13 @@ def generate_velocity(n_particles, dimensions, clamp=None): size=(n_particles, dimensions) ) + min_velocity except ValueError: - rep.logger.exception( - "Please check the size and value of clamp and dimensions" - ) + msg = "Please check clamp shape: {} != {}" + rep.logger.exception(msg.format(len(clamp), dimensions)) + raise except TypeError: - rep.logger.exception("Invalid input type!") + msg = "generate_velocity() takes an int for n_particles and dimensions and an array for clamp" + rep.logger.exception(msg) + raise else: return velocity diff --git a/pyswarms/backend/operators.py b/pyswarms/backend/operators.py index 7714f241..2ff104e3 100644 --- a/pyswarms/backend/operators.py +++ b/pyswarms/backend/operators.py @@ -8,8 +8,10 @@ to specify how the swarm will behave. """ +# Import standard library import logging +# Import modules import numpy as np from ..utils.reporter import Reporter @@ -69,6 +71,7 @@ def compute_pbest(swarm): rep.logger.exception( "Please pass a Swarm class. You passed {}".format(type(swarm)) ) + raise else: return (new_pbest_pos, new_pbest_cost) @@ -139,8 +142,10 @@ def compute_velocity(swarm, clamp): rep.logger.exception( "Please pass a Swarm class. You passed {}".format(type(swarm)) ) + raise except KeyError: rep.logger.exception("Missing keyword in swarm.options") + raise else: return updated_velocity @@ -187,5 +192,6 @@ def compute_position(swarm, bounds): rep.logger.exception( "Please pass a Swarm class. You passed {}".format(type(swarm)) ) + raise else: return position diff --git a/pyswarms/backend/swarms.py b/pyswarms/backend/swarms.py index 71a6d730..eb88cd00 100644 --- a/pyswarms/backend/swarms.py +++ b/pyswarms/backend/swarms.py @@ -8,6 +8,7 @@ as input to most backend cases. """ +# Import modules import numpy as np from attr import attrib, attrs from attr.validators import instance_of diff --git a/pyswarms/backend/topology/base.py b/pyswarms/backend/topology/base.py index 63129ad5..d1b6a5a0 100644 --- a/pyswarms/backend/topology/base.py +++ b/pyswarms/backend/topology/base.py @@ -12,6 +12,7 @@ :mod:`pyswarms.backend.swarms.Swarm` module. """ +# Import standard library import abc import logging @@ -33,7 +34,7 @@ def __init__(self, static, **kwargs): self.rep.log( "Running on `dynamic` topology," "set `static=True` for fixed neighbors.", - lvl=10, + lvl=logging.DEBUG, ) @abc.abstractmethod diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 53b0fea4..4d6f77c4 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -6,8 +6,10 @@ This class implements a pyramid topology. In this topology, the particles are connected by N-dimensional simplices. """ +# Import standard library import logging +# Import modules import numpy as np from scipy.spatial import Delaunay @@ -29,7 +31,7 @@ def __init__(self, static=False): super(Pyramid, self).__init__(static) self.rep = Reporter(logger=logging.getLogger(__name__)) - def compute_gbest(self, swarm): + def compute_gbest(self, swarm, **kwargs): """Update the global best using a pyramid neighborhood approach This topology uses the :code:`Delaunay` class from :code:`scipy`. To prevent precision errors in the Delaunay diff --git a/pyswarms/backend/topology/random.py b/pyswarms/backend/topology/random.py index b72116d8..09671b98 100644 --- a/pyswarms/backend/topology/random.py +++ b/pyswarms/backend/topology/random.py @@ -6,9 +6,11 @@ This class implements a random topology. All particles are connected in a random fashion. """ +# Import standard library import itertools import logging +# Import modules import numpy as np from scipy.sparse.csgraph import connected_components, dijkstra @@ -18,23 +20,19 @@ class Random(Topology): - def __init__(self, k, static=False): + def __init__(self, static=False): """Initializes the class Parameters ---------- - k : int - number of neighbors to be considered. Must be a - positive integer less than :code:`n_particles-1` static : bool (Default is :code:`False`) a boolean that decides whether the topology is static or dynamic """ super(Random, self).__init__(static) - self.k = k self.rep = Reporter(logger=logging.getLogger(__name__)) - def compute_gbest(self, swarm): + def compute_gbest(self, swarm, k, **kwargs): """Update the global best using a random neighborhood approach This uses random class from :code:`numpy` to give every particle k @@ -51,6 +49,9 @@ def compute_gbest(self, swarm): ---------- swarm : pyswarms.backend.swarms.Swarm a Swarm instance + k : int + number of neighbors to be considered. Must be a + positive integer less than :code:`n_particles-1` Returns ------- @@ -62,7 +63,7 @@ def compute_gbest(self, swarm): try: # Check if the topology is static or dynamic and assign neighbors if (self.static and self.neighbor_idx is None) or not self.static: - adj_matrix = self.__compute_neighbors(swarm, self.k) + adj_matrix = self.__compute_neighbors(swarm, k) self.neighbor_idx = np.array( [ adj_matrix[i].nonzero()[0] @@ -83,10 +84,14 @@ def compute_gbest(self, swarm): ).astype(int) # Obtain best cost and position - best_cost = np.min(swarm.pbest_cost[best_neighbor]) - best_pos = swarm.pbest_pos[ - best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])] - ] + if np.min(swarm.pbest_cost) < swarm.best_cost: + best_cost = np.min(swarm.pbest_cost[best_neighbor]) + best_pos = swarm.pbest_pos[ + best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])] + ] + else: + # Just get the previous best_pos and best_cost + best_pos, best_cost = swarm.best_pos, swarm.best_cost except AttributeError: self.rep.logger.exception( diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index a9fbf8d7..8af1a912 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -9,8 +9,10 @@ optimizers. """ +# Import standard library import logging +# Import modules import numpy as np from scipy.spatial import cKDTree @@ -20,7 +22,7 @@ class Ring(Topology): - def __init__(self, p, k, static=False): + def __init__(self, static=False): """Initializes the class Parameters @@ -28,19 +30,11 @@ def __init__(self, p, k, static=False): static : bool (Default is :code:`False`) a boolean that decides whether the topology is static or dynamic - 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. - k : int - number of neighbors to be considered. Must be a - positive integer less than :code:`n_particles` """ super(Ring, self).__init__(static) - self.p, self.k = p, k self.rep = Reporter(logger=logging.getLogger(__name__)) - def compute_gbest(self, swarm): + def compute_gbest(self, swarm, p, k, **kwargs): """Update the global best using a ring-like neighborhood approach This uses the cKDTree method from :code:`scipy` to obtain the nearest @@ -50,6 +44,13 @@ def compute_gbest(self, swarm): ---------- swarm : pyswarms.backend.swarms.Swarm a Swarm instance + 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. + k : int + number of neighbors to be considered. Must be a + positive integer less than :code:`n_particles` Returns ------- @@ -63,14 +64,12 @@ def compute_gbest(self, swarm): if (self.static and self.neighbor_idx is None) or not self.static: # Obtain the nearest-neighbors for each particle tree = cKDTree(swarm.position) - _, self.neighbor_idx = tree.query( - swarm.position, p=self.p, k=self.k - ) + _, self.neighbor_idx = tree.query(swarm.position, p=p, k=k) # Map the computed costs to the neighbour indices and take the # argmin. If k-neighbors is equal to 1, then the swarm acts # independently of each other. - if self.k == 1: + if k == 1: # The minimum index is itself, no mapping needed. best_neighbor = swarm.pbest_cost[self.neighbor_idx][ :, np.newaxis diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index b341f7d9..85974c2d 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -9,8 +9,10 @@ optimizers. """ +# Import standard library import logging +# Import modules import numpy as np from .. import operators as ops @@ -19,16 +21,17 @@ class Star(Topology): - def __init__(self): + def __init__(self, static=None, **kwargs): + # static = None is just an artifact to make the API consistent + # Setting it will not change swarm behavior super(Star, self).__init__(static=True) self.rep = Reporter(logger=logging.getLogger(__name__)) - def compute_gbest(self, swarm): + def compute_gbest(self, swarm, **kwargs): """Update the global best using a star topology This method takes the current pbest_pos and pbest_cost, then returns - the minimum cost and position from the matrix. It should be used in - tandem with an if statement + the minimum cost and position from the matrix. .. code-block:: python @@ -39,10 +42,8 @@ def compute_gbest(self, swarm): my_swarm = P.create_swarm(n_particles, dimensions) my_topology = Star() - # If the minima of the pbest_cost is less than the best_cost - if np.min(pbest_cost) < best_cost: - # Update best_cost and position - swarm.best_pos, swarm.best_cost = my_topology.compute_best_particle(my_swarm) + # Update best_cost and position + swarm.best_pos, swarm.best_cost = my_topology.compute_best_particle(my_swarm) Parameters ---------- @@ -61,8 +62,14 @@ def compute_gbest(self, swarm): self.neighbor_idx = np.tile( np.arange(swarm.n_particles), (swarm.n_particles, 1) ) - best_pos = swarm.pbest_pos[np.argmin(swarm.pbest_cost)] - best_cost = np.min(swarm.pbest_cost) + if np.min(swarm.pbest_cost) < swarm.best_cost: + # Get the particle position with the lowest pbest_cost + # and assign it to be the best_pos + best_pos = swarm.pbest_pos[np.argmin(swarm.pbest_cost)] + best_cost = np.min(swarm.pbest_cost) + else: + # Just get the previous best_pos and best_cost + best_pos, best_cost = swarm.best_pos, swarm.best_cost except AttributeError: self.rep.logger.exception( "Please pass a Swarm class. You passed {}".format(type(swarm)) diff --git a/pyswarms/backend/topology/von_neumann.py b/pyswarms/backend/topology/von_neumann.py index e20eb1a0..d8765b41 100644 --- a/pyswarms/backend/topology/von_neumann.py +++ b/pyswarms/backend/topology/von_neumann.py @@ -6,19 +6,21 @@ This class implements a Von Neumann topology. """ +# Import standard library import logging -from .ring import Ring from ...utils.reporter import Reporter +from .ring import Ring class VonNeumann(Ring): - def __init__(self, p, r): - super(VonNeumann, self).__init__(static=True, p=p, k=None) - self.r = r + def __init__(self, static=None): + # static = None is just an artifact to make the API consistent + # Setting it will not change swarm behavior + super(VonNeumann, self).__init__(static=True) self.rep = Reporter(logger=logging.getLogger(__name__)) - def compute_gbest(self, swarm): + def compute_gbest(self, swarm, p, r, **kwargs): """Updates the global best using a neighborhood approach The Von Neumann topology inherits from the Ring topology and uses @@ -44,8 +46,8 @@ def compute_gbest(self, swarm): float Best cost """ - self.k = VonNeumann.delannoy(swarm.dimensions, self.r) - return super(VonNeumann, self).compute_gbest(swarm) + k = VonNeumann.delannoy(swarm.dimensions, r) + return super(VonNeumann, self).compute_gbest(swarm, p, k) @staticmethod def delannoy(d, r): diff --git a/pyswarms/base/base_discrete.py b/pyswarms/base/base_discrete.py index 9ac54099..9a84c405 100644 --- a/pyswarms/base/base_discrete.py +++ b/pyswarms/base/base_discrete.py @@ -28,9 +28,11 @@ """ +# Import standard library import abc from collections import namedtuple +# Import modules import numpy as np # Import from package @@ -38,40 +40,6 @@ class DiscreteSwarmOptimizer(abc.ABC): - def assertions(self): - """Check inputs and throw assertions - - Raises - ------ - TypeError - When the :code:`bounds` is not of type tuple - IndexError - When the :code:`bounds` is not of size 2. - When the arrays in :code:`bounds` is not of equal size. - When the shape of :code:`bounds` is not the same as `dimensions`. - ValueError - When the value of :code:`bounds[1]` is less than - :code:`bounds[0]`. - """ - - # Check clamp settings - if self.velocity_clamp is not None: - if not isinstance(self.velocity_clamp, tuple): - raise TypeError("Parameter `velocity_clamp` must be a tuple") - if not len(self.velocity_clamp) == 2: - raise IndexError( - "Parameter `velocity_clamp` must be of " "size 2" - ) - if not self.velocity_clamp[0] < self.velocity_clamp[1]: - raise ValueError( - "Make sure that velocity_clamp is in the " - "form (v_min, v_max)" - ) - - # Required keys in options argument - if not all(key in self.options for key in ("c1", "c2", "w")): - raise KeyError("Missing either c1, c2, or w in options") - def __init__( self, n_particles, @@ -136,8 +104,6 @@ def __init__( "velocity", ], ) - # Invoke assertions - self.assertions() # Initialize resettable attributes self.reset() diff --git a/pyswarms/base/base_single.py b/pyswarms/base/base_single.py index b804593b..862dce8b 100644 --- a/pyswarms/base/base_single.py +++ b/pyswarms/base/base_single.py @@ -30,80 +30,17 @@ :mod:`pyswarms.single.general_optimizer`: a more general PSO implementation with a custom topology """ +# Import standard library import abc from collections import namedtuple +# Import modules import numpy as np from ..backend import create_swarm class SwarmOptimizer(abc.ABC): - def assertions(self): - """Check inputs and throw assertions - - Raises - ------ - TypeError - When the :code:`bounds` is not of type tuple - IndexError - When the :code:`bounds` is not of size 2. - When the arrays in :code:`bounds` is not of equal size. - When the shape of :code:`bounds` is not the same as :code:`dimensions`. - ValueError - When the value of :code:`bounds[1]` is less than - :code:`bounds[0]`. - """ - - # Check setting of bounds - if self.bounds is not None: - if not isinstance(self.bounds, tuple): - raise TypeError("Parameter `bound` must be a tuple.") - if not len(self.bounds) == 2: - raise IndexError("Parameter `bound` must be of size 2.") - if not self.bounds[0].shape == self.bounds[1].shape: - raise IndexError("Arrays in `bound` must be of equal shapes") - if ( - not self.bounds[0].shape[0] - == self.bounds[1].shape[0] - == self.dimensions - ): - raise IndexError( - "Parameter `bound` must be the same shape " - "as dimensions." - ) - if not (self.bounds[1] > self.bounds[0]).all(): - raise ValueError( - "Values of `bounds[1]` must be greater than " - "`bounds[0]`." - ) - - # Check clamp settings - if hasattr(self.velocity_clamp, "__iter__"): - if not len(self.velocity_clamp) == 2: - raise IndexError( - "Parameter `velocity_clamp` must be of " "size 2" - ) - if not self.velocity_clamp[0] < self.velocity_clamp[1]: - raise ValueError( - "Make sure that velocity_clamp is in the " - "form (min, max)" - ) - - # Check setting of center - if isinstance(self.center, (list, np.ndarray)): - if not len(self.center) == self.dimensions: - raise IndexError( - "Parameter `center` must be the same shape " - "as dimensions." - ) - if isinstance(self.center, np.ndarray) and self.center.ndim != 1: - raise ValueError("Parameter `center` must have a 1d array") - - # Required keys in options argument - if not all(key in self.options for key in ("c1", "c2", "w")): - raise KeyError("Missing either c1, c2, or w in options") - def __init__( self, n_particles, @@ -168,8 +105,6 @@ def __init__( "velocity", ], ) - # Invoke assertions - self.assertions() # Initialize resettable attributes self.reset() diff --git a/pyswarms/discrete/binary.py b/pyswarms/discrete/binary.py index 91f710f3..5dd7090a 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -51,43 +51,20 @@ Conference on Systems, Man, and Cybernetics, 1997. """ +# Import standard library import logging from time import sleep +# Import modules import numpy as np -from ..base import DiscreteSwarmOptimizer from ..backend.operators import compute_pbest from ..backend.topology import Ring +from ..base import DiscreteSwarmOptimizer from ..utils.reporter import Reporter class BinaryPSO(DiscreteSwarmOptimizer): - def assertions(self): - """Check inputs and throw assertions - - Raises - ------ - KeyError - When one of the required dictionary keys is missing. - ValueError - When the number of neighbors is not within the range :code:`[0, n_particles]`. - When the p-value is not in the list of values :code:`[1,2]`. - """ - super(BinaryPSO, self).assertions() - - if not all(key in self.options for key in ("k", "p")): - raise KeyError("Missing either k or p in options") - if not 0 <= self.k <= self.n_particles: - raise ValueError( - "No. of neighbors must be between 0 and no. of" "particles." - ) - if self.p not in [1, 2]: - raise ValueError( - "p-value should either be 1 (for L1/Minkowski)" - "or 2 (for L2/Euclidean)." - ) - def __init__( self, n_particles, @@ -140,12 +117,10 @@ def __init__( velocity_clamp=velocity_clamp, ftol=ftol, ) - # Invoke assertions - self.assertions() # Initialize the resettable attributes self.reset() # Initialize the topology - self.top = Ring(static=False, p=self.p, k=self.k) + self.top = Ring(static=False) self.name = __name__ def optimize(self, objective_func, iters, fast=False, **kwargs): @@ -171,9 +146,10 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): the local best cost and the local best position among the swarm. """ - self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG) self.rep.log( - "Optimize for {} iters with {}".format(iters, self.options), lvl=20 + "Optimize for {} iters with {}".format(iters, self.options), + lvl=logging.INFO, ) for i in self.rep.pbar(iters, self.name): @@ -192,7 +168,7 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): 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 + self.swarm, p=self.p, k=self.k ) # Print to console self.rep.hook(best_cost=self.swarm.best_cost) @@ -224,7 +200,7 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): "Optimization finished | best cost: {}, best pos: {}".format( final_best_cost, final_best_pos ), - lvl=20, + lvl=logging.INFO, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py index 1bb137d2..ed7d9942 100644 --- a/pyswarms/single/general_optimizer.py +++ b/pyswarms/single/general_optimizer.py @@ -56,13 +56,15 @@ Networks, 1995, pp. 1942-1948. """ +# Import standard library import logging from time import sleep +# Import modules import numpy as np from ..backend.operators import compute_pbest -from ..backend.topology import Random, Ring, Topology, VonNeumann +from ..backend.topology import Topology from ..base import SwarmOptimizer from ..utils.reporter import Reporter @@ -88,7 +90,8 @@ def __init__( number of particles in the swarm. dimensions : int number of dimensions in the space. - options : dict with keys :code:`{'c1', 'c2', 'w'}` or :code:`{'c1', 'c2', 'w', 'k', 'p'}` + options : dict with keys :code:`{'c1', 'c2', 'w'}` or :code:`{'c1', + 'c2', 'w', 'k', 'p'}` a dictionary containing the parameters for the specific optimization technique. * c1 : float @@ -97,27 +100,26 @@ def __init__( social parameter * w : float inertia parameter - if used with the :code:`Ring`, :code:`VonNeumann` or :code:`Random` topology the additional - parameter k must be included + if used with the :code:`Ring`, :code:`VonNeumann` or + :code:`Random` topology the additional parameter k must be + included * k : int - number of neighbors to be considered. Must be a - positive integer less than :code:`n_particles` + number of neighbors to be considered. Must be a positive + integer less than :code:`n_particles` if used with the :code:`Ring` topology the additional parameters k and p must be included * 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. + the Minkowski p-norm to use. 1 is the sum-of-absolute + values (or L1 distance) while 2 is the Euclidean (or L2) + distance. if used with the :code:`VonNeumann` topology the additional parameters p and r must be included * r: int - the range of the VonNeumann topology. - This is used to determine the number of - neighbours in the topology. + the range of the VonNeumann topology. This is used to + determine the number of neighbours in the topology. topology : pyswarms.backend.topology.Topology - a :code:`Topology` object that defines the topology to use - in the optimization process. The currently available topologies - are: + a :code:`Topology` object that defines the topology to use in the + optimization process. The currently available topologies are: * Star All particles are connected * Ring (static and dynamic) @@ -128,17 +130,17 @@ def __init__( Particles are connected in N-dimensional simplices * Random (static and dynamic) Particles are connected to k random particles - Static variants of the topologies remain with the same neighbours - over the course of the optimization. Dynamic variants calculate - new neighbours every time step. + Static variants of the topologies remain with the same + neighbours over the course of the optimization. Dynamic + variants calculate new neighbours every time step. bounds : tuple of :code:`np.ndarray` (default is :code:`None`) - 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,)`. + 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,)`. velocity_clamp : tuple (default is :code:`None`) - 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. + 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. center : list (default is :code:`None`) an array of size :code:`dimensions` ftol : float @@ -158,8 +160,6 @@ def __init__( # Initialize logger self.rep = Reporter(logger=logging.getLogger(__name__)) - # Invoke assertions - self.assertions() # Initialize the resettable attributes self.reset() # Initialize the topology and check for type @@ -194,28 +194,24 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): if not fast: sleep(0.01) - self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG) self.rep.log( - "Optimize for {} iters with {}".format(iters, self.options), lvl=20 + "Optimize for {} iters with {}".format(iters, self.options), + lvl=logging.INFO, ) for i in self.rep.pbar(iters, self.name): # Compute cost for current position and personal best - self.swarm.current_cost = objective_func( - self.swarm.position, **kwargs - ) - self.swarm.pbest_cost = objective_func( - self.swarm.pbest_pos, **kwargs - ) - self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest( - self.swarm - ) + # fmt: off + self.swarm.current_cost = objective_func(self.swarm.position, **kwargs) + self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos, **kwargs) + self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(self.swarm) best_cost_yet_found = self.swarm.best_cost + # fmt: on # Update swarm - if np.min(self.swarm.pbest_cost) < self.swarm.best_cost: - self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( - self.swarm - ) + self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( + self.swarm, **self.options + ) # Print to console self.rep.hook(best_cost=self.swarm.best_cost) hist = self.ToHistory( @@ -248,6 +244,6 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): "Optimization finished | best cost: {}, best pos: {}".format( final_best_cost, final_best_pos ), - lvl=20, + lvl=logging.INFO, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/single/global_best.py b/pyswarms/single/global_best.py index ebe0ffb0..a036d241 100644 --- a/pyswarms/single/global_best.py +++ b/pyswarms/single/global_best.py @@ -55,9 +55,11 @@ Networks, 1995, pp. 1942-1948. """ +# Import standard library import logging from time import sleep +# Import modules import numpy as np from ..backend.operators import compute_pbest @@ -122,8 +124,6 @@ def __init__( # Initialize logger self.rep = Reporter(logger=logging.getLogger(__name__)) - # Invoke assertions - self.assertions() # Initialize the resettable attributes self.reset() # Initialize the topology @@ -153,30 +153,24 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): the global best cost and the global best position. """ - self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG) self.rep.log( - "Optimize for {} iters with {}".format(iters, self.options), lvl=20 + "Optimize for {} iters with {}".format(iters, self.options), + lvl=logging.INFO, ) for i in self.rep.pbar(iters, self.name): if not fast: sleep(0.01) # Compute cost for current position and personal best - self.swarm.current_cost = objective_func( - self.swarm.position, **kwargs - ) - self.swarm.pbest_cost = objective_func( - self.swarm.pbest_pos, **kwargs - ) - self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest( - self.swarm - ) + # fmt: off + self.swarm.current_cost = objective_func(self.swarm.position, **kwargs) + self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos, **kwargs) + 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 - # Get minima of pbest and check if it's less than gbest - if np.min(self.swarm.pbest_cost) < self.swarm.best_cost: - self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( - self.swarm - ) + 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( @@ -209,6 +203,6 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): "Optimization finished | best cost: {}, best pos: {}".format( final_best_cost, final_best_pos ), - lvl=20, + lvl=logging.INFO, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/single/local_best.py b/pyswarms/single/local_best.py index 6f6dde4d..1f35b775 100644 --- a/pyswarms/single/local_best.py +++ b/pyswarms/single/local_best.py @@ -64,9 +64,11 @@ Symposium on Micromachine and Human Science, 1995, pp. 39–43. """ +# Import standard library import logging from time import sleep +# Import modules import numpy as np from ..backend.operators import compute_pbest @@ -76,31 +78,6 @@ class LocalBestPSO(SwarmOptimizer): - def assertions(self): - """Check inputs and throw assertions - - Raises - ------ - KeyError - When one of the required dictionary keys is missing. - ValueError - When the number of neighbors is not within the range :code:`[0, n_particles]`. - When the p-value is not in the list of values :code:`[1,2]`. - """ - super(LocalBestPSO, self).assertions() - - if not all(key in self.options for key in ("k", "p")): - raise KeyError("Missing either k or p in options") - if not 0 <= self.k <= self.n_particles: - raise ValueError( - "No. of neighbors must be between 0 and no. " "of particles." - ) - if self.p not in [1, 2]: - raise ValueError( - "p-value should either be 1 (for L1/Minkowski) " - "or 2 (for L2/Euclidean)." - ) - def __init__( self, n_particles, @@ -171,12 +148,10 @@ def __init__( ) # Initialize logger self.rep = Reporter(logger=logging.getLogger(__name__)) - # Invoke assertions - self.assertions() # Initialize the resettable attributes self.reset() # Initialize the topology - self.top = Ring(static=static, p=self.p, k=self.k) + self.top = Ring(static=static) self.name = __name__ def optimize(self, objective_func, iters, fast=False, **kwargs): @@ -202,9 +177,10 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): the local best cost and the local best position among the swarm. """ - self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=logging.DEBUG) self.rep.log( - "Optimize for {} iters with {}".format(iters, self.options), lvl=20 + "Optimize for {} iters with {}".format(iters, self.options), + lvl=logging.INFO, ) for i in self.rep.pbar(iters, self.name): @@ -223,7 +199,7 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): 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 + self.swarm, p=self.p, k=self.k ) self.rep.hook(best_cost=np.min(self.swarm.best_cost)) # Save to history @@ -257,6 +233,6 @@ def optimize(self, objective_func, iters, fast=False, **kwargs): "Optimization finished | best cost: {}, best pos: {}".format( final_best_cost, final_best_pos ), - lvl=20, + lvl=logging.INFO, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/utils/decorators/decorators.py b/pyswarms/utils/decorators/decorators.py index e45400c5..8e22d0ab 100644 --- a/pyswarms/utils/decorators/decorators.py +++ b/pyswarms/utils/decorators/decorators.py @@ -1,3 +1,4 @@ +# Import modules import numpy as np diff --git a/pyswarms/utils/functions/single_obj.py b/pyswarms/utils/functions/single_obj.py index 4dfc1234..aa34a2ca 100644 --- a/pyswarms/utils/functions/single_obj.py +++ b/pyswarms/utils/functions/single_obj.py @@ -36,6 +36,7 @@ - Three Hump Camel, threehump """ +# Import modules import numpy as np diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index 5c3bdda9..1ca6931f 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -6,6 +6,7 @@ This module implements helpful classes to format your plots or create meshes. """ +# Import modules import numpy as np from attr import attrib, attrs from attr.validators import instance_of diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py index 822ec290..98c3e9f1 100644 --- a/pyswarms/utils/plotters/plotters.py +++ b/pyswarms/utils/plotters/plotters.py @@ -65,15 +65,17 @@ speed of animation. """ +# Import standard library import logging +# Import modules import matplotlib.pyplot as plt import numpy as np from matplotlib import animation, cm from mpl_toolkits.mplot3d import Axes3D -from .formatters import Designer, Animator from ..reporter import Reporter +from .formatters import Animator, Designer rep = Reporter(logger=logging.getLogger(__name__)) diff --git a/pyswarms/utils/reporter/reporter.py b/pyswarms/utils/reporter/reporter.py index 01853c5d..8e02894d 100644 --- a/pyswarms/utils/reporter/reporter.py +++ b/pyswarms/utils/reporter/reporter.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- -import os -import yaml -import pprint +# Import standard library import logging import logging.config +import os +import pprint + +# Import modules +import yaml from tqdm import trange @@ -17,7 +20,7 @@ class Reporter(object): from pyswarms.utils import Reporter rep = Reporter() - rep.log("Here's my message", lvl=20) + rep.log("Here's my message", lvl=logging.INFO) This will set-up a reporter with a default configuration that logs to a file, `report.log`, on the current working directory. @@ -29,7 +32,7 @@ class Reporter(object): from pyswarms.utils import Reporter rep = Reporter(log_path="/path/to/log/file.log") - rep.log("Here's my message", lvl=20) + rep.log("Here's my message", lvl=logging.INFO) If you are working on a module and you have an existing logger, you can pass that logger instance during initialization: @@ -52,7 +55,7 @@ class Reporter(object): from pyswarms.utils import Reporter rep = Reporter(config_path="/path/to/config/file.yml") - rep.log("Here's my message", lvl=20) + rep.log("Here's my message", lvl=logging.INFO) """ @@ -114,7 +117,7 @@ def __init__( } self._setup_logger(config_path) - def log(self, msg, lvl=20, *args, **kwargs): + def log(self, msg, lvl=logging.INFO, *args, **kwargs): """Log a message within a set level This method abstracts the logging.Logger.log() method. We use this diff --git a/pyswarms/utils/search/base_search.py b/pyswarms/utils/search/base_search.py index 9bb2cbae..6533ffad 100644 --- a/pyswarms/utils/search/base_search.py +++ b/pyswarms/utils/search/base_search.py @@ -2,11 +2,9 @@ """Base class for hyperparameter optimization search functions""" # Import from __future__ -from __future__ import with_statement -from __future__ import absolute_import -from __future__ import print_function +from __future__ import absolute_import, print_function, with_statement -# Import modules +# Import standard library import operator as op diff --git a/pyswarms/utils/search/grid_search.py b/pyswarms/utils/search/grid_search.py index 1229fbea..62fe47ee 100644 --- a/pyswarms/utils/search/grid_search.py +++ b/pyswarms/utils/search/grid_search.py @@ -29,13 +29,12 @@ """ # Import from __future__ -from __future__ import with_statement -from __future__ import absolute_import -from __future__ import print_function +from __future__ import absolute_import, print_function, with_statement -# Import modules +# Import standard library import itertools +# Import from pyswarms # Import from package from pyswarms.utils.search.base_search import SearchBase diff --git a/pyswarms/utils/search/random_search.py b/pyswarms/utils/search/random_search.py index e04e9119..879ece7a 100644 --- a/pyswarms/utils/search/random_search.py +++ b/pyswarms/utils/search/random_search.py @@ -31,14 +31,13 @@ """ # Import from __future__ -from __future__ import with_statement -from __future__ import absolute_import -from __future__ import print_function +from __future__ import absolute_import, print_function, with_statement # Import modules import numpy as np from past.builtins import xrange +# Import from pyswarms # Import from package from pyswarms.utils.search.base_search import SearchBase diff --git a/setup.py b/setup.py index f8574a13..b8a412f0 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,8 @@ """The setup script.""" -from setuptools import setup, find_packages +# Import modules +from setuptools import find_packages, setup with open("README.md", encoding="utf8") as readme_file: readme = readme_file.read() diff --git a/tests/backend/conftest.py b/tests/backend/conftest.py index bccc1047..90b19e6b 100644 --- a/tests/backend/conftest.py +++ b/tests/backend/conftest.py @@ -4,9 +4,10 @@ """Fixtures for tests""" # Import modules -import pytest import numpy as np +import pytest +# Import from pyswarms # Import from package from pyswarms.backend.swarms import Swarm diff --git a/tests/backend/test_generators.py b/tests/backend/test_generators.py index 6c48c56c..89586604 100644 --- a/tests/backend/test_generators.py +++ b/tests/backend/test_generators.py @@ -2,70 +2,115 @@ # -*- coding: utf-8 -*- # Import modules -import pytest import numpy as np +import pytest -# Import from package +# Import from pyswarms import pyswarms.backend as P -@pytest.mark.parametrize( - "bounds", [None, ([2, 2, 2], [5, 5, 5]), ([-1, -1, 0], [2, 2, 5])] -) -@pytest.mark.parametrize("center", [1, [3, 3, 3], [0.2, 0.2, 0.1]]) -def test_generate_swarm_return_values(bounds, center): - """Tests if generate_swarm() returns expected values""" - pos = P.generate_swarm( - n_particles=2, dimensions=3, bounds=bounds, center=center +class TestGenerateSwarm(object): + """Test suite for generate_swarm() method""" + + @pytest.mark.parametrize( + "bounds", [None, ([2, 2, 2], [5, 5, 5]), ([-1, -1, 0], [2, 2, 5])] ) - if bounds is None: - min_bounds, max_bounds = (0.0, 1.00) - else: - min_bounds, max_bounds = bounds - lower_bound = center * np.array(min_bounds) - upper_bound = center * np.array(max_bounds) - assert (pos <= upper_bound).all() and (pos >= lower_bound).all() - - -def test_generate_swarm_out_of_bounds(): - """Tests if generate_swarm() raises ValueError when initialized with the wrong value""" - bounds = ([1, 1, 1], [5, 5, 5]) - init_pos = np.array([[-2, 3, 3], [6, 8, 1]]) - with pytest.raises(ValueError): + @pytest.mark.parametrize("center", [1, [3, 3, 3], [0.2, 0.2, 0.1]]) + def test_return_values(self, bounds, center): + """Test if method returns expected values""" pos = P.generate_swarm( - n_particles=2, dimensions=3, bounds=bounds, init_pos=init_pos + n_particles=2, dimensions=3, bounds=bounds, center=center ) + if bounds is None: + min_bounds, max_bounds = (0.0, 1.00) + else: + min_bounds, max_bounds = bounds + lower_bound = center * np.array(min_bounds) + upper_bound = center * np.array(max_bounds) + assert (pos <= upper_bound).all() and (pos >= lower_bound).all() + def test_out_of_bounds(self): + """Test if method raises ValueError when initialized with the wrong value""" + bounds = ([1, 1, 1], [5, 5, 5]) + init_pos = np.array([[-2, 3, 3], [6, 8, 1]]) + with pytest.raises(ValueError): + P.generate_swarm( + n_particles=2, dimensions=3, bounds=bounds, init_pos=init_pos + ) -@pytest.mark.parametrize("binary", [False, True]) -def test_generate_discrete_binary_swarm(binary): - """Tests if generate_discrete_swarm(binary=True) returns expected values""" - dims = 3 - pos = P.generate_discrete_swarm( - n_particles=2, dimensions=dims, binary=binary + @pytest.mark.parametrize("bounds", [0.1]) + def test_bounds_wrong_type(self, bounds): + """Test if method raises TypeError when bounds is not an array""" + with pytest.raises(TypeError): + P.generate_swarm(n_particles=2, dimensions=3, bounds=bounds) + + @pytest.mark.parametrize( + "bounds", [(1, 1, 1), ([1, 1, 1]), ([1, 1, 1], [2, 2])] ) - if binary: - assert len(np.unique(pos)) <= 2 # Might generate pure 0 or 1 - else: - assert (np.max(pos, axis=1) == dims - 1).all() - - -@pytest.mark.parametrize("init_pos", [None, np.array([[4, 2, 1], [1, 4, 6]])]) -def test_generate_discrete_swarm(init_pos): - """Tests if init_pos actually sets the position properly""" - dims = 3 - pos = P.generate_discrete_swarm( - n_particles=2, dimensions=dims, init_pos=init_pos + def test_bounds_wrong_size(self, bounds): + """Test if method raises ValueError when bounds is of wrong shape""" + with pytest.raises(ValueError): + P.generate_swarm(n_particles=2, dimensions=3, bounds=bounds) + + +class TestDiscreteSwarm(object): + """Test suite for generate_discrete_swarm() method""" + + @pytest.mark.parametrize("binary", [False, True]) + def test_generate_discrete_binary_swarm(self, binary): + """Test if binary=True returns expected values""" + dims = 3 + pos = P.generate_discrete_swarm( + n_particles=2, dimensions=dims, binary=binary + ) + if binary: + assert len(np.unique(pos)) <= 2 # Might generate pure 0 or 1 + else: + assert (np.max(pos, axis=1) == dims - 1).all() + + def test_not_binary_error_discrete_swarm(self): + """Test if method raises ValueError given wrong init_pos val""" + init_pos = [0, 1, 2] + with pytest.raises(ValueError): + P.generate_discrete_swarm( + n_particles=2, dimensions=3, binary=True, init_pos=init_pos + ) + + @pytest.mark.parametrize( + "init_pos", [None, np.array([[4, 2, 1], [1, 4, 6]])] ) - if init_pos is None: - assert (np.max(pos, axis=1) == dims - 1).all() - else: - assert np.equal(pos, init_pos).all() - - -@pytest.mark.parametrize("clamp", [None, (0, 1), (2, 5), (1, 6)]) -def test_generate_velocity_return_values(clamp): - """Tests if generate_velocity() returns expected values""" - min_clamp, max_clamp = (0, 1) if clamp == None else clamp - velocity = P.generate_velocity(n_particles=2, dimensions=3, clamp=clamp) - assert (velocity <= max_clamp).all() and (velocity >= min_clamp).all() + def test_generate_discrete_swarm(self, init_pos): + """Test if init_pos actually sets the position properly""" + dims = 3 + pos = P.generate_discrete_swarm( + n_particles=2, dimensions=dims, init_pos=init_pos + ) + if init_pos is None: + assert (np.max(pos, axis=1) == dims - 1).all() + else: + assert np.equal(pos, init_pos).all() + + +class TestGenerateVelocity(object): + """Test suite for generate_velocity()""" + + @pytest.mark.parametrize("clamp", [None, (0, 1), (2, 5), (1, 6)]) + def test_return_values(self, clamp): + """Test if the method returns expected values""" + min_clamp, max_clamp = (0, 1) if clamp is None else clamp + velocity = P.generate_velocity( + n_particles=2, dimensions=3, clamp=clamp + ) + assert (velocity <= max_clamp).all() and (velocity >= min_clamp).all() + + @pytest.mark.parametrize("clamp", [(0, 2, 5), [1, 3, 5]]) + def test_invalid_clamp_value(self, clamp): + """Test if the method raises a ValueError given invalid clamp size""" + with pytest.raises(ValueError): + P.generate_velocity(n_particles=2, dimensions=3, clamp=clamp) + + @pytest.mark.parametrize("clamp", [0, 1]) + def test_invalid_clamp_type(self, clamp): + """Test if method raises a TypeError given invalid clamp type""" + with pytest.raises(TypeError): + P.generate_velocity(n_particles=2, dimensions=3, clamp=clamp) diff --git a/tests/backend/test_operators.py b/tests/backend/test_operators.py index 7fdbe9aa..224dcc04 100644 --- a/tests/backend/test_operators.py +++ b/tests/backend/test_operators.py @@ -2,38 +2,73 @@ # -*- coding: utf-8 -*- # Import modules -import pytest import numpy as np +import pytest -# Import from package +# Import from pyswarms import pyswarms.backend as P -def test_compute_pbest_return_values(swarm): - """Test if compute_pbest() gives the expected return values""" - expected_cost = np.array([1, 2, 2]) - expected_pos = np.array([[1, 2, 3], [4, 5, 6], [1, 1, 1]]) - pos, cost = P.compute_pbest(swarm) - assert (pos == expected_pos).all() - assert (cost == expected_cost).all() - - -@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp): - """Test if compute_velocity() gives the expected shape and range""" - v = P.compute_velocity(swarm, clamp) - assert v.shape == swarm.position.shape - if clamp is not None: - assert (clamp[0] <= v).all() and (clamp[1] >= v).all() - - -@pytest.mark.parametrize( - "bounds", - [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], -) -def test_compute_position_return_values(swarm, bounds): - """Test if compute_position() gives the expected shape and range""" - p = P.compute_position(swarm, bounds) - assert p.shape == swarm.velocity.shape - if bounds is not None: - assert (bounds[0] <= p).all() and (bounds[1] >= p).all() +class TestComputePbest(object): + """Test suite for compute_pbest()""" + + def test_return_values(self, swarm): + """Test if method gives the expected return values""" + expected_cost = np.array([1, 2, 2]) + expected_pos = np.array([[1, 2, 3], [4, 5, 6], [1, 1, 1]]) + pos, cost = P.compute_pbest(swarm) + assert (pos == expected_pos).all() + assert (cost == expected_cost).all() + + @pytest.mark.parametrize("swarm", [0, (1, 2, 3)]) + def test_input_swarm(self, swarm): + """Test if method raises AttributeError with wrong swarm""" + with pytest.raises(AttributeError): + P.compute_pbest(swarm) + + +class TestComputeVelocity(object): + """Test suite for compute_velocity()""" + + @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) + def test_return_values(self, swarm, clamp): + """Test if method gives the expected shape and range""" + v = P.compute_velocity(swarm, clamp) + assert v.shape == swarm.position.shape + if clamp is not None: + assert (clamp[0] <= v).all() and (clamp[1] >= v).all() + + @pytest.mark.parametrize("swarm", [0, (1, 2, 3)]) + def test_input_swarm(self, swarm): + """Test if method raises AttributeError with wrong swarm""" + with pytest.raises(AttributeError): + P.compute_velocity(swarm, clamp=(0, 1)) + + @pytest.mark.parametrize("options", [{"c1": 0.5, "c2": 0.3}]) + def test_missing_kwargs(self, swarm, options): + """Test if method raises KeyError with missing kwargs""" + with pytest.raises(KeyError): + swarm.options = options + clamp = (0, 1) + P.compute_velocity(swarm, clamp) + + +class TestComputePosition(object): + """Test suite for compute_position()""" + + @pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], + ) + def test_return_values(self, swarm, bounds): + """Test if method gives the expected shape and range""" + p = P.compute_position(swarm, bounds) + assert p.shape == swarm.velocity.shape + if bounds is not None: + assert (bounds[0] <= p).all() and (bounds[1] >= p).all() + + @pytest.mark.parametrize("swarm", [0, (1, 2, 3)]) + def test_input_swarm(self, swarm): + """Test if method raises AttributeError with wrong swarm""" + with pytest.raises(AttributeError): + P.compute_position(swarm, bounds=([-5, -5], [5, 5])) diff --git a/tests/backend/topology/abc_test_topology.py b/tests/backend/topology/abc_test_topology.py new file mode 100644 index 00000000..f91c5701 --- /dev/null +++ b/tests/backend/topology/abc_test_topology.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import standard library +import abc + +# Import modules +import pytest + + +class ABCTestTopology(abc.ABC): + """Abstract class that defines various tests for topologies + + Whenever a topology inherits from ABCTestTopology, + you don't need to write down all tests anymore. Instead, you can just + specify all required fixtures in the test suite. + """ + + @pytest.fixture + def topology(self): + """Return an instance of the topology""" + raise NotImplementedError("NotImplementedError::topology") + + @pytest.fixture + def options(self): + """Return a dictionary of options""" + raise NotImplementedError("NotImplementedError::options") + + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) + def test_compute_velocity_return_values( + self, topology, swarm, clamp, static + ): + """Test if compute_velocity() gives the expected shape and range""" + topo = topology(static=static) + v = topo.compute_velocity(swarm, clamp) + assert v.shape == swarm.position.shape + if clamp is not None: + assert (clamp[0] <= v).all() and (clamp[1] >= v).all() + + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], + ) + def test_compute_position_return_values( + self, topology, swarm, bounds, static + ): + """Test if compute_position() gives the expected shape and range""" + topo = topology(static=static) + p = topo.compute_position(swarm, bounds) + assert p.shape == swarm.velocity.shape + if bounds is not None: + assert (bounds[0] <= p).all() and (bounds[1] >= p).all() + + @pytest.mark.parametrize("static", [True, False]) + def test_neighbor_idx(self, topology, options, swarm, static): + """Test if the neighbor_idx attribute is assigned""" + topo = topology(static=static) + topo.compute_gbest(swarm, **options) + assert topo.neighbor_idx is not None + + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize("swarm", [0, (1, 2, 3)]) + def test_input_swarm(self, topology, static, swarm, options): + """Test if AttributeError is raised when passed with a non-Swarm instance""" + with pytest.raises(AttributeError): + topo = topology(static=static) + topo.compute_gbest(swarm, **options) diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index be5a5014..1ddcdeb9 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -4,9 +4,10 @@ """Fixtures for tests""" # Import modules -import pytest import numpy as np +import pytest +# Import from pyswarms # Import from package from pyswarms.backend.swarms import Swarm @@ -16,39 +17,39 @@ def swarm(): """A contrived instance of the Swarm class at a certain timestep""" # fmt: off attrs_at_t = { - "position": np.array([[9.95838686e-01, 5.87433429e-04, 6.49113772e-03], - [1.00559609e+00, 3.96477697e-02, 7.67205397e-01], - [2.87990950e-01, -3.64932609e-02, 1.89750725e-02], - [1.11646877e+00, 3.12037361e-03, 1.97885369e-01], - [8.96117216e-01, -9.79602053e-03, -1.66139336e-01], - [9.90423669e-01, 1.99307974e-03, -1.23386797e-02], - [2.06800701e-01, -1.67869387e-02, 1.14268810e-01], - [4.21786494e-01, 2.58755510e-02, 6.62254843e-01], - [9.90350831e-01, 3.81575154e-03, 8.80833545e-01], - [9.94353749e-01, -4.85086205e-02, 9.85313500e-03]]), - "velocity": np.array([[2.09076818e-02, 2.04936403e-03, 1.06761248e-02], - [1.64940497e-03, 5.67924469e-03, 9.74902301e-02], - [1.50445516e-01, 9.11699158e-03, 1.51474794e-02], - [2.94238740e-01, 5.71545680e-04, 1.54122294e-02], - [4.10430034e-02, 6.51847479e-04, 6.25109226e-02], + "position": np.array([[40.95838686e-01, 5.87433429e-04, 6.49113772e-03], + [1.00559609e+00, 3.96477697e-02, 7.67205397e-01], + [8.87990950e-01, -3.64932609e-02, 1.89750725e-02], + [1.11646877e+00, 3.12037361e-03, 1.97885369e-01], + [8.96117216e-01, -9.79602053e-03, -1.66139336e-01], + [9.90423669e-01, 1.99307974e-03, -1.23386797e-02], + [2.06800701e-01, -1.67869387e-02, 1.14268810e-01], + [4.21786494e-01, 2.58755510e-02, 6.62254843e-01], + [9.90350831e-01, 3.81575154e-03, 8.80833545e-01], + [9.94353749e-01, -4.85086205e-02, 9.85313500e-03]]), + "velocity": np.array([[2.09076818e-02, 2.04936403e-03, 1.06761248e-02], + [1.64940497e-03, 5.67924469e-03, 9.74902301e-02], + [1.50445516e-01, 9.11699158e-03, 1.51474794e-02], + [2.94238740e-01, 5.71545680e-04, 1.54122294e-02], + [4.10430034e-02, 6.51847479e-04, 6.25109226e-02], [6.71076116e-06, 1.89615516e-04, 4.65023770e-03], - [4.76081378e-02, 4.24416089e-03, 7.11856172e-02], - [1.33832808e-01, 1.81818698e-02, 1.16947941e-01], - [1.22849955e-03, 1.55685312e-03, 1.67819003e-02], - [5.60617396e-03, 4.31819608e-02, 2.52217220e-02]]), - "current_cost": np.array([1.07818462, 5.5647911, 19.6046078, 14.05300016, 3.72597614, 1.01169386, - 16.51846203, 32.72262829, 3.80274901, 1.05237138]), + [4.76081378e-02, 4.24416089e-03, 7.11856172e-02], + [1.33832808e-01, 1.81818698e-02, 1.16947941e-01], + [1.22849955e-03, 1.55685312e-03, 1.67819003e-02], + [5.60617396e-03, 4.31819608e-02, 2.52217220e-02]]), + "current_cost": np.array([1.07818462, 5.5647911, 19.6046078, 14.05300016, 3.72597614, 1.01169386, + 16.51846203, 32.72262829, 3.80274901, 1.05237138]), "pbest_cost": np.array([1.00362006, 2.39151041, 2.55208424, 5.00176207, 1.04510827, 1.00025284, 6.31216654, 2.53873121, 2.00530884, 1.05237138]), - "pbest_pos": np.array([[9.98033031e-01, 4.97392619e-03, 3.07726256e-03], - [1.00665809e+00, 4.22504014e-02, 9.84334657e-01], - [1.12159389e-02, 1.11429739e-01, 2.86388193e-02], - [1.64059236e-01, 6.85791237e-03, -2.32137604e-02], + "pbest_pos": np.array([[9.98033031e-01, 4.97392619e-03, 3.07726256e-03], + [1.00665809e+00, 4.22504014e-02, 9.84334657e-01], + [1.12159389e-02, 1.11429739e-01, 2.86388193e-02], + [1.64059236e-01, 6.85791237e-03, -2.32137604e-02], [9.93740665e-01, -6.16501403e-03, -1.46096578e-02], - [9.90438476e-01, 2.50379538e-03, 1.87405987e-05], - [1.12301876e-01, 1.77099784e-03, 1.45382457e-01], - [4.41204876e-02, 4.84059652e-02, 1.05454822e+00], - [9.89348409e-01, -1.31692358e-03, 9.88291764e-01], + [9.90438476e-01, 2.50379538e-03, 1.87405987e-05], + [1.12301876e-01, 1.77099784e-03, 1.45382457e-01], + [4.41204876e-02, 4.84059652e-02, 1.05454822e+00], + [9.89348409e-01, -1.31692358e-03, 9.88291764e-01], [9.99959923e-01, -5.32665972e-03, -1.53685870e-02]]), "best_cost": 1.0002528364353296, "best_pos": np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]), diff --git a/tests/backend/topology/test_pyramid.py b/tests/backend/topology/test_pyramid.py index cce94e19..4def9bbd 100644 --- a/tests/backend/topology/test_pyramid.py +++ b/tests/backend/topology/test_pyramid.py @@ -2,52 +2,34 @@ # -*- coding: utf-8 -*- # Import modules -import pytest import numpy as np +import pytest -# Import from package +# Import from pyswarms from pyswarms.backend.topology import Pyramid - -@pytest.mark.parametrize("static", [True, False]) -def test_compute_gbest_return_values(swarm, static): - """Test if compute_gbest() gives the expected return values""" - topology = Pyramid(static=static) - expected_cost = 1.0002528364353296 - expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) - pos, cost = topology.compute_gbest(swarm) - assert cost == pytest.approx(expected_cost) - assert pos == pytest.approx(expected_pos) - - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp, static): - """Test if compute_velocity() gives the expected shape and range""" - topology = Pyramid(static=static) - v = topology.compute_velocity(swarm, clamp) - assert v.shape == swarm.position.shape - if clamp is not None: - assert (clamp[0] <= v).all() and (clamp[1] >= v).all() - - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize( - "bounds", - [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], -) -def test_compute_position_return_values(swarm, bounds, static): - """Test if compute_position() gives the expected shape and range""" - topology = Pyramid(static=static) - p = topology.compute_position(swarm, bounds) - assert p.shape == swarm.velocity.shape - if bounds is not None: - assert (bounds[0] <= p).all() and (bounds[1] >= p).all() - - -@pytest.mark.parametrize("static", [True, False]) -def test_neighbor_idx(swarm, static): - """Test if the neighbor_idx attribute is assigned""" - topology = Pyramid(static=static) - topology.compute_gbest(swarm) - assert topology.neighbor_idx is not None +from .abc_test_topology import ABCTestTopology + + +class TestPyramidTopology(ABCTestTopology): + @pytest.fixture + def topology(self): + return Pyramid + + @pytest.fixture + def options(self): + return {} + + @pytest.mark.parametrize("static", [True, False]) + def test_compute_gbest_return_values( + self, swarm, topology, options, static + ): + """Test if compute_gbest() gives the expected return values""" + topo = topology(static=static) + expected_cost = 1.0002528364353296 + expected_pos = np.array( + [9.90438476e-01, 2.50379538e-03, 1.87405987e-05] + ) + pos, cost = topo.compute_gbest(swarm, **options) + assert cost == pytest.approx(expected_cost) + assert pos == pytest.approx(expected_pos) diff --git a/tests/backend/topology/test_random.py b/tests/backend/topology/test_random.py index 430c9be7..1ee99339 100644 --- a/tests/backend/topology/test_random.py +++ b/tests/backend/topology/test_random.py @@ -2,86 +2,69 @@ # -*- coding: utf-8 -*- # Import modules -import pytest import numpy as np +import pytest -# Import from package +# Import from pyswarms from pyswarms.backend.topology import Random - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("k", [1, 2]) -def test_update_gbest_neighborhood(swarm, k, static): - """Test if update_gbest_neighborhood gives the expected return values""" - topology = Random(static=static, k=k) - pos, cost = topology.compute_gbest(swarm) - expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) - expected_cost = 1.0002528364353296 - assert cost == pytest.approx(expected_cost) - assert pos == pytest.approx(expected_pos) - - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp, static): - """Test if compute_velocity() gives the expected shape and range""" - topology = Random(static=static, k=1) - v = topology.compute_velocity(swarm, clamp) - assert v.shape == swarm.position.shape - if clamp is not None: - assert (clamp[0] <= v).all() and (clamp[1] >= v).all() - - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize( - "bounds", - [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], -) -def test_compute_position_return_values(swarm, bounds, static): - """Test if compute_position() gives the expected shape and range""" - topology = Random(static=static, k=1) - p = topology.compute_position(swarm, bounds) - assert p.shape == swarm.velocity.shape - if bounds is not None: - assert (bounds[0] <= p).all() and (bounds[1] >= p).all() +from .abc_test_topology import ABCTestTopology -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("k", [1, 2]) -def test_compute_neighbors_return_values(swarm, k, static): - """Test if __compute_neighbors() gives the expected shape and symmetry""" - topology = Random(static=static, k=k) - adj_matrix = topology._Random__compute_neighbors(swarm, k=k) - assert adj_matrix.shape == (swarm.n_particles, swarm.n_particles) - assert np.allclose(adj_matrix, adj_matrix.T, atol=1e-8) # Symmetry test +class TestRandomTopology(ABCTestTopology): + @pytest.fixture + def topology(self): + return Random + @pytest.fixture(params=[1, 2, 3]) + def options(self, request): + return {"k": request.param} -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("k", [1]) -def test_compute_neighbors_adjacency_matrix(swarm, k, static): - """Test if __compute_neighbors() gives the expected matrix""" - np.random.seed(1) - topology = Random(static=static, k=k) - adj_matrix = topology._Random__compute_neighbors(swarm, k=k) - # fmt: off - comparison_matrix = np.array([[1, 1, 1, 0, 1, 0, 0, 0, 0, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 1, 1, 1, 1, 0, 0, 0, 1, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 1, 1, 0, 1, 1, 0, 1, 0, 1], - [0, 1, 1, 0, 1, 0, 1, 0, 1, 1], - [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], - [0, 1, 1, 1, 1, 0, 1, 1, 1, 0], - [1, 1, 1, 0, 1, 1, 1, 0, 0, 1]]) - assert np.allclose(adj_matrix, comparison_matrix, atol=1e-8) - # fmt: on + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize("k", [1, 2]) + def test_compute_gbest_return_values( + self, swarm, options, topology, k, static + ): + """Test if update_gbest_neighborhood gives the expected return values""" + topo = topology(static=static) + pos, cost = topo.compute_gbest(swarm, **options) + expected_pos = np.array( + [9.90438476e-01, 2.50379538e-03, 1.87405987e-05] + ) + expected_cost = 1.0002528364353296 + assert cost == pytest.approx(expected_cost) + assert pos == pytest.approx(expected_pos) + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize("k", [1, 2]) + def test_compute_neighbors_return_values(self, swarm, topology, k, static): + """Test if __compute_neighbors() gives the expected shape and symmetry""" + topo = topology(static=static) + adj_matrix = topo._Random__compute_neighbors(swarm, k=k) + assert adj_matrix.shape == (swarm.n_particles, swarm.n_particles) + assert np.allclose( + adj_matrix, adj_matrix.T, atol=1e-8 + ) # Symmetry test -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("k", [1]) -def test_neighbor_idx(swarm, k, static): - """Test if the neighbor_idx attribute is assigned""" - topology = Random(static=static, k=k) - topology.compute_gbest(swarm) - assert topology.neighbor_idx is not None + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize("k", [1]) + def test_compute_neighbors_adjacency_matrix( + self, swarm, topology, k, static + ): + """Test if __compute_neighbors() gives the expected matrix""" + np.random.seed(1) + topo = topology(static=static) + adj_matrix = topo._Random__compute_neighbors(swarm, k=k) + # fmt: off + comparison_matrix = np.array([[1, 1, 1, 0, 1, 0, 0, 0, 0, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 1, 1, 1, 1, 0, 0, 0, 1, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 1, 1, 0, 1, 1, 0, 1, 0, 1], + [0, 1, 1, 0, 1, 0, 1, 0, 1, 1], + [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], + [0, 1, 1, 1, 1, 0, 1, 1, 1, 0], + [1, 1, 1, 0, 1, 1, 1, 0, 0, 1]]) + assert np.allclose(adj_matrix, comparison_matrix, atol=1e-8) + # fmt: on diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index 0c4444ec..d1288f07 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -2,56 +2,35 @@ # -*- coding: utf-8 -*- # Import modules -import pytest import numpy as np +import pytest -# Import from package +# Import from pyswarms from pyswarms.backend.topology import Ring - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("k", [i for i in range(1, 10)]) -@pytest.mark.parametrize("p", [1, 2]) -def test_update_gbest_neighborhood(swarm, p, k, static): - """Test if update_gbest_neighborhood gives the expected return values""" - topology = Ring(static=static, p=p, k=k) - pos, cost = topology.compute_gbest(swarm) - expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) - expected_cost = 1.0002528364353296 - assert cost == pytest.approx(expected_cost) - assert pos == pytest.approx(expected_pos) - - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp, static): - """Test if compute_velocity() gives the expected shape and range""" - topology = Ring(static=static, p=1, k=2) - v = topology.compute_velocity(swarm, clamp) - assert v.shape == swarm.position.shape - if clamp is not None: - assert (clamp[0] <= v).all() and (clamp[1] >= v).all() - - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize( - "bounds", - [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], -) -def test_compute_position_return_values(swarm, bounds, static): - """Test if compute_position() gives the expected shape and range""" - topology = Ring(static=static, p=1, k=2) - p = topology.compute_position(swarm, bounds) - assert p.shape == swarm.velocity.shape - if bounds is not None: - assert (bounds[0] <= p).all() and (bounds[1] >= p).all() - - -@pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("k", [1, 2, 3]) -@pytest.mark.parametrize("p", [1, 2]) -def test_neighbor_idx(swarm, static, p, k): - """Test if the neighbor_idx attribute is assigned""" - topology = Ring(static=static, p=p, k=k) - topology.compute_gbest(swarm) - assert topology.neighbor_idx is not None +from .abc_test_topology import ABCTestTopology + + +class TestRingTopology(ABCTestTopology): + @pytest.fixture + def topology(self): + return Ring + + @pytest.fixture(params=[(1, 2), (2, 3)]) + def options(self, request): + p, k = request.param + return {"p": p, "k": k} + + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize("k", [i for i in range(1, 10)]) + @pytest.mark.parametrize("p", [1, 2]) + def test_compute_gbest_return_values(self, swarm, topology, p, k, static): + """Test if update_gbest_neighborhood gives the expected return values""" + topo = topology(static=static) + pos, cost = topo.compute_gbest(swarm, p=p, k=k) + expected_pos = np.array( + [9.90438476e-01, 2.50379538e-03, 1.87405987e-05] + ) + expected_cost = 1.0002528364353296 + assert cost == pytest.approx(expected_cost) + assert pos == pytest.approx(expected_pos) diff --git a/tests/backend/topology/test_star.py b/tests/backend/topology/test_star.py index 957ecdad..3c16a99a 100644 --- a/tests/backend/topology/test_star.py +++ b/tests/backend/topology/test_star.py @@ -2,48 +2,31 @@ # -*- coding: utf-8 -*- # Import modules -import pytest import numpy as np +import pytest -# Import from package +# Import from pyswarms from pyswarms.backend.topology import Star - -def test_compute_gbest_return_values(swarm): - """Test if compute_gbest() gives the expected return values""" - topology = Star() - expected_cost = 1.0002528364353296 - expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) - pos, cost = topology.compute_gbest(swarm) - assert cost == pytest.approx(expected_cost) - assert pos == pytest.approx(expected_pos) - - -@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp): - """Test if compute_velocity() gives the expected shape and range""" - topology = Star() - v = topology.compute_velocity(swarm, clamp) - assert v.shape == swarm.position.shape - if clamp is not None: - assert (clamp[0] <= v).all() and (clamp[1] >= v).all() +from .abc_test_topology import ABCTestTopology -@pytest.mark.parametrize( - "bounds", - [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], -) -def test_compute_position_return_values(swarm, bounds): - """Test if compute_position() gives the expected shape and range""" - topology = Star() - p = topology.compute_position(swarm, bounds) - assert p.shape == swarm.velocity.shape - if bounds is not None: - assert (bounds[0] <= p).all() and (bounds[1] >= p).all() +class TestStarTopology(ABCTestTopology): + @pytest.fixture + def topology(self): + return Star + @pytest.fixture + def options(self): + return {} -def test_neighbor_idx(swarm): - """Test if the neighbor_idx attribute is assigned""" - topology = Star() - topology.compute_gbest(swarm) - assert topology.neighbor_idx is not None + def test_compute_gbest_return_values(self, swarm, options, topology): + """Test if compute_gbest() gives the expected return values""" + topo = topology() + expected_cost = 1.0002528364353296 + expected_pos = np.array( + [9.90438476e-01, 2.50379538e-03, 1.87405987e-05] + ) + pos, cost = topo.compute_gbest(swarm, **options) + assert cost == pytest.approx(expected_cost) + assert pos == pytest.approx(expected_pos) diff --git a/tests/backend/topology/test_von_neumann.py b/tests/backend/topology/test_von_neumann.py index 860de4f8..8bca028c 100644 --- a/tests/backend/topology/test_von_neumann.py +++ b/tests/backend/topology/test_von_neumann.py @@ -2,66 +2,39 @@ # -*- coding: utf-8 -*- # Import modules -import pytest import numpy as np +import pytest -# Import from package +# Import from pyswarms from pyswarms.backend.topology import VonNeumann - -@pytest.mark.parametrize("r", [0, 1]) -@pytest.mark.parametrize("p", [1, 2]) -def test_update_gbest_neighborhood(swarm, p, r): - """Test if update_gbest_neighborhood gives the expected return values""" - topology = VonNeumann(p=p, r=r) - pos, cost = topology.compute_gbest(swarm) - expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) - expected_cost = 1.0002528364353296 - assert cost == pytest.approx(expected_cost) - assert pos == pytest.approx(expected_pos) - - -@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp): - """Test if compute_velocity() gives the expected shape and range""" - topology = VonNeumann(p=1, r=3) - v = topology.compute_velocity(swarm, clamp) - assert v.shape == swarm.position.shape - if clamp is not None: - assert (clamp[0] <= v).all() and (clamp[1] >= v).all() - - -@pytest.mark.parametrize( - "bounds", - [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], -) -def test_compute_position_return_values(swarm, bounds): - """Test if compute_position() gives the expected shape and range""" - topology = VonNeumann(p=1, r=2) - p = topology.compute_position(swarm, bounds) - assert p.shape == swarm.velocity.shape - if bounds is not None: - assert (bounds[0] <= p).all() and (bounds[1] >= p).all() - - -@pytest.mark.parametrize("r", [0, 1]) -@pytest.mark.parametrize("p", [1, 2]) -def test_neighbor_idx(swarm, p, r): - """Test if the neighbor_idx attribute is assigned""" - topology = VonNeumann(p=p, r=r) - topology.compute_gbest(swarm) - assert topology.neighbor_idx is not None - - -@pytest.mark.parametrize("m", [i for i in range(9)]) -@pytest.mark.parametrize("n", [i for i in range(10)]) -def test_delannoy_numbers(m, n): - # fmt: off - expected_values = np.array([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 25, - 41, 61, 63, 85, 113, 129, 145, 181, 231, - 321, 377, 575, 681, 833, 1159, 1289, - 1683, 2241, 3649, 3653, 5641, 7183, - 8989, 13073, 19825, 40081, 48639, 75517, - 108545, 22363, 224143, 265729, 598417]) - # fmt: on - assert VonNeumann.delannoy(m, n) in expected_values +from .abc_test_topology import ABCTestTopology + + +class TestVonNeumannTopology(ABCTestTopology): + @pytest.fixture + def topology(self): + return VonNeumann + + @pytest.fixture + def options(self): + return {"p": 1, "r": 1} + + @pytest.mark.parametrize("r", [0, 1]) + @pytest.mark.parametrize("p", [1, 2]) + def test_update_gbest_neighborhood(self, swarm, topology, p, r): + """Test if update_gbest_neighborhood gives the expected return values""" + topo = topology() + pos, cost = topo.compute_gbest(swarm, p=p, r=r) + expected_pos = np.array( + [9.90438476e-01, 2.50379538e-03, 1.87405987e-05] + ) + expected_cost = 1.0002528364353296 + assert cost == pytest.approx(expected_cost) + assert pos == pytest.approx(expected_pos) + + @pytest.mark.parametrize("m", [i for i in range(3)]) + @pytest.mark.parametrize("n", [i for i in range(3)]) + def test_delannoy_numbers(self, m, n): + expected_values = np.array([1, 3, 5, 7, 9, 11, 13, 15, 17]) + assert VonNeumann.delannoy(m, n) in expected_values diff --git a/tests/optimizers/abc_test_discrete_optimizer.py b/tests/optimizers/abc_test_discrete_optimizer.py new file mode 100644 index 00000000..5e498ea1 --- /dev/null +++ b/tests/optimizers/abc_test_discrete_optimizer.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import modules +import numpy as np +import pytest + +from .abc_test_optimizer import ABCTestOptimizer + +# from pyswarms.utils.functions.single_obj import sphere + + +class ABCTestDiscreteOptimizer(ABCTestOptimizer): + """Abstract class that defines various tests for high-level optimizers + + Whenever an optimizer implementation inherits from ABCTestOptimizer, + you don't need to write down all tests anymore. Instead, you can just + specify all required fixtures in the test suite. + """ + + @pytest.mark.skip("No way of testing this yet") + def test_obj_with_kwargs(self, obj_with_args, optimizer, options): + """Test if kwargs are passed properly in objfunc""" + opt = optimizer(100, 2, options=options) + cost, pos = opt.optimize(obj_with_args, 1000, a=1, b=100) + assert np.isclose(cost, 0, rtol=1e-03) + assert np.isclose(pos[0], 1.0, rtol=1e-03) + assert np.isclose(pos[1], 1.0, rtol=1e-03) + + @pytest.mark.skip("No way of testing this yet") + def test_obj_unnecessary_kwargs( + self, obj_without_args, optimizer, options + ): + """Test if error is raised given unnecessary kwargs""" + opt = optimizer(100, 2, options=options) + with pytest.raises(TypeError): + # kwargs `a` should not be supplied + cost, pos = opt.optimize(obj_without_args, 1000, a=1) + + @pytest.mark.skip("No way of testing this yet") + def test_obj_missing_kwargs(self, obj_with_args, optimizer, options): + """Test if error is raised with incomplete kwargs""" + opt = optimizer(100, 2, options=options) + with pytest.raises(TypeError): + # kwargs `b` is missing here + cost, pos = opt.optimize(obj_with_args, 1000, a=1) + + @pytest.mark.skip("No way of testing this yet") + def test_obj_incorrect_kwargs(self, obj_with_args, optimizer, options): + """Test if error is raised with wrong kwargs""" + opt = optimizer(100, 2, options=options) + with pytest.raises(TypeError): + # Wrong kwargs + cost, pos = opt.optimize(obj_with_args, 1000, c=1, d=100) diff --git a/tests/optimizers/abc_test_optimizer.py b/tests/optimizers/abc_test_optimizer.py new file mode 100644 index 00000000..bb0d5a51 --- /dev/null +++ b/tests/optimizers/abc_test_optimizer.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import standard library +import abc + +# Import modules +import numpy as np +import pytest + +# Import from pyswarms +from pyswarms.utils.functions.single_obj import rosenbrock, sphere + + +class ABCTestOptimizer(abc.ABC): + """Abstract class that defines various tests for high-level optimizers + + Whenever an optimizer implementation inherits from ABCTestOptimizer, + you don't need to write down all tests anymore. Instead, you can just + specify all required fixtures in the test suite. + """ + + @pytest.fixture + def optimizer(self): + """Return an instance of the optimizer""" + raise NotImplementedError("NotImplementedError::optimizer") + + @pytest.fixture + def optimizer_history(self): + """Run the optimizer for 1000 iterations and return its instance""" + raise NotImplementedError("NotImplementedError::optimizer_history") + + @pytest.fixture + def optimizer_reset(self): + """Reset the optimizer and return its instance""" + raise NotImplementedError("NotImplementedError::optimizer_reset") + + @pytest.fixture + def options(self): + """Default options dictionary for most PSO use-cases""" + return {"c1": 0.3, "c2": 0.7, "w": 0.9, "k": 2, "p": 2, "r": 1} + + @pytest.fixture + def obj_with_args(self): + """Objective function with arguments""" + + def obj_with_args_(x, a, b): + f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 + return f + + return obj_with_args_ + + @pytest.fixture + def obj_without_args(self): + """Objective function without arguments""" + return rosenbrock + + @pytest.mark.parametrize( + "history, expected_shape", + [ + ("cost_history", (1000,)), + ("mean_pbest_history", (1000,)), + ("mean_neighbor_history", (1000,)), + ("pos_history", (1000, 10, 2)), + ("velocity_history", (1000, 10, 2)), + ], + ) + def test_train_history(self, optimizer_history, history, expected_shape): + """Test if training histories are of expected shape""" + opt = vars(optimizer_history) + assert np.array(opt[history]).shape == expected_shape + + def test_reset_default_values(self, optimizer_reset): + """Test if best cost and best pos are set properly when the reset() + method is called""" + assert optimizer_reset.swarm.best_cost == np.inf + assert set(optimizer_reset.swarm.best_pos) == set(np.array([])) + + def test_ftol_effect(self, options, optimizer): + """Test if setting the ftol breaks the optimization process""" + opt = optimizer(10, 2, options=options, ftol=1e-1) + opt.optimize(sphere, 2000) + assert np.array(opt.cost_history).shape != (2000,) + + def test_obj_with_kwargs(self, obj_with_args, optimizer, options): + """Test if kwargs are passed properly in objfunc""" + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt = optimizer(100, 2, options=options, bounds=bounds) + cost, pos = opt.optimize(obj_with_args, 1000, a=1, b=100) + assert np.isclose(cost, 0, rtol=1e-03) + assert np.isclose(pos[0], 1.0, rtol=1e-03) + assert np.isclose(pos[1], 1.0, rtol=1e-03) + + def test_obj_unnecessary_kwargs( + self, obj_without_args, optimizer, options + ): + """Test if error is raised given unnecessary kwargs""" + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt = optimizer(100, 2, options=options, bounds=bounds) + with pytest.raises(TypeError): + # kwargs `a` should not be supplied + cost, pos = opt.optimize(obj_without_args, 1000, a=1) + + def test_obj_missing_kwargs(self, obj_with_args, optimizer, options): + """Test if error is raised with incomplete kwargs""" + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt = optimizer(100, 2, options=options, bounds=bounds) + with pytest.raises(TypeError): + # kwargs `b` is missing here + cost, pos = opt.optimize(obj_with_args, 1000, a=1) + + def test_obj_incorrect_kwargs(self, obj_with_args, optimizer, options): + """Test if error is raised with wrong kwargs""" + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt = optimizer(100, 2, options=options, bounds=bounds) + with pytest.raises(TypeError): + # Wrong kwargs + cost, pos = opt.optimize(obj_with_args, 1000, c=1, d=100) diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py deleted file mode 100644 index 36a8e300..00000000 --- a/tests/optimizers/conftest.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Fixtures for tests""" - -import pytest - -from pyswarms.backend.topology import Pyramid, Random, Ring, Star, VonNeumann -from pyswarms.discrete import BinaryPSO -from pyswarms.single import GeneralOptimizerPSO, GlobalBestPSO, LocalBestPSO -from pyswarms.utils.functions.single_obj import sphere - - -@pytest.fixture(scope="module") -def general_opt_history(topology): - """Returns a GeneralOptimizerPSO instance run for 1000 iterations for checking - history""" - pso = GeneralOptimizerPSO( - 10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=topology - ) - pso.optimize(sphere, 1000) - return pso - - -@pytest.fixture(scope="module") -def general_opt_reset(topology): - """Returns a GeneralOptimizerPSO instance that has been run and reset to check - default value""" - pso = GeneralOptimizerPSO( - 10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=topology - ) - pso.optimize(sphere, 10, verbose=0) - pso.reset() - return pso - - -@pytest.fixture(scope="module") -def gbest_history(): - """Returns a GlobalBestPSO instance run for 1000 iterations for checking - history""" - pso = GlobalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}) - pso.optimize(sphere, 1000) - return pso - - -@pytest.fixture(scope="module") -def gbest_reset(): - """Returns a GlobalBestPSO instance that has been run and reset to check - default value""" - pso = GlobalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}) - pso.optimize(sphere, 10) - pso.reset() - return pso - - -@pytest.fixture(scope="module") -def lbest_history(): - """Returns a LocalBestPSO instance run for 1000 iterations for checking - history""" - pso = LocalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) - pso.optimize(sphere, 1000) - return pso - - -@pytest.fixture(scope="module") -def lbest_reset(): - """Returns a LocalBestPSO instance that has been run and reset to check - default value""" - pso = LocalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) - pso.optimize(sphere, 10) - pso.reset() - return pso - - -@pytest.fixture(scope="module") -def binary_history(): - """Returns a BinaryPSO instance run for 1000 iterations for checking - history""" - pso = BinaryPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) - pso.optimize(sphere, 1000) - return pso - - -@pytest.fixture(scope="module") -def binary_reset(): - """Returns a BinaryPSO instance that has been run and reset to check - default value""" - pso = BinaryPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) - pso.optimize(sphere, 10) - pso.reset() - return pso - - -@pytest.fixture -def options(): - """Default options dictionary for most PSO use-cases""" - options_ = {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2, "r": 1} - return options_ - - -# fmt: off -@pytest.fixture(params=[ - Star, - Ring, - Pyramid, - Random, - VonNeumann - ]) -# fmt: on -def topology(request): - """Parametrized topology parameter""" - topology_ = request.param - return topology_ diff --git a/tests/optimizers/test_binary.py b/tests/optimizers/test_binary.py index acf533b6..0fca5fc4 100644 --- a/tests/optimizers/test_binary.py +++ b/tests/optimizers/test_binary.py @@ -3,85 +3,28 @@ # Import modules import pytest -import numpy as np -# Import from package +# Import from pyswarms from pyswarms.discrete import BinaryPSO +from pyswarms.utils.functions.single_obj import sphere +from .abc_test_discrete_optimizer import ABCTestDiscreteOptimizer -@pytest.mark.parametrize( - "options", - [ - {"c2": 0.7, "w": 0.5, "k": 2, "p": 2}, - {"c1": 0.5, "w": 0.5, "k": 2, "p": 2}, - {"c1": 0.5, "c2": 0.7, "k": 2, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2}, - ], -) -def test_keyword_exception(options): - """Tests if exceptions are thrown when keywords are missing""" - with pytest.raises(KeyError): - BinaryPSO(5, 2, options) +class TestDiscreteOptimizer(ABCTestDiscreteOptimizer): + @pytest.fixture + def optimizer(self): + return BinaryPSO -@pytest.mark.parametrize( - "options", - [ - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": -1, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 6, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 5}, - ], -) -def test_invalid_k_or_p_values(options): - """Tests if exception is thrown when passing - an invalid value for k or p""" - with pytest.raises(ValueError): - BinaryPSO(5, 2, options) + @pytest.fixture + def optimizer_history(self, options): + opt = BinaryPSO(10, 2, options=options) + opt.optimize(sphere, 1000) + return opt - -@pytest.mark.parametrize("velocity_clamp", [[1, 3], np.array([1, 3])]) -def test_vclamp_type_exception(velocity_clamp, options): - """Tests if exception is raised when velocity_clamp type is not a - tuple""" - with pytest.raises(TypeError): - BinaryPSO(5, 2, velocity_clamp=velocity_clamp, options=options) - - -@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) -def test_vclamp_shape_exception(velocity_clamp, options): - """Tests if exception is raised when velocity_clamp's size is not equal - to 2""" - with pytest.raises(IndexError): - BinaryPSO(5, 2, velocity_clamp=velocity_clamp, options=options) - - -@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) -def test_vclamp_maxmin_exception(velocity_clamp, options): - """Tests if the max velocity_clamp is less than min velocity_clamp and - vice-versa""" - with pytest.raises(ValueError): - BinaryPSO(5, 2, velocity_clamp=velocity_clamp, options=options) - - -def test_reset_default_values(binary_reset): - """Tests if best cost and best pos are set properly when the reset() - method is called""" - assert binary_reset.swarm.best_cost == np.inf - assert set(binary_reset.swarm.best_pos) == set(np.array([])) - - -@pytest.mark.parametrize( - "history, expected_shape", - [ - ("cost_history", (1000,)), - ("mean_pbest_history", (1000,)), - ("mean_neighbor_history", (1000,)), - ("pos_history", (1000, 10, 2)), - ("velocity_history", (1000, 10, 2)), - ], -) -def test_training_history_shape(binary_history, history, expected_shape): - """Test if training histories are of expected shape""" - pso = vars(binary_history) - assert np.array(pso[history]).shape == expected_shape + @pytest.fixture + def optimizer_reset(self, options): + opt = BinaryPSO(10, 2, options=options) + opt.optimize(sphere, 10) + opt.reset() + return opt diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py index 4b6e25be..f42dc621 100644 --- a/tests/optimizers/test_general_optimizer.py +++ b/tests/optimizers/test_general_optimizer.py @@ -1,130 +1,96 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# Import standard library +import inspect + +# Import modules import numpy as np import pytest -from pyswarms.backend.topology import Random, Ring, VonNeumann +# Import from pyswarms +import pyswarms.backend.topology as t from pyswarms.single import GeneralOptimizerPSO from pyswarms.utils.functions.single_obj import sphere +from .abc_test_optimizer import ABCTestOptimizer -@pytest.mark.parametrize("topology", [object(), int(), dict()]) -def test_topology_type_exception(options, topology): - """Tests if exceptions are thrown when the topology has the wrong type""" - with pytest.raises(TypeError): - GeneralOptimizerPSO(5, 2, options, topology) - - -@pytest.mark.parametrize( - "bounds", - [ - tuple(np.array([-5, -5])), - (np.array([-5, -5, -5]), np.array([5, 5])), - (np.array([-5, -5, -5]), np.array([5, 5, 5])), - ], -) -def test_bounds_size_exception(bounds, options, topology): - """Tests if exceptions are raised when bound sizes are wrong""" - with pytest.raises(IndexError): - GeneralOptimizerPSO( - 5, 2, options=options, topology=topology, bounds=bounds - ) +def istopology(x): + """Helper predicate to check if it's a subclass""" + return inspect.isclass(x) and not inspect.isabstract(x) -@pytest.mark.parametrize( - "bounds", - [ - (np.array([5, 5]), np.array([-5, -5])), - (np.array([5, -5]), np.array([-5, 5])), - ], -) -def test_bounds_maxmin_exception(bounds, options, topology): - """Tests if the max bounds is less than min bounds and vice-versa""" - with pytest.raises(ValueError): - GeneralOptimizerPSO( - 5, 2, options=options, topology=topology, bounds=bounds - ) - -@pytest.mark.parametrize( - "bounds", - [ - [np.array([-5, -5]), np.array([5, 5])], - np.array([np.array([-5, -5]), np.array([5, 5])]), - ], -) -def test_bound_type_exception(bounds, options, topology): - """Tests if exception is raised when bound type is not a tuple""" - with pytest.raises(TypeError): - GeneralOptimizerPSO( - 5, 2, options=options, topology=topology, bounds=bounds - ) +# Get all classes in the topology module, then +# Instatiate topologies, no need to suppy static param +topologies = [topo() for _, topo in inspect.getmembers(t, istopology)] -@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) -def test_vclamp_shape_exception(velocity_clamp, options, topology): - """Tests if exception is raised when velocity_clamp's size is not equal - to 2""" - with pytest.raises(IndexError): - GeneralOptimizerPSO( - 5, - 2, - velocity_clamp=velocity_clamp, +class TestGeneralOptimizer(ABCTestOptimizer): + @pytest.fixture(params=topologies) + def optimizer(self, request, options): + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + return GeneralOptimizerPSO( + n_particles=100, + dimensions=2, options=options, - topology=topology, + bounds=bounds, + topology=request.param, ) - -@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) -def test_vclamp_maxmin_exception(velocity_clamp, options, topology): - """Tests if the max velocity_clamp is less than min velocity_clamp and - vice-versa""" - with pytest.raises(ValueError): - GeneralOptimizerPSO( - 5, - 2, - velocity_clamp=velocity_clamp, + @pytest.fixture(params=topologies) + def optimizer_history(self, request, options): + opt = GeneralOptimizerPSO( + n_particles=10, + dimensions=2, options=options, - topology=topology, + topology=request.param, ) - - -@pytest.mark.parametrize("err, center", [(IndexError, [1.5, 3.2, 2.5])]) -def test_center_exception(err, center, options, topology): - """Tests if exception is thrown when center is not a list or of different shape""" - with pytest.raises(err): - GeneralOptimizerPSO( - 5, 2, center=center, options=options, topology=topology + opt.optimize(sphere, 1000) + return opt + + @pytest.fixture(params=topologies) + def optimizer_reset(self, request, options): + opt = GeneralOptimizerPSO( + n_particles=10, + dimensions=2, + options=options, + topology=request.param, ) - -def test_reset_default_values(gbest_reset): - """Tests if best cost and best pos are set properly when the reset() - method is called""" - assert gbest_reset.swarm.best_cost == np.inf - assert set(gbest_reset.swarm.best_pos) == set(np.array([])) - - -@pytest.mark.parametrize( - "history, expected_shape", - [ - ("cost_history", (1000,)), - ("mean_pbest_history", (1000,)), - ("mean_neighbor_history", (1000,)), - ("pos_history", (1000, 10, 2)), - ("velocity_history", (1000, 10, 2)), - ], -) -def test_training_history_shape(gbest_history, history, expected_shape): - """Test if training histories are of expected shape""" - pso = vars(gbest_history) - assert np.array(pso[history]).shape == expected_shape - - -def test_ftol_effect(options, topology): - """Test if setting the ftol breaks the optimization process accordingly""" - pso = GeneralOptimizerPSO( - 10, 2, options=options, topology=topology, ftol=1e-1 - ) - pso.optimize(sphere, 2000) - assert np.array(pso.cost_history).shape != (2000,) + opt.optimize(sphere, 1000) + opt.reset() + return opt + + def test_ftol_effect(self, optimizer): + """Test if setting the ftol breaks the optimization process""" + # Set optimizer tolerance + optimizer.ftol = 1e-1 + optimizer.optimize(sphere, 2000) + assert np.array(optimizer.cost_history).shape != (2000,) + + def test_obj_with_kwargs(self, obj_with_args, optimizer): + """Test if kwargs are passed properly in objfunc""" + cost, pos = optimizer.optimize(obj_with_args, 1000, a=1, b=100) + assert np.isclose(cost, 0, rtol=1e-03) + assert np.isclose(pos[0], 1.0, rtol=1e-03) + assert np.isclose(pos[1], 1.0, rtol=1e-03) + + def test_obj_unnecessary_kwargs(self, obj_without_args, optimizer): + """Test if error is raised given unnecessary kwargs""" + with pytest.raises(TypeError): + # kwargs `a` should not be supplied + cost, pos = optimizer.optimize(obj_without_args, 1000, a=1) + + def test_obj_missing_kwargs(self, obj_with_args, optimizer): + """Test if error is raised with incomplete kwargs""" + with pytest.raises(TypeError): + # kwargs `b` is missing here + cost, pos = optimizer.optimize(obj_with_args, 1000, a=1) + + def test_obj_incorrect_kwargs(self, obj_with_args, optimizer): + """Test if error is raised with wrong kwargs""" + with pytest.raises(TypeError): + # Wrong kwargs + cost, pos = optimizer.optimize(obj_with_args, 1000, c=1, d=100) diff --git a/tests/optimizers/test_global_best.py b/tests/optimizers/test_global_best.py index 5a2020a5..58187288 100644 --- a/tests/optimizers/test_global_best.py +++ b/tests/optimizers/test_global_best.py @@ -1,111 +1,30 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import numpy as np +# Import modules import pytest +# Import from pyswarms from pyswarms.single import GlobalBestPSO from pyswarms.utils.functions.single_obj import sphere +from .abc_test_optimizer import ABCTestOptimizer -@pytest.mark.parametrize( - "options", - [{"c2": 0.7, "w": 0.5}, {"c1": 0.5, "w": 0.5}, {"c1": 0.5, "c2": 0.7}], -) -def test_keyword_exception(options): - """Tests if exceptions are thrown when keywords are missing""" - with pytest.raises(KeyError): - GlobalBestPSO(5, 2, options) +class TestGlobalBestOptimizer(ABCTestOptimizer): + @pytest.fixture + def optimizer(self): + return GlobalBestPSO -@pytest.mark.parametrize( - "bounds", - [ - tuple(np.array([-5, -5])), - (np.array([-5, -5, -5]), np.array([5, 5])), - (np.array([-5, -5, -5]), np.array([5, 5, 5])), - ], -) -def test_bounds_size_exception(bounds, options): - """Tests if exceptions are raised when bound sizes are wrong""" - with pytest.raises(IndexError): - GlobalBestPSO(5, 2, options=options, bounds=bounds) + @pytest.fixture + def optimizer_history(self, options): + opt = GlobalBestPSO(10, 2, options=options) + opt.optimize(sphere, 1000) + return opt - -@pytest.mark.parametrize( - "bounds", - [ - (np.array([5, 5]), np.array([-5, -5])), - (np.array([5, -5]), np.array([-5, 5])), - ], -) -def test_bounds_maxmin_exception(bounds, options): - """Tests if the max bounds is less than min bounds and vice-versa""" - with pytest.raises(ValueError): - GlobalBestPSO(5, 2, options=options, bounds=bounds) - - -@pytest.mark.parametrize( - "bounds", - [ - [np.array([-5, -5]), np.array([5, 5])], - np.array([np.array([-5, -5]), np.array([5, 5])]), - ], -) -def test_bound_type_exception(bounds, options): - """Tests if exception is raised when bound type is not a tuple""" - with pytest.raises(TypeError): - GlobalBestPSO(5, 2, options=options, bounds=bounds) - - -@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) -def test_vclamp_shape_exception(velocity_clamp, options): - """Tests if exception is raised when velocity_clamp's size is not equal - to 2""" - with pytest.raises(IndexError): - GlobalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) - - -@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) -def test_vclamp_maxmin_exception(velocity_clamp, options): - """Tests if the max velocity_clamp is less than min velocity_clamp and - vice-versa""" - with pytest.raises(ValueError): - GlobalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) - - -@pytest.mark.parametrize("err, center", [(IndexError, [1.5, 3.2, 2.5])]) -def test_center_exception(err, center, options): - """Tests if exception is thrown when center is not a list or of different shape""" - with pytest.raises(err): - GlobalBestPSO(5, 2, center=center, options=options) - - -def test_reset_default_values(gbest_reset): - """Tests if best cost and best pos are set properly when the reset() - method is called""" - assert gbest_reset.swarm.best_cost == np.inf - assert set(gbest_reset.swarm.best_pos) == set(np.array([])) - - -@pytest.mark.parametrize( - "history, expected_shape", - [ - ("cost_history", (1000,)), - ("mean_pbest_history", (1000,)), - ("mean_neighbor_history", (1000,)), - ("pos_history", (1000, 10, 2)), - ("velocity_history", (1000, 10, 2)), - ], -) -def test_training_history_shape(gbest_history, history, expected_shape): - """Test if training histories are of expected shape""" - pso = vars(gbest_history) - assert np.array(pso[history]).shape == expected_shape - - -def test_ftol_effect(options): - """Test if setting the ftol breaks the optimization process accodingly""" - pso = GlobalBestPSO(10, 2, options=options, ftol=1e-1) - pso.optimize(sphere, 2000) - assert np.array(pso.cost_history).shape != (2000,) + @pytest.fixture + def optimizer_reset(self, options): + opt = GlobalBestPSO(10, 2, options=options) + opt.optimize(sphere, 10) + opt.reset() + return opt diff --git a/tests/optimizers/test_local_best.py b/tests/optimizers/test_local_best.py index 1a991df4..40df0e47 100644 --- a/tests/optimizers/test_local_best.py +++ b/tests/optimizers/test_local_best.py @@ -1,132 +1,30 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import numpy as np +# Import modules import pytest +# Import from pyswarms from pyswarms.single import LocalBestPSO from pyswarms.utils.functions.single_obj import sphere +from .abc_test_optimizer import ABCTestOptimizer -@pytest.mark.parametrize( - "options", - [ - {"c2": 0.7, "w": 0.5, "k": 2, "p": 2}, - {"c1": 0.5, "w": 0.5, "k": 2, "p": 2}, - {"c1": 0.5, "c2": 0.7, "k": 2, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2}, - ], -) -def test_keyword_exception(options): - """Tests if exceptions are thrown when keywords are missing""" - with pytest.raises(KeyError): - LocalBestPSO(5, 2, options) +class TestLocalBestOptimizer(ABCTestOptimizer): + @pytest.fixture + def optimizer(self): + return LocalBestPSO -@pytest.mark.parametrize( - "options", - [ - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": -1, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 6, "p": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 5}, - ], -) -def test_invalid_k_or_p_values(options): - """Tests if exception is thrown when passing - an invalid value for k or p""" - with pytest.raises(ValueError): - LocalBestPSO(5, 2, options) + @pytest.fixture + def optimizer_history(self, options): + opt = LocalBestPSO(10, 2, options) + opt.optimize(sphere, 1000) + return opt - -@pytest.mark.parametrize( - "bounds", - [ - tuple(np.array([-5, -5])), - (np.array([-5, -5, -5]), np.array([5, 5])), - (np.array([-5, -5, -5]), np.array([5, 5, 5])), - ], -) -def test_bounds_size_exception(bounds, options): - """Tests if exceptions are raised when bound sizes are wrong""" - with pytest.raises(IndexError): - LocalBestPSO(5, 2, options=options, bounds=bounds) - - -@pytest.mark.parametrize( - "bounds", - [ - (np.array([5, 5]), np.array([-5, -5])), - (np.array([5, -5]), np.array([-5, 5])), - ], -) -def test_bounds_maxmin_exception(bounds, options): - """Tests if the max bounds is less than min bounds and vice-versa""" - with pytest.raises(ValueError): - LocalBestPSO(5, 2, options=options, bounds=bounds) - - -@pytest.mark.parametrize( - "bounds", - [ - [np.array([-5, -5]), np.array([5, 5])], - np.array([np.array([-5, -5]), np.array([5, 5])]), - ], -) -def test_bound_type_exception(bounds, options): - """Tests if exception is raised when bound type is not a tuple""" - with pytest.raises(TypeError): - LocalBestPSO(5, 2, options=options, bounds=bounds) - - -@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) -def test_vclamp_shape_exception(velocity_clamp, options): - """Tests if exception is raised when velocity_clamp's size is not equal - to 2""" - with pytest.raises(IndexError): - LocalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) - - -@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) -def test_vclamp_maxmin_exception(velocity_clamp, options): - """Tests if the max velocity_clamp is less than min velocity_clamp and - vice-versa""" - with pytest.raises(ValueError): - LocalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) - - -@pytest.mark.parametrize("err, center", [(IndexError, [1.5, 3.2, 2.5])]) -def test_center_exception(err, center, options): - """Tests if exception is thrown when center is not a list or of different shape""" - with pytest.raises(err): - LocalBestPSO(5, 2, center=center, options=options) - - -def test_reset_default_values(lbest_reset): - """Tests if best cost and best pos are set properly when the reset() - method is called""" - assert lbest_reset.swarm.best_cost == np.inf - assert set(lbest_reset.swarm.best_pos) == set(np.array([])) - - -@pytest.mark.parametrize( - "history, expected_shape", - [ - ("cost_history", (1000,)), - ("mean_pbest_history", (1000,)), - ("mean_neighbor_history", (1000,)), - ("pos_history", (1000, 10, 2)), - ("velocity_history", (1000, 10, 2)), - ], -) -def test_training_history_shape(lbest_history, history, expected_shape): - """Test if training histories are of expected shape""" - pso = vars(lbest_history) - assert np.array(pso[history]).shape == expected_shape - - -def test_ftol_effect(options): - """Test if setting the ftol breaks the optimization process accodingly""" - pso = LocalBestPSO(10, 2, options=options, ftol=1e-1) - pso.optimize(sphere, 2000) - assert np.array(pso.cost_history).shape != (2000,) + @pytest.fixture + def optimizer_reset(self, options): + opt = LocalBestPSO(10, 2, options) + opt.optimize(sphere, 10) + opt.reset() + return opt diff --git a/tests/optimizers/test_objective_func_with_kwargs.py b/tests/optimizers/test_objective_func_with_kwargs.py deleted file mode 100644 index 96228765..00000000 --- a/tests/optimizers/test_objective_func_with_kwargs.py +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import pytest -import numpy as np - -from pyswarms.single import GlobalBestPSO, LocalBestPSO -from pyswarms.utils.functions.single_obj import rosenbrock - - -def rosenbrock_with_args(x, a, b): - - f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 - return f - - -@pytest.mark.parametrize("func", [rosenbrock_with_args]) -def test_global_kwargs(func): - """Tests if kwargs are passed properly to the objective function for when kwargs are present""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = GlobalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - cost, pos = opt_ps.optimize(func, 1000, a=1, b=100) - - assert np.isclose(cost, 0, rtol=1e-03) - assert np.isclose(pos[0], 1.0, rtol=1e-03) - assert np.isclose(pos[1], 1.0, rtol=1e-03) - - -@pytest.mark.parametrize("func", [rosenbrock_with_args]) -def test_global_kwargs_without_named_arguments(func): - """Tests if kwargs are passed properly to the objective function for when kwargs are present and - other named arguments are not passed, such as print_step""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = GlobalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - cost, pos = opt_ps.optimize(func, 1000, a=1, b=100) - - assert np.isclose(cost, 0, rtol=1e-03) - assert np.isclose(pos[0], 1.0, rtol=1e-03) - assert np.isclose(pos[1], 1.0, rtol=1e-03) - - -@pytest.mark.parametrize("func", [rosenbrock]) -def test_global_no_kwargs(func): - """Tests if args are passed properly to the objective function for when no args are present""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = GlobalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - cost, pos = opt_ps.optimize(func, 1000) - - assert np.isclose(cost, 0, rtol=1e-03) - assert np.isclose(pos[0], 1.0, rtol=1e-03) - assert np.isclose(pos[1], 1.0, rtol=1e-03) - - -@pytest.mark.parametrize("func", [rosenbrock_with_args]) -def test_local_kwargs(func): - """Tests if kwargs are passed properly to the objective function for when kwargs are present""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = LocalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - cost, pos = opt_ps.optimize(func, 1000, a=1, b=100) - - assert np.isclose(cost, 0, rtol=1e-03) - assert np.isclose(pos[0], 1.0, rtol=1e-03) - assert np.isclose(pos[1], 1.0, rtol=1e-03) - - -@pytest.mark.parametrize("func", [rosenbrock]) -def test_local_no_kwargs(func): - """Tests if no kwargs/args are passed properly to the objective function for when kwargs are present""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = LocalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - cost, pos = opt_ps.optimize(func, iters=1000) - - assert np.isclose(cost, 0, rtol=1e-03) - assert np.isclose(pos[0], 1.0, rtol=1e-03) - assert np.isclose(pos[1], 1.0, rtol=1e-03) - - -@pytest.mark.parametrize("func", [rosenbrock]) -def test_global_uneeded_kwargs(func): - """Tests kwargs are passed the objective function for when kwargs do not exist""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = GlobalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, a=1) - assert "unexpected keyword" in str(excinfo.value) - - -@pytest.mark.parametrize("func", [rosenbrock_with_args]) -def test_global_missed_kwargs(func): - """Tests kwargs are passed the objective function for when kwargs do not exist""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = GlobalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, a=1) - assert "missing 1 required positional argument" in str(excinfo.value) - - -@pytest.mark.parametrize("func", [rosenbrock]) -def test_local_uneeded_kwargs(func): - """Tests kwargs are passed the objective function for when kwargs do not exist""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = LocalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, a=1) - assert "unexpected keyword" in str(excinfo.value) - - -@pytest.mark.parametrize("func", [rosenbrock_with_args]) -def test_local_missed_kwargs(func): - """Tests kwargs are passed the objective function for when kwargs do not exist""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = LocalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, a=1) - assert "missing 1 required positional argument" in str(excinfo.value) - - -@pytest.mark.parametrize("func", [rosenbrock_with_args]) -def test_local_wrong_kwargs(func): - """Tests kwargs are passed the objective function for when kwargs do not exist""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = LocalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, print_step=10, c=1, d=100) - assert "unexpected keyword" in str(excinfo.value) - - -@pytest.mark.parametrize("func", [rosenbrock_with_args]) -def test_global_wrong_kwargs(func): - """Tests kwargs are passed the objective function for when kwargs do not exist""" - - # setup optimizer - options = {"c1": 0.5, "c2": 0.3, "w": 0.9, "k": 2, "p": 2} - - x_max = 10 * np.ones(2) - x_min = -1 * x_max - bounds = (x_min, x_max) - opt_ps = GlobalBestPSO( - n_particles=100, dimensions=2, options=options, bounds=bounds - ) - - # run it - with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, c=1, d=100) - assert "unexpected keyword" in str(excinfo.value) diff --git a/tests/utils/decorators/conftest.py b/tests/utils/decorators/conftest.py index 7d8d3fa1..40981b6e 100644 --- a/tests/utils/decorators/conftest.py +++ b/tests/utils/decorators/conftest.py @@ -1,5 +1,6 @@ -import pytest +# Import modules import numpy as np +import pytest @pytest.fixture() diff --git a/tests/utils/decorators/test_decorators.py b/tests/utils/decorators/test_decorators.py index 03efc361..383b5bf4 100644 --- a/tests/utils/decorators/test_decorators.py +++ b/tests/utils/decorators/test_decorators.py @@ -1,21 +1,21 @@ # Import modules -import pytest import numpy as np +import pytest +# Import from pyswarms # Import from package from pyswarms.utils.decorators import cost -@pytest.mark.parametrize( - "objective_func", - [np.sum, np.prod] -) +@pytest.mark.parametrize("objective_func", [np.sum, np.prod]) def test_cost_decorator(objective_func, particles): n_particles = particles.shape[0] def cost_func_without_decorator(x): n_particles_in_func = x.shape[0] - cost = np.array([objective_func(x[i]) for i in range(n_particles_in_func)]) + cost = np.array( + [objective_func(x[i]) for i in range(n_particles_in_func)] + ) return cost @cost @@ -27,4 +27,4 @@ def cost_func_with_decorator(x): decorated = cost_func_with_decorator(particles) assert np.array_equal(decorated, undecorated) - assert decorated.shape == (n_particles, ) + assert decorated.shape == (n_particles,) diff --git a/tests/utils/functions/conftest.py b/tests/utils/functions/conftest.py index 855f3845..e8988ef3 100644 --- a/tests/utils/functions/conftest.py +++ b/tests/utils/functions/conftest.py @@ -4,8 +4,8 @@ """Fixtures for tests""" # Import modules -import pytest import numpy as np +import pytest @pytest.fixture diff --git a/tests/utils/functions/test_singleobj_bounds.py b/tests/utils/functions/test_singleobj_bounds.py index 17fa9379..1418153e 100644 --- a/tests/utils/functions/test_singleobj_bounds.py +++ b/tests/utils/functions/test_singleobj_bounds.py @@ -3,12 +3,14 @@ """Tests for `pyswarms` package.""" +# Import standard library +from collections import namedtuple + # Import modules -import pytest import numpy as np -from collections import namedtuple +import pytest -# Import from package +# Import from pyswarms from pyswarms.utils.functions import single_obj as fx Bounds = namedtuple("Bounds", "low high") diff --git a/tests/utils/functions/test_singleobj_dims.py b/tests/utils/functions/test_singleobj_dims.py index a7f7c328..d25259bb 100644 --- a/tests/utils/functions/test_singleobj_dims.py +++ b/tests/utils/functions/test_singleobj_dims.py @@ -3,12 +3,14 @@ """Tests for `pyswarms` package.""" +# Import standard library +from collections import namedtuple + # Import modules -import pytest import numpy as np -from collections import namedtuple +import pytest -# Import from package +# Import from pyswarms from pyswarms.utils.functions import single_obj as fx diff --git a/tests/utils/functions/test_singleobj_return.py b/tests/utils/functions/test_singleobj_return.py index e3cf8c74..a7a1e705 100644 --- a/tests/utils/functions/test_singleobj_return.py +++ b/tests/utils/functions/test_singleobj_return.py @@ -1,12 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# Import standard library +from collections import namedtuple + # Import modules -import pytest import numpy as np -from collections import namedtuple +import pytest -# Import from package +# Import from pyswarms from pyswarms.utils.functions import single_obj as fx diff --git a/tests/utils/functions/test_singleobj_returndims.py b/tests/utils/functions/test_singleobj_returndims.py index 56dea11f..1704d4c4 100644 --- a/tests/utils/functions/test_singleobj_returndims.py +++ b/tests/utils/functions/test_singleobj_returndims.py @@ -1,12 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# Import standard library +from collections import namedtuple + # Import modules -import pytest import numpy as np -from collections import namedtuple +import pytest -# Import from package +# Import from pyswarms from pyswarms.utils.functions import single_obj as fx diff --git a/tests/utils/plotters/conftest.py b/tests/utils/plotters/conftest.py index 7197a247..b5289848 100644 --- a/tests/utils/plotters/conftest.py +++ b/tests/utils/plotters/conftest.py @@ -1,19 +1,23 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""Fixtures for tests""" +"""Fixtures for tests -# Import modules +isort:skip_file +""" + +# Import standard library import os -import pytest -import numpy as np -from mock import Mock + +# Import modules import matplotlib as mpl +import numpy as np +import pytest if os.environ.get("DISPLAY", "") == "": mpl.use("Agg") -# Import from package +# Import from pyswarms from pyswarms.single import GlobalBestPSO from pyswarms.utils.functions.single_obj import sphere from pyswarms.utils.plotters.formatters import Mesher diff --git a/tests/utils/plotters/test_plotters.py b/tests/utils/plotters/test_plotters.py index 668129d4..cde9249e 100644 --- a/tests/utils/plotters/test_plotters.py +++ b/tests/utils/plotters/test_plotters.py @@ -1,26 +1,28 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Import modules +# Import from standard library import os + +# Import modules import pytest import matplotlib as mpl +# Set $DISPLAY environmental variable if os.environ.get("DISPLAY", "") == "": + print("No display found. Using non-interactive Agg backend.") mpl.use("Agg") -from matplotlib.axes._subplots import SubplotBase from matplotlib.animation import FuncAnimation +from matplotlib.axes._subplots import SubplotBase -# Import from package +# Import from pyswarms from pyswarms.utils.plotters import ( - plot_cost_history, plot_contour, + plot_cost_history, plot_surface, ) - -from pyswarms.utils.plotters.plotters import _mesh, _animate -from pyswarms.utils.plotters.formatters import Mesher +from pyswarms.utils.plotters.plotters import _animate, _mesh @pytest.mark.parametrize( diff --git a/tests/utils/search/conftest.py b/tests/utils/search/conftest.py index 823b8981..478a4aa2 100644 --- a/tests/utils/search/conftest.py +++ b/tests/utils/search/conftest.py @@ -4,14 +4,16 @@ """Fixtures for tests""" # Import modules -import pytest import numpy as np +import pytest + +# Import from pyswarms +from pyswarms.single import LocalBestPSO +from pyswarms.utils.functions.single_obj import sphere # Import from package from pyswarms.utils.search.grid_search import GridSearch from pyswarms.utils.search.random_search import RandomSearch -from pyswarms.single import LocalBestPSO -from pyswarms.utils.functions.single_obj import sphere @pytest.fixture diff --git a/travis_pypi_setup.py b/travis_pypi_setup.py index a0354fde..d972d826 100644 --- a/travis_pypi_setup.py +++ b/travis_pypi_setup.py @@ -4,15 +4,18 @@ from __future__ import print_function + +# Import standard library import base64 import json import os from getpass import getpass + +# Import modules import yaml -from cryptography.hazmat.primitives.serialization import load_pem_public_key from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 - +from cryptography.hazmat.primitives.serialization import load_pem_public_key try: from urllib import urlopen