Skip to content

Commit

Permalink
Add Binary PSO (#7)
Browse files Browse the repository at this point in the history
Another optimizer, BinaryPSO, has been added. This update
commit introduces test cases in tests/optimizers/test_bn.py
and an abstract discrete class pyswarms/base/dbs.py.

A list of new files can be seen below:
  [Documentation]
  - docs/api/pyswarms.discrete.rst
  [Base Class and Optimizers]
  - pyswarms/base/dbs.py
  - pyswarms/discrete/__init__.py
  - pyswarms/discrete/bn.py
  [Test Cases]
  - tests/optimizers/test_bn.py

This also introduces a small patch for the other base classes.
In this case, the k and p arguments were then transformed into
keyword arguments. They should now be part of the `**kwargs`
dictionary from here on in. This is consistent with the local
best PSO and the binary PSO.

Author: ljvmiranda921
  • Loading branch information
ljvmiranda921 committed Aug 4, 2017
1 parent 6d5421a commit b2f3c97
Show file tree
Hide file tree
Showing 15 changed files with 675 additions and 36 deletions.
13 changes: 11 additions & 2 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@ History

* First release on PyPI.

0.1.1 (2017-7-25)
0.1.1 (2017-07-25)
~~~~~~~~~~~~~~~~~

* Bug fixes
* Pre-release
* Bug fixes on calling local-best
* Implemented Local Best PSO

0.1.2 (2017-08-02)
~~~~~~~~~~~~~~~~~

* Pre-release
* Implemented Binary PSO
* More efficient API for gbest and lbest
* Documentation and tests
3 changes: 1 addition & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
PySwarms
========


.. image:: https://badge.fury.io/py/pyswarms.svg
:target: https://badge.fury.io/py/pyswarms

Expand Down Expand Up @@ -70,7 +69,7 @@ built-in sphere function, :code:`pyswarms.utils.functions.sphere_func()`, and th
from pyswarms.utils.functions import single_obj as fx
# Set-up hyperparameters
options = {'c1': 0.5, 'c2': 0.3, 'm':0.9}
options = {'c1': 0.5, 'c2': 0.3, 'w':0.9}
# Call instance of PSO
optimizer = ps.single.GBestPSO(n_particles=10, dims=2, **options)
Expand Down
10 changes: 8 additions & 2 deletions docs/api/pyswarms.base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ pyswarms.base package
Submodules
----------

pyswarms.base.bs module
------------------------
pyswarms.base module
--------------------

.. automodule:: pyswarms.base.bs
:members:
Expand All @@ -17,3 +17,9 @@ pyswarms.base.bs module
:private-members:
:special-members: __init__

.. automodule:: pyswarms.base.dbs
:members:
:undoc-members:
:show-inheritance:
:private-members:
:special-members: __init__
16 changes: 16 additions & 0 deletions docs/api/pyswarms.discrete.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pyswarms.discrete package
=========================

.. automodule:: pyswarms.discrete

Submodules
----------

pyswarms.discrete.bn module
---------------------------

.. automodule:: pyswarms.discrete.bn
:members:
:undoc-members:
:show-inheritance:
:special-members: __init__
5 changes: 3 additions & 2 deletions docs/api/pyswarms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ The optimizers include the actual PSO implementations for various tasks. General
there are two ways to implement an optimizer from this library: (1) as an easy
off-the-shelf algorithm, and (2) as an experimental custom-made algorithm.

* Easy off-the-shelf implementations include those that are already considered as standard in literature. This may include the classics such as global-best and local-best. Their topologies are hardcoded already, and there is no need for prior set-up in order to use. This is useful for quick-and-easy optimization problems.
1. Easy off-the-shelf implementations include those that are already considered as standard in literature. This may include the classics such as global-best and local-best. Their topologies are hardcoded already, and there is no need for prior set-up in order to use. This is useful for quick-and-easy optimization problems.

* Experimental PSO algorithms are like standard PSO algorithms but without a defined topology. Instead, an object that inherits from a :code:`Topology` class is passed to an optimizer to define swarm behavior. Although the standard PSO implementations can be done through this, this is more experimental.
2. Experimental PSO algorithms are like standard PSO algorithms but without a defined topology. Instead, an object that inherits from a :code:`Topology` class is passed to an optimizer to define swarm behavior. Although the standard PSO implementations can be done through this, this is more experimental.

.. toctree::

pyswarms.single
pyswarms.discrete


Utilities
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/basic_optimization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ Now, let's try this one using local-best PSO:
.. code-block:: python
# Set-up hyperparameters
options = {'c1': 0.5, 'c2': 0.3, 'w':0.9}
options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2}
# Call instance of PSO
lbest_pso = ps.single.LBestPSO(n_particles=10, dims=2, k=2,p=2, **options)
lbest_pso = ps.single.LBestPSO(n_particles=10, dims=2, **options)
# Perform optimization
cost, pos = lbest_pso.optimize(fx.sphere_func, print_step=100, iters=1000, verbose=3)
Expand Down
9 changes: 8 additions & 1 deletion pyswarms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@
__email__ = 'ljvmiranda@gmail.com'
__version__ = '0.1.1'

from .single import gb, lb
from .single import gb, lb
from .discrete import bn

__all__ = [
'gb',
'lb',
'bn'
]
182 changes: 182 additions & 0 deletions pyswarms/base/dbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# -*- coding: utf-8 -*-

r"""
Base class for single-objective discrete Particle Swarm Optimization
implementations.
All methods here are abstract and raises a :code:`NotImplementedError`
when not used. When defining your own swarm implementation,
create another class,
>>> class MySwarm(DiscreteSwarmBase):
>>> def __init__(self):
>>> super(MySwarm, self).__init__()
and define all the necessary methods needed.
As a guide, check the discrete PSO implementations in this package.
.. note:: Regarding :code:`**kwargs`, it is highly recommended to
include parameters used in position and velocity updates as
keyword arguments. For parameters that affect the topology of
the swarm, it may be much better to have them as positional
arguments.
See Also
--------
:mod:`pyswarms.discrete.bn`: binary PSO implementation
"""

import numpy as np

class DiscreteSwarmBase(object):

def assertions(self):
"""Assertion method to check various inputs.
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 `dims`.
ValueError
When the value of :code:`bounds[1]` is less than
:code:`bounds[0]`.
"""

# Check clamp settings
if self.v_clamp is not None:
if not type(self.v_clamp) == tuple:
raise TypeError('Parameter `v_clamp` must be a tuple')
if not len(self.v_clamp) == 2:
raise IndexError('Parameter `v_clamp` must be of size 2')
if not self.v_clamp[0] < self.v_clamp[1]:
raise ValueError('Make sure that v_clamp is in the form (v_min, v_max)')

# Required keys in keyword arguments
if not all (key in self.kwargs for key in ('c1', 'c2', 'w')):
raise KeyError('Missing either c1, c2, or w in kwargs')

def __init__(self, n_particles, dims, binary, v_clamp=None, **kwargs):
"""Initializes the swarm.
Creates a :code:`numpy.ndarray` of positions depending on the
number of particles needed and the number of dimensions.
The initial positions of the particles depends on the argument
:code:`binary`, which governs if a binary matrix will be produced.
Attributes
----------
n_particles : int
number of particles in the swarm.
dims : int
number of dimensions in the space.
binary : boolean
a trigger to generate a binary matrix for the swarm's
initial positions. When passed with a :code:`False` value,
random integers from 0 to :code:`dims` are generated.
v_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.
**kwargs: dict
a dictionary containing various keyword arguments for a
specific optimization technique
"""
# Initialize primary swarm attributes
self.n_particles = n_particles
self.dims = dims
self.binary = binary
self.v_clamp = v_clamp
self.swarm_size = (n_particles, dims)
self.kwargs = kwargs

# Invoke assertions
self.assertions()

# Initialize resettable attributes
self.reset()

def optimize(self, f, iters, print_step=1, verbose=1):
"""Optimizes the swarm for a number of iterations.
Performs the optimization to evaluate the objective
function :code:`f` for a number of iterations :code:`iter.`
Parameters
----------
f : function
objective function to be evaluated
iters : int
number of iterations
print_step : int (the default is 1)
amount of steps for printing into console.
verbose : int (the default is 1)
verbosity setting.
Raises
------
NotImplementedError
When this method is not implemented.
"""
raise NotImplementedError("SwarmBase::optimize()")

def _update_velocity(self):
"""Updates the velocity matrix.
Raises
------
NotImplementedError
When this method is not implemented.
"""
raise NotImplementedError("SwarmBase::_update_velocity()")

def _update_position(self):
"""Updates the position matrix.
Raises
------
NotImplementedError
When this method is not implemented.
"""
raise NotImplementedError("SwarmBase::_update_position()")

def reset(self):
"""Resets the attributes of the optimizer.
All variables/atributes that will be re-initialized when this
method is defined here. Note that this method
can be called twice: (1) during initialization, and (2) when
this is called from an instance.
It is good practice to keep the number of resettable
attributes at a minimum. This is to prevent spamming the same
object instance with various swarm definitions.
Normally, swarm definitions are as atomic as possible, where
each type of swarm is contained in its own instance. Thus, the
following attributes are the only ones recommended to be
resettable:
* Swarm position matrix (self.pos)
* Velocity matrix (self.pos)
* Best scores and positions (gbest_cost, gbest_pos, etc.)
Otherwise, consider using positional arguments.
"""
# Generate initial position
self.pos = np.random.random_sample(size=self.swarm_size).argsort(axis=1)
if self.binary:
self.pos = np.random.randint(2,size=self.swarm_size)

# Initialize velocity vectors
if self.v_clamp is not None:
v_min, v_max = self.v_clamp[0], self.v_clamp[1]
self.velocity = ((v_max - v_min)
* np.random.random_sample(size=self.swarm_size)
+ v_min)
else:
self.velocity = np.random.random_sample(size=self.swarm_size)
12 changes: 11 additions & 1 deletion pyswarms/discrete/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
""" Discrete optimization package for `pyswarms` """
"""
The :mod:`pyswarms.discrete` module implements various techniques in
discrete optimization. These are techniques that can be applied to a
discrete search-space.
"""

from .bn import BinaryPSO

__all__ = [
"BinaryPSO"
]
Loading

0 comments on commit b2f3c97

Please sign in to comment.