Skip to content

Commit

Permalink
Add reporter module (#227)
Browse files Browse the repository at this point in the history
Resolves #209 

This commit adds a reporter module that implements a Reporter class.
Its goal is to separate the concern of logging and printing from the
optimizer classes.

Signed-off-by: Lester James V. Miranda <[email protected]>
  • Loading branch information
ljvmiranda921 committed Jan 29, 2019
1 parent 3e835fd commit 7929a41
Show file tree
Hide file tree
Showing 31 changed files with 443 additions and 401 deletions.
1 change: 1 addition & 0 deletions docs/api/_pyswarms.utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ functionalities.
pyswarms.utils.functions
pyswarms.utils.search
pyswarms.utils.plotters
pyswarms.utils.reporter
10 changes: 10 additions & 0 deletions docs/api/pyswarms.utils.reporter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pyswarms.utils.reporter package
================================

.. automodule:: pyswarms.utils.reporter.reporter
:members:
:undoc-members:
:show-inheritance:
:private-members:
:special-members: __init__

26 changes: 21 additions & 5 deletions pyswarms/backend/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
"""

# Import modules
import logging

import numpy as np

# Import from package
from ..utils.reporter import Reporter
from .swarms import Swarm

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


def generate_swarm(
n_particles, dimensions, bounds=None, center=1.00, init_pos=None
Expand Down Expand Up @@ -67,7 +70,12 @@ 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"
)
raise
except TypeError:
rep.logger.exception("Invalid input type!")
else:
return pos

Expand Down Expand Up @@ -103,7 +111,11 @@ def generate_discrete_swarm(
size=(n_particles, dimensions)
).argsort(axis=1)
except ValueError:
raise
rep.logger.exception(
"Please check the size and value of bounds and dimensions"
)
except TypeError:
rep.logger.exception("Invalid input type!")
else:
return pos

Expand Down Expand Up @@ -132,8 +144,12 @@ def generate_velocity(n_particles, dimensions, clamp=None):
velocity = (max_velocity - min_velocity) * np.random.random_sample(
size=(n_particles, dimensions)
) + min_velocity
except (ValueError, TypeError):
raise
except ValueError:
rep.logger.exception(
"Please check the size and value of clamp and dimensions"
)
except TypeError:
rep.logger.exception("Invalid input type!")
else:
return velocity

Expand Down
29 changes: 13 additions & 16 deletions pyswarms/backend/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
to specify how the swarm will behave.
"""

# Import from stdlib
import logging

# Import modules
import numpy as np

# Create a logger
logger = logging.getLogger(__name__)
from ..utils.reporter import Reporter

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


def compute_pbest(swarm):
Expand Down Expand Up @@ -67,9 +66,9 @@ def compute_pbest(swarm):
~mask_cost, swarm.pbest_cost, swarm.current_cost
)
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(type(swarm))
logger.error(msg)
raise
rep.logger.exception(
"Please pass a Swarm class. You passed {}".format(type(swarm))
)
else:
return (new_pbest_pos, new_pbest_cost)

Expand Down Expand Up @@ -137,13 +136,11 @@ def compute_velocity(swarm, clamp):
)
updated_velocity = np.where(~mask, swarm.velocity, temp_velocity)
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(type(swarm))
logger.error(msg)
raise
rep.logger.exception(
"Please pass a Swarm class. You passed {}".format(type(swarm))
)
except KeyError:
msg = "Missing keyword in swarm.options"
logger.error(msg)
raise
rep.logger.exception("Missing keyword in swarm.options")
else:
return updated_velocity

Expand Down Expand Up @@ -187,8 +184,8 @@ def compute_position(swarm, bounds):
temp_position = np.where(~mask, swarm.position, temp_position)
position = temp_position
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(type(swarm))
logger.error(msg)
raise
rep.logger.exception(
"Please pass a Swarm class. You passed {}".format(type(swarm))
)
else:
return position
3 changes: 1 addition & 2 deletions pyswarms/backend/swarms.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
as input to most backend cases.
"""

# Import modules
import numpy as np
from attr import attrs, attrib
from attr import attrib, attrs
from attr.validators import instance_of


Expand Down
16 changes: 6 additions & 10 deletions pyswarms/backend/topology/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,28 @@
:mod:`pyswarms.backend.swarms.Swarm` module.
"""

# Import from stdlib
import abc
import logging

# Import from package
from ...utils.console_utils import cli_print
from ...utils.reporter import Reporter


class Topology(abc.ABC):
def __init__(self, static, **kwargs):
"""Initializes the class"""

# Initialize logger
self.logger = logging.getLogger(__name__)
self.rep = Reporter(logger=logging.getLogger(__name__))

# Initialize attributes
self.static = static
self.neighbor_idx = None

if self.static:
cli_print(
"Running on `dynamic` topology, neighbors are updated regularly."
"Set `static=True` for fixed neighbors.",
1,
0,
self.logger,
self.rep.log(
"Running on `dynamic` topology,"
"set `static=True` for fixed neighbors.",
lvl=10,
)

@abc.abstractmethod
Expand Down
13 changes: 4 additions & 9 deletions pyswarms/backend/topology/pyramid.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,15 @@
This class implements a pyramid topology. In this topology, the particles are connected by N-dimensional simplices.
"""

# Import from stdlib
import logging

# Import modules
import numpy as np
from scipy.spatial import Delaunay

# Import from package
from .. import operators as ops
from ...utils.reporter import Reporter
from .base import Topology

# Create a logger
logger = logging.getLogger(__name__)


class Pyramid(Topology):
def __init__(self, static=False):
Expand All @@ -32,6 +27,7 @@ def __init__(self, static=False):
is static or dynamic
"""
super(Pyramid, self).__init__(static)
self.rep = Reporter(logger=logging.getLogger(__name__))

def compute_gbest(self, swarm):
"""Update the global best using a pyramid neighborhood approach
Expand Down Expand Up @@ -102,10 +98,9 @@ def compute_gbest(self, swarm):
best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])]
]
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(
type(swarm)
self.rep.logger.exception(
"Please pass a Swarm class. You passed {}".format(type(swarm))
)
logger.error(msg)
raise
else:
return (best_pos, best_cost)
Expand Down
15 changes: 5 additions & 10 deletions pyswarms/backend/topology/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,16 @@
This class implements a random topology. All particles are connected in a random fashion.
"""

# Import from stdlib
import logging
import itertools
import logging

# Import modules
import numpy as np
from scipy.sparse.csgraph import connected_components, dijkstra

# Import from package
from .. import operators as ops
from ...utils.reporter import Reporter
from .base import Topology

# Create a logger
logger = logging.getLogger(__name__)


class Random(Topology):
def __init__(self, static=False):
Expand All @@ -32,6 +27,7 @@ def __init__(self, static=False):
a boolean that decides whether the topology
is static or dynamic"""
super(Random, self).__init__(static)
self.rep = Reporter(logger=logging.getLogger(__name__))

def compute_gbest(self, swarm, k):
"""Update the global best using a random neighborhood approach
Expand Down Expand Up @@ -91,10 +87,9 @@ def compute_gbest(self, swarm, k):
]

except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(
type(swarm)
self.rep.logger.exception(
"Please pass a Swarm class. You passed {}".format(type(swarm))
)
logger.error(msg)
raise
else:
return (best_pos, best_cost)
Expand Down
13 changes: 4 additions & 9 deletions pyswarms/backend/topology/ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,15 @@
optimizers.
"""

# Import from stdlib
import logging

# Import modules
import numpy as np
from scipy.spatial import cKDTree

# Import from package
from .. import operators as ops
from ...utils.reporter import Reporter
from .base import Topology

# Create a logger
logger = logging.getLogger(__name__)


class Ring(Topology):
def __init__(self, static=False):
Expand All @@ -34,6 +29,7 @@ def __init__(self, static=False):
a boolean that decides whether the topology
is static or dynamic"""
super(Ring, self).__init__(static)
self.rep = Reporter(logger=logging.getLogger(__name__))

def compute_gbest(self, swarm, p, k):
"""Update the global best using a ring-like neighborhood approach
Expand Down Expand Up @@ -86,10 +82,9 @@ def compute_gbest(self, swarm, p, k):
best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])]
]
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(
type(swarm)
self.rep.logger.exception(
"Please pass a Swarm class. You passed {}".format(type(swarm))
)
logger.error(msg)
raise
else:
return (best_pos, best_cost)
Expand Down
13 changes: 4 additions & 9 deletions pyswarms/backend/topology/star.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,19 @@
optimizers.
"""

# Import from stdlib
import logging

# Import modules
import numpy as np

# Import from package
from .. import operators as ops
from ...utils.reporter import Reporter
from .base import Topology

# Create a logger
logger = logging.getLogger(__name__)


class Star(Topology):
def __init__(self):
super(Star, self).__init__(static=True)
self.rep = Reporter(logger=logging.getLogger(__name__))

def compute_gbest(self, swarm):
"""Update the global best using a star topology
Expand Down Expand Up @@ -68,10 +64,9 @@ def compute_gbest(self, swarm):
best_pos = swarm.pbest_pos[np.argmin(swarm.pbest_cost)]
best_cost = np.min(swarm.pbest_cost)
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(
type(swarm)
self.rep.logger.exception(
"Please pass a Swarm class. You passed {}".format(type(swarm))
)
logger.error(msg)
raise
else:
return (best_pos, best_cost)
Expand Down
6 changes: 2 additions & 4 deletions pyswarms/backend/topology/von_neumann.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@
This class implements a Von Neumann topology.
"""

# Import from stdlib
import logging

from .ring import Ring

# Create a logger
logger = logging.getLogger(__name__)
from ...utils.reporter import Reporter


class VonNeumann(Ring):
def __init__(self):
super(VonNeumann, self).__init__(static=True)
self.rep = Reporter(logger=logging.getLogger(__name__))

def compute_gbest(self, swarm, p, r):
"""Updates the global best using a neighborhood approach
Expand Down
Loading

0 comments on commit 7929a41

Please sign in to comment.