diff --git a/AUTHORS.rst b/AUTHORS.rst index 8688e2ee..9037748a 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,6 +2,13 @@ Credits ======= +This project was inspired by the pyswarm_ module that performs PSO with constrained support. +The package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template. + +.. _pyswarm: https://github.com/tisimst/pyswarm +.. _Cookiecutter: https://github.com/audreyr/cookiecutter +.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage + Development Lead ---------------- diff --git a/README.rst b/README.rst index 8c5c8012..ef638f02 100644 --- a/README.rst +++ b/README.rst @@ -66,7 +66,7 @@ built-in sphere function, :code:`pyswarms.utils.functions.sphere_func()`, and th options = {'c1': 0.5, 'c2': 0.3, 'm':0.9} # Call instance of PSO - optimizer = ps.GBestPSO(n_particles=10, dims=2, **options) + optimizer = ps.single.GBestPSO(n_particles=10, dims=2, **options) # Perform optimization stats = optimizer.optimize(sphere_func, iters=100) diff --git a/docs/_static/theme_load.css b/docs/_static/theme_load.css deleted file mode 100644 index 0e4da362..00000000 --- a/docs/_static/theme_load.css +++ /dev/null @@ -1,17 +0,0 @@ -.wy-menu-vertical header, .wy-menu-vertical p.caption { - color: gold; -} - -.wy-menu-vertical a { - color: white; -} - -.wy-side-nav-search -{ - color: #cacaca; - background: #074E68 -} - -.wy-side-nav-search input[type=text] { - border-color: rgba(7, 78, 104, 0.83); -} \ No newline at end of file diff --git a/docs/api/pyswarms.rst b/docs/api/pyswarms.rst index 08f8f372..fc7f7154 100644 --- a/docs/api/pyswarms.rst +++ b/docs/api/pyswarms.rst @@ -2,9 +2,7 @@ PySwarms package ================= .. automodule:: pyswarms - :members: - :undoc-members: - :show-inheritance: + Base Classes ------------ diff --git a/docs/conf.py b/docs/conf.py index bac121a8..76bac8a0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,7 +41,14 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'sphinx.ext.mathjax'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', + 'sphinx.ext.mathjax' + ] + +exclude_patterns = ['_build', '**.ipynb_checkpoints'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -145,12 +152,6 @@ # "default.css". html_static_path = ['_static'] -html_context = { - 'css_files': [ - '_static/theme_overrides.css', # overrides for wide tables in RTD theme - ], - } - # If not '', a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. html_last_updated_fmt = '%b %d, %Y' diff --git a/docs/index.rst b/docs/index.rst index 8b6eaeed..316affd1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,6 +12,12 @@ Contents: authors history +.. toctree:: + :maxdepth: 2 + :caption: Examples + + Use-cases + .. toctree:: :maxdepth: 2 :caption: Developer's Guide diff --git a/pyswarms/__init__.py b/pyswarms/__init__.py index 831ad00c..cad5ad1a 100644 --- a/pyswarms/__init__.py +++ b/pyswarms/__init__.py @@ -12,4 +12,6 @@ __author__ = """Lester James V. Miranda""" __email__ = 'ljvmiranda@gmail.com' -__version__ = '0.1.1' \ No newline at end of file +__version__ = '0.1.1' + +from .single import gb, lb \ No newline at end of file diff --git a/pyswarms/single/gb.py b/pyswarms/single/gb.py index e55f5476..887bc50d 100644 --- a/pyswarms/single/gb.py +++ b/pyswarms/single/gb.py @@ -1,6 +1,57 @@ # -*- coding: utf-8 -*- -""" gb.py: global-best particle swarm optimization algorithm """ +r""" +A Global-best Particle Swarm Optimization (gbest PSO) algorithm. + +It takes a set of candidate solutions, and tries to find the best +solution using a position-velocity update method. Uses a +star-topology where each particle is attracted to the best +performing particle. + +The position update can be defined as: + +.. math:: + + x_{i}(t+1) = x_{i}(t) + v_{i}(t+1) + +Where the position at the current timestep :math:`t` is updated using +the computed velocity at :math:`t+1`. Furthermore, the velocity update +is defined as: + +.. math:: + + v_{ij}(t + 1) = m * v_{ij}(t) + c_{1}r_{1j}(t)[y_{ij}(t) − x_{ij}(t)] + c_{2}r_{2j}(t)[\hat{y}_{j}(t) − x_{ij}(t)] + +Here, :math:`c1` and :math:`c2` are the cognitive and social parameters +respectively. They control the particle's behavior in choosing how to +react given two choices: (1) to follow its *personal best* or (2) follow +the swarm's *global best* position. Overall, this dictates if the swarm +is explorative or exploitative in nature. In addition, a parameter +:math:`m` controls the inertia of the swarm's movement. + +An example usage is as follows: + +.. code-block:: python + + import pyswarms as ps + from pyswarms.utils.functions import sphere_func + + # Set-up hyperparameters + options = {'c1': 0.5, 'c2': 0.3, 'm':0.9} + + # Call instance of GBestPSO + optimizer = ps.single.GBestPSO(n_particles=10, dims=2, **options) + + # Perform optimization + stats = optimizer.optimize(sphere_func, iters=100) + +This algorithm was adapted from the earlier works of J. Kennedy and +R.C. Eberhart in Particle Swarm Optimization [IJCNN1995]_. + +.. [IJCNN1995] J. Kennedy and R.C. Eberhart, "Particle Swarm Optimization," + Proceedings of the IEEE International Joint Conference on Neural + Networks, 1995, pp. 1942-1948. +""" # Import modules import numpy as np @@ -10,21 +61,7 @@ from ..utils.console_utils import cli_print, end_report class GBestPSO(SwarmBase): - """A global-best Particle Swarm Optimization (PSO) algorithm. - - It takes a set of candidate solutions, and tries to find the best - solution using a position-velocity update method. Uses a - star-topology where each particle is attracted to the best - performing particle. - - This algorithm was adapted from the earlier works of J. Kennedy and - R.C. Eberhart in Particle Swarm Optimization [1]_ - - .. [1] J. Kennedy and R.C. Eberhart, "Particle Swarm Optimization," - Proceedings of the IEEE International Joint Conference on Neural - Networks, 1995, pp. 1942-1948. - """ def assertions(self): """Assertion method to check various inputs. @@ -55,7 +92,7 @@ def __init__(self, n_particles, dims, bounds=None, **kwargs): number of particles in the swarm. dims : int number of dimensions in the space. - bounds : tuple of np.ndarray, optional (default is :code:`None`) + bounds : tuple of :code:`np.ndarray`, optional (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:`(dims,)`. diff --git a/pyswarms/single/lb.py b/pyswarms/single/lb.py index 45b2e073..6b17fb33 100644 --- a/pyswarms/single/lb.py +++ b/pyswarms/single/lb.py @@ -1,6 +1,66 @@ # -*- coding: utf-8 -*- -""" lb.py: local-best partical swarm optimization algorithm """ +r""" +A Local-best Particle Swarm Optimization (lbest PSO) algorithm. + +Similar to global-best PSO, it takes a set of candidate solutions, +and finds the best solution using a position-velocity update method. +However, it uses a ring topology, thus making the particles +attracted to its corresponding neighborhood. + +The position update can be defined as: + +.. math:: + + x_{i}(t+1) = x_{i}(t) + v_{i}(t+1) + +Where the position at the current timestep :math:`t` is updated using +the computed velocity at :math:`t+1`. Furthermore, the velocity update +is defined as: + +.. math:: + + v_{ij}(t + 1) = m * v_{ij}(t) + c_{1}r_{1j}(t)[y_{ij}(t) − x_{ij}(t)] + c_{2}r_{2j}(t)[\hat{y}_{j}(t) − x_{ij}(t)] + +However, in local-best PSO, a particle doesn't compare itself to the +overall performance of the swarm. Instead, it looks at the performance +of its nearest-neighbours, and compares itself with them. In general, +this kind of topology takes much more time to converge, but has a more +powerful explorative feature. + +In this implementation, a neighbor is selected via a k-D tree +imported from :code:`scipy`. Distance are computed with either +the L1 or L2 distance. The nearest-neighbours are then queried from +this k-D tree. + +An example usage is as follows: + +.. code-block:: python + + import pyswarms as ps + from pyswarms.utils.functions import single_obj as fx + + # Set-up hyperparameters + options = {'c1': 0.5, 'c2': 0.3, 'm':0.9} + + # Call instance of LBestPSO with a neighbour-size of 3 determined by + # the L2 (p=2) distance. + optimizer = ps.single.LBestPSO(n_particles=10, dims=2, k=3, p=2, **options) + + # Perform optimization + stats = optimizer.optimize(fx.sphere_func, iters=100) + +This algorithm was adapted from one of the earlier works of +J. Kennedy and R.C. Eberhart in Particle Swarm Optimization [IJCNN1995]_ [MHS1995] + +.. [IJCNN1995] J. Kennedy and R.C. Eberhart, "Particle Swarm Optimization," + Proceedings of the IEEE International Joint Conference on Neural + Networks, 1995, pp. 1942-1948. + +.. [MHS1995] J. Kennedy and R.C. Eberhart, "A New Optimizer using Particle + Swarm Theory," in Proceedings of the Sixth International + Symposium on Micromachine and Human Science, 1995, pp. 39–43. +""" # Import modules import numpy as np @@ -11,29 +71,7 @@ from ..utils.console_utils import cli_print, end_report class LBestPSO(SwarmBase): - """A local-best Particle Swarm Optimization algorithm. - Similar to global-best PSO, it takes a set of candidate solutions, - and finds the best solution using a position-velocity update method. - However, it uses a ring topology, thus making the particles - attracted to its corresponding neighborhood. - - In this implementation, a neighbor is selected via a k-D tree - imported from :code:`scipy`. Distance are computed with either - the L1 or L2 distance. The nearest-neighbours are then queried from - this k-D tree. - - This algorithm was adapted from one of the earlier works of - J. Kennedy and R.C. Eberhart in Particle Swarm Optimization [1]_ [2]_ - - .. [1] J. Kennedy and R.C. Eberhart, "Particle Swarm Optimization," - Proceedings of the IEEE International Joint Conference on Neural - Networks, 1995, pp. 1942-1948. - - .. [2] J. Kennedy and R.C. Eberhart, "A New Optimizer using Particle - Swarm Theory," in Proceedings of the Sixth International - Symposium on Micromachine and Human Science, 1995, pp. 39–43. - """ def assertions(self): """Assertion method to check various inputs. @@ -156,6 +194,17 @@ def optimize(self, f, iters, print_step=1, verbose=1): # Perform position velocity update self._update_velocity_position() + # Because we have multiple neighbor spaces, we are reporting the + # local-best for each neighbour, thus giving us multiple values + # for the local-best cost and positions. What we'll do is that + # we are going to obtain only the minimum of all these local + # positions and then report it. + + self.best_neighbor_cost = np.argmin(self.lbest_cost) + self.best_neighbor_pos = self.lbest_pos[self.best_neighbor_cost] + + end_report(self.best_neighbor_cost, self.best_neighbor_pos, verbose) + return (self.best_neighbor_cost, self.best_neighbor_pos) def _get_neighbors(self, current_cost): """Helper function to obtain the best position found in the diff --git a/pyswarms/utils/functions/single_obj.py b/pyswarms/utils/functions/single_obj.py index 242ee298..ecdb5a16 100644 --- a/pyswarms/utils/functions/single_obj.py +++ b/pyswarms/utils/functions/single_obj.py @@ -2,14 +2,14 @@ """single_obj.py: collection of single-objective functions -All objective functions obj_func() must accept a (numpy.ndarray) -with shape (n_particles, dims). Thus, each row represents a -particle, and each column represents its position on a specific -dimension of the search-space. +All objective functions :code:`obj_func()` must accept a +:code:`(numpy.ndarray)` with shape :code:`(n_particles, dims)`. +Thus, each row represents a particle, and each column represents its +position on a specific dimension of the search-space. -In this context, obj_func() must return an array j of size -(n_particles, ) that contains all the computed fitness for -each particle. +In this context, :code:`obj_func()` must return an array :code:`j` +of size :code:`(n_particles, )` that contains all the computed fitness +for each particle. Whenever you make changes to this file via an implementation of a new objective function, be sure to perform unittesting @@ -22,18 +22,18 @@ def sphere_func(x): """Sphere objective function. - Has a global minimum at 0 and with a search domain of - [-inf, inf] + Has a global minimum at :code:`0` and with a search domain of + :code:`[-inf, inf]` Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` """ j = (x**2.0).sum(axis=1) @@ -42,18 +42,18 @@ def sphere_func(x): def rastrigin_func(x): """Rastrigin objective function. - Has a global minimum at f(0,0,...,0) with a search - domain of -[-5.12, 5.12] + Has a global minimum at :code:`f(0,0,...,0)` with a search + domain of :code:`[-5.12, 5.12]` Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ @@ -72,18 +72,18 @@ def rastrigin_func(x): def ackley_func(x): """Ackley's objective function. - Has a global minimum at f(0,0,...,0) with a search + Has a global minimum at :code:`f(0,0,...,0)` with a search domain of [-32, 32] Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ @@ -106,18 +106,19 @@ def rosenbrock_func(x): """Rosenbrock objective function. Also known as the Rosenbrock's valley or Rosenbrock's banana - function. Has a global minimum of np.ones(dims) where dims - is x.shape[1]. The search domain is [-inf, inf]. + function. Has a global minimum of :code:`np.ones(dims)` where + :code:`dims` is :code:`x.shape[1]`. The search domain is + :code:`[-inf, inf]`. Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` """ j = (100 * np.square(x[:,1:] - x[:,:-1]**2.0) + (1.0 - x[:,:-1]) ** 2.0) @@ -127,18 +128,18 @@ def rosenbrock_func(x): def beale_func(x): """Beale objective function. - Only takes two dimensions and has a global minimum at f([0,3.5]) - Its domain is bounded between [-4.5, 4.5] + Only takes two dimensions and has a global minimum at + :code:`f([0,3.5])` Its domain is bounded between :code:`[-4.5, 4.5]` Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ @@ -166,18 +167,18 @@ def beale_func(x): def goldstein_func(x): """Goldstein-Price's objective function. - Only takes two dimensions and has a global minimum at f([0,-1]) - Its domain is bounded between [-2, 2] + Only takes two dimensions and has a global minimum at + :code:`f([0,-1])`. Its domain is bounded between :code:`[-2, 2]` Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ @@ -206,18 +207,18 @@ def goldstein_func(x): def booth_func(x): """Booth's objective function. - Only takes two dimensions and has a global minimum at f([1,3]) - Its domain is bounded between [-10, 10] + Only takes two dimensions and has a global minimum at + :code:`f([1,3])`. Its domain is bounded between :code:`[-10, 10]` Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ @@ -243,20 +244,20 @@ def booth_func(x): def bukin6_func(x): """Bukin N. 6 Objective Function - Only takes two dimensions and has a global minimum at f([-10,1]) - Its coordinates are bounded by: - x[:,0] must be within [-15, -5] - x[:,1] must be within [-3, 3] + Only takes two dimensions and has a global minimum at + :code:`f([-10,1])`. Its coordinates are bounded by: + * x[:,0] must be within [-15, -5] + * x[:,1] must be within [-3, 3] Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ @@ -283,47 +284,42 @@ def bukin6_func(x): def matyas_func(x): """Matyas objective function - Only takes two dimensions and has a global minimum at f([0,0]) - Its coordinates are bounded within [-10,10] + Only takes two dimensions and has a global minimum at + :code:`f([0,0])`. Its coordinates are bounded within + :code:`[-10,10]`. Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) """ -<<<<<<< HEAD if not x.shape[1] == 2: raise IndexError('Matyas function only takes two-dimensional input.') if not np.logical_and(x >= -10, x <= 10).all(): raise ValueError('Input for Matyas function must be within [-10, 10].') -======= - x_ = x[:,0] - y_ = x[:,1] - j = 0.26 * (x_**2.0 + y_**2.0) - 0.48 * x_ * y_ ->>>>>>> f5649c5... implemented all single-objective functions return j def levi_func(x): """Levi objective function - Only takes two dimensions and has a global minimum at f([1,1]) - Its coordinates are bounded within [-10,10] + Only takes two dimensions and has a global minimum at + :code:`f([1,1])`. Its coordinates are bounded within + :code:`[-10,10]`. Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ @@ -359,18 +355,19 @@ def levi_func(x): def schaffer2_func(x): """Schaffer N.2 objective function - Only takes two dimensions and has a global minimum at f([0,0]) - Its coordinates are bounded within [-100,100] + Only takes two dimensions and has a global minimum at + :code:`f([0,0])`. Its coordinates are bounded within + :code:`[-100,100]`. Parameters ---------- x : numpy.ndarray - set of inputs of shape (n_particles, dims) + set of inputs of shape :code:`(n_particles, dims)` Returns ------- numpy.ndarray - computed cost of size (n_particles, ) + computed cost of size :code:`(n_particles, )` Raises ------ diff --git a/requirements_dev.txt b/requirements_dev.txt index 8df9d2e0..75416919 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -9,5 +9,4 @@ Sphinx==1.6.3 cryptography==2.0 PyYAML==3.12 scipy>=0.17.0 -numpy>=1.10.4 - +numpy>=1.10.4 \ No newline at end of file