From 286623b3fa7d388700b79d3477ded3eb9ef8e30f Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Wed, 15 Aug 2018 22:40:02 +0900 Subject: [PATCH 1/8] Add reporter module (#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 --- docs/conf.py | 166 ++++++++-------- pyswarms/backend/topology/pyramid.py | 27 ++- pyswarms/backend/topology/random.py | 67 +++++-- pyswarms/backend/topology/ring.py | 10 +- pyswarms/backend/topology/star.py | 4 +- pyswarms/backend/topology/von_neumann.py | 9 +- pyswarms/discrete/binary.py | 24 ++- pyswarms/single/general_optimizer.py | 38 ++-- pyswarms/single/global_best.py | 24 ++- pyswarms/single/local_best.py | 26 ++- pyswarms/utils/__init__.py | 4 + pyswarms/utils/plotters/formatters.py | 6 +- pyswarms/utils/reporter/__init__.py | 5 + pyswarms/utils/reporter/reporter.py | 182 ++++++++++++++++++ tests/backend/topology/conftest.py | 2 + tests/backend/topology/test_pyramid.py | 2 +- tests/backend/topology/test_random.py | 4 +- tests/backend/topology/test_ring.py | 2 +- tests/backend/topology/test_star.py | 2 +- tests/backend/topology/test_von_neumann.py | 5 +- tests/optimizers/conftest.py | 10 +- tests/optimizers/test_general_optimizer.py | 43 +++-- .../test_objective_func_with_kwargs.py | 140 +++++++------- .../utils/functions/test_singleobj_bounds.py | 1 - tests/utils/functions/test_singleobj_dims.py | 1 + .../utils/functions/test_singleobj_return.py | 76 ++++---- travis_pypi_setup.py | 42 ++-- 27 files changed, 632 insertions(+), 290 deletions(-) create mode 100644 pyswarms/utils/reporter/__init__.py create mode 100644 pyswarms/utils/reporter/reporter.py diff --git a/docs/conf.py b/docs/conf.py index 56e0eceb..a9ad411d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,50 +20,50 @@ # directory, add these directories to sys.path here. If the directory is # relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # Get the project root dir, which is the parent dir of this -#cwd = os.getcwd() -#project_root = os.path.dirname(cwd) +# cwd = os.getcwd() +# project_root = os.path.dirname(cwd) # Insert the project root dir as the first element in the PYTHONPATH. # This lets us ensure that the source package is imported, and that its # version is used. -#sys.path.insert(0, project_root) -sys.path.insert(0, os.path.abspath('../')) +# sys.path.insert(0, project_root) +sys.path.insert(0, os.path.abspath("../")) import pyswarms # -- General configuration --------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # 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' - ] + "sphinx.ext.autodoc", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinx.ext.mathjax", +] -exclude_patterns = ['_build', '**.ipynb_checkpoints'] +exclude_patterns = ["_build", "**.ipynb_checkpoints"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'PySwarms' +project = u"PySwarms" copyright = u"2017, Lester James V. Miranda" # The version info for the project you're documenting, acts as replacement @@ -77,174 +77,177 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to # some non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built # documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a # theme further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as # html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the # top of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon # of the docs. This file should be a Windows icon file (.ico) being # 16x16 or 32x32 pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) # here, relative to this directory. They are copied after the builtin # static files, so a file named "default.css" will overwrite the builtin # "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] + def setup(app): # overrides for wide tables in RTD theme - app.add_stylesheet('theme_overrides.css') # path relative to static + app.add_stylesheet("theme_overrides.css") # path relative to static # 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' +html_last_updated_fmt = "%b %d, %Y" # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names # to template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. # Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. # Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages # will contain a tag referring to it. The value of this option # must be the base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'pyswarmsdoc' +htmlhelp_basename = "pyswarmsdoc" # -- Options for LaTeX output ------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - 'papersize': 'a4paper', - + "papersize": "a4paper", # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', - + # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. - #'preamble': '', + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ - ('index', 'pyswarms.tex', - u'PySwarms Documentation', - u'Lester James V. Miranda', 'manual'), + ( + "index", + "pyswarms.tex", + u"PySwarms Documentation", + u"Lester James V. Miranda", + "manual", + ) ] # The name of an image file (relative to this directory) to place at # the top of the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings # are parts, not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output ------------------------------------ @@ -252,13 +255,17 @@ def setup(app): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'pyswarms', - u'PySwarms Documentation', - [u'Lester James V. Miranda'], 1) + ( + "index", + "pyswarms", + u"PySwarms Documentation", + [u"Lester James V. Miranda"], + 1, + ) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ---------------------------------------- @@ -267,22 +274,25 @@ def setup(app): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'pyswarms', - u'PySwarms Documentation', - u'Lester James V. Miranda', - 'pyswarms', - 'PySwarms is a simple, Python-based, Particle Swarm Optimization (PSO) library.', - 'Research'), + ( + "index", + "pyswarms", + u"PySwarms Documentation", + u"Lester James V. Miranda", + "pyswarms", + "PySwarms is a simple, Python-based, Particle Swarm Optimization (PSO) library.", + "Research", + ) ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 1f209502..f1af7713 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -61,24 +61,39 @@ def compute_gbest(self, swarm): try: # If there are less than (swarm.dimensions + 1) particles they are all connected if swarm.n_particles < swarm.dimensions + 1: - self.neighbor_idx = np.tile(np.arange(swarm.n_particles), (swarm.n_particles, 1)) + 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) else: # Check if the topology is static or dynamic and assign neighbors - if (self.static and self.neighbor_idx is None) or not self.static: - pyramid = Delaunay(swarm.position, qhull_options="QJ0.001 Qbb Qc Qx") + if ( + self.static and self.neighbor_idx is None + ) or not self.static: + pyramid = Delaunay( + swarm.position, qhull_options="QJ0.001 Qbb Qc Qx" + ) indices, index_pointer = pyramid.vertex_neighbor_vertices # Insert all the neighbors for each particle in the idx array self.neighbor_idx = np.array( - [index_pointer[indices[i]:indices[i + 1]] for i in range(swarm.n_particles)] + [ + index_pointer[indices[i] : indices[i + 1]] + for i in range(swarm.n_particles) + ] ) idx_min = np.array( - [swarm.pbest_cost[self.neighbor_idx[i]].argmin() for i in range(len(self.neighbor_idx))] + [ + swarm.pbest_cost[self.neighbor_idx[i]].argmin() + for i in range(len(self.neighbor_idx)) + ] ) best_neighbor = np.array( - [self.neighbor_idx[i][idx_min[i]] for i in range(len(self.neighbor_idx))] + [ + self.neighbor_idx[i][idx_min[i]] + for i in range(len(self.neighbor_idx)) + ] ).astype(int) # Obtain best cost and position diff --git a/pyswarms/backend/topology/random.py b/pyswarms/backend/topology/random.py index 17439621..91464ee6 100644 --- a/pyswarms/backend/topology/random.py +++ b/pyswarms/backend/topology/random.py @@ -15,7 +15,7 @@ from scipy.sparse.csgraph import connected_components, dijkstra # Import from package -from ..import operators as ops +from .. import operators as ops from .base import Topology # Create a logger @@ -65,10 +65,23 @@ def compute_gbest(self, swarm, k): # 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, k) - self.neighbor_idx = np.array([adj_matrix[i].nonzero()[0] for i in range(swarm.n_particles)]) - idx_min = np.array([swarm.pbest_cost[self.neighbor_idx[i]].argmin() for i in range(len(self.neighbor_idx))]) + self.neighbor_idx = np.array( + [ + adj_matrix[i].nonzero()[0] + for i in range(swarm.n_particles) + ] + ) + idx_min = np.array( + [ + swarm.pbest_cost[self.neighbor_idx[i]].argmin() + for i in range(len(self.neighbor_idx)) + ] + ) best_neighbor = np.array( - [self.neighbor_idx[i][idx_min[i]] for i in range(len(self.neighbor_idx))] + [ + self.neighbor_idx[i][idx_min[i]] + for i in range(len(self.neighbor_idx)) + ] ).astype(int) # Obtain best cost and position @@ -186,23 +199,43 @@ def __compute_neighbors(self, swarm, k): adj_matrix = np.identity(swarm.n_particles, dtype=int) neighbor_matrix = np.array( - [np.random.choice( - # Exclude i from the array - np.setdiff1d( - np.arange(swarm.n_particles), np.array([i]) - ), k, replace=False - ) for i in range(swarm.n_particles)]) + [ + np.random.choice( + # Exclude i from the array + np.setdiff1d(np.arange(swarm.n_particles), np.array([i])), + k, + replace=False, + ) + for i in range(swarm.n_particles) + ] + ) # Set random elements to one using the neighbor matrix - adj_matrix[np.arange(swarm.n_particles).reshape(swarm.n_particles, 1), neighbor_matrix] = 1 - adj_matrix[neighbor_matrix, np.arange(swarm.n_particles).reshape(swarm.n_particles, 1)] = 1 - - dist_matrix = dijkstra(adj_matrix, directed=False, return_predecessors=False, unweighted=True) + adj_matrix[ + np.arange(swarm.n_particles).reshape(swarm.n_particles, 1), + neighbor_matrix, + ] = 1 + adj_matrix[ + neighbor_matrix, + np.arange(swarm.n_particles).reshape(swarm.n_particles, 1), + ] = 1 + + dist_matrix = dijkstra( + adj_matrix, + directed=False, + return_predecessors=False, + unweighted=True, + ) # Generate connected graph. - while connected_components(adj_matrix, directed=False, return_labels=False) != 1: + while ( + connected_components( + adj_matrix, directed=False, return_labels=False + ) + != 1 + ): for i, j in itertools.product(range(swarm.n_particles), repeat=2): - if dist_matrix[i][j] == np.inf: - adj_matrix[i][j] = 1 + if dist_matrix[i][j] == np.inf: + adj_matrix[i][j] = 1 return adj_matrix diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index bef98219..660899c4 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -72,12 +72,14 @@ def compute_gbest(self, swarm, p, k): # independently of each other. if k == 1: # The minimum index is itself, no mapping needed. - best_neighbor = swarm.pbest_cost[self.neighbor_idx][:, np.newaxis].argmin( - axis=0 - ) + best_neighbor = swarm.pbest_cost[self.neighbor_idx][ + :, np.newaxis + ].argmin(axis=0) else: idx_min = swarm.pbest_cost[self.neighbor_idx].argmin(axis=1) - best_neighbor = self.neighbor_idx[np.arange(len(self.neighbor_idx)), idx_min] + best_neighbor = self.neighbor_idx[ + np.arange(len(self.neighbor_idx)), idx_min + ] # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) best_pos = swarm.pbest_pos[ diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index 8c666b2d..8a8f3750 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -62,7 +62,9 @@ def compute_gbest(self, swarm): """ try: if self.neighbor_idx is None: - self.neighbor_idx = np.tile(np.arange(swarm.n_particles), (swarm.n_particles, 1)) + 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) except AttributeError: diff --git a/pyswarms/backend/topology/von_neumann.py b/pyswarms/backend/topology/von_neumann.py index ad44cb85..168ec8ec 100644 --- a/pyswarms/backend/topology/von_neumann.py +++ b/pyswarms/backend/topology/von_neumann.py @@ -71,8 +71,9 @@ def delannoy(d, r): if d == 0 or r == 0: return 1 else: - del_number = (VonNeumann.delannoy(d - 1, r) - + VonNeumann.delannoy(d - 1, r - 1) - + VonNeumann.delannoy(d, r - 1) - ) + del_number = ( + VonNeumann.delannoy(d - 1, r) + + VonNeumann.delannoy(d - 1, r - 1) + + VonNeumann.delannoy(d, r - 1) + ) return del_number diff --git a/pyswarms/discrete/binary.py b/pyswarms/discrete/binary.py index 72d8d536..93c481e6 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -149,7 +149,9 @@ def __init__( # Initialize the topology self.top = Ring(static=False) - def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): + def optimize( + self, objective_func, iters, print_step=1, verbose=1, **kwargs + ): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -174,13 +176,21 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): the local best cost and the local best position among the swarm. """ - cli_print("Arguments Passed to Objective Function: {}".format(kwargs), - verbose, 2, logger=self.logger) + cli_print( + "Arguments Passed to Objective Function: {}".format(kwargs), + verbose, + 2, + logger=self.logger, + ) for i in range(iters): # 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.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 ) @@ -192,7 +202,9 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): # Print to console if i % print_step == 0: cli_print( - "Iteration {}/{}, cost: {}".format(i + 1, iters, np.min(self.swarm.best_cost)), + "Iteration {}/{}, cost: {}".format( + i + 1, iters, np.min(self.swarm.best_cost) + ), verbose, 2, logger=self.logger, diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py index d127e582..27edc268 100644 --- a/pyswarms/single/general_optimizer.py +++ b/pyswarms/single/general_optimizer.py @@ -206,8 +206,8 @@ def __init__( if ( self.r <= 0 or not 0 - <= VonNeumann.delannoy(self.swarm.dimensions, self.r) - <= self.n_particles - 1 + <= VonNeumann.delannoy(self.swarm.dimensions, self.r) + <= self.n_particles - 1 ): raise ValueError( "The range must be set such that the computed" @@ -215,7 +215,9 @@ def __init__( "between 0 and the no. of particles." ) - def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): + def optimize( + self, objective_func, iters, print_step=1, verbose=1, **kwargs + ): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -240,19 +242,29 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): the global best cost and the global best position. """ - cli_print("Arguments Passed to Objective Function: {}".format(kwargs), - verbose, 2, logger=self.logger) + cli_print( + "Arguments Passed to Objective Function: {}".format(kwargs), + verbose, + 2, + logger=self.logger, + ) for i in range(iters): # 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.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 # If the topology is a ring topology just use the local minimum - if isinstance(self.top, Ring) and not isinstance(self.top, VonNeumann): + if isinstance(self.top, Ring) and not isinstance( + self.top, VonNeumann + ): # Update gbest from neighborhood self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( self.swarm, self.p, self.k @@ -279,10 +291,12 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): # Print to console if i % print_step == 0: cli_print( - "Iteration {}/{}, cost: {}".format(i + 1, iters, self.swarm.best_cost), + "Iteration {}/{}, cost: {}".format( + i + 1, iters, self.swarm.best_cost + ), verbose, 2, - logger=self.logger + logger=self.logger, ) # Save to history hist = self.ToHistory( @@ -290,7 +304,7 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): mean_pbest_cost=np.mean(self.swarm.pbest_cost), mean_neighbor_cost=self.swarm.best_cost, position=self.swarm.position, - velocity=self.swarm.velocity + velocity=self.swarm.velocity, ) self._populate_history(hist) # Verify stop criteria based on the relative acceptable cost ftol @@ -314,4 +328,4 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): end_report( final_best_cost, final_best_pos, verbose, logger=self.logger ) - return(final_best_cost, final_best_pos) + return (final_best_cost, final_best_pos) diff --git a/pyswarms/single/global_best.py b/pyswarms/single/global_best.py index 36bc812e..dbbc35ba 100644 --- a/pyswarms/single/global_best.py +++ b/pyswarms/single/global_best.py @@ -131,7 +131,9 @@ def __init__( # Initialize the topology self.top = Star() - def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): + def optimize( + self, objective_func, iters, print_step=1, verbose=1, **kwargs + ): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -156,13 +158,21 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): the global best cost and the global best position. """ - cli_print("Arguments Passed to Objective Function: {}".format(kwargs), - verbose, 2, logger=self.logger) + cli_print( + "Arguments Passed to Objective Function: {}".format(kwargs), + verbose, + 2, + logger=self.logger, + ) for i in range(iters): # 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.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 ) @@ -175,7 +185,9 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): # Print to console if i % print_step == 0: cli_print( - "Iteration {}/{}, cost: {}".format(i + 1, iters, self.swarm.best_cost), + "Iteration {}/{}, cost: {}".format( + i + 1, iters, self.swarm.best_cost + ), verbose, 2, logger=self.logger, diff --git a/pyswarms/single/local_best.py b/pyswarms/single/local_best.py index 8e64b041..06cb8ad2 100644 --- a/pyswarms/single/local_best.py +++ b/pyswarms/single/local_best.py @@ -113,7 +113,7 @@ def __init__( center=1.00, ftol=-np.inf, init_pos=None, - static=False + static=False, ): """Initialize the swarm @@ -178,7 +178,9 @@ def __init__( # Initialize the topology self.top = Ring(static=static) - def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): + def optimize( + self, objective_func, iters, print_step=1, verbose=1, **kwargs + ): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -203,13 +205,21 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): the local best cost and the local best position among the swarm. """ - cli_print("Arguments Passed to Objective Function: {}".format(kwargs), - verbose, 2, logger=self.logger) + cli_print( + "Arguments Passed to Objective Function: {}".format(kwargs), + verbose, + 2, + logger=self.logger, + ) for i in range(iters): # 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.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 ) @@ -221,7 +231,9 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): # Print to console if i % print_step == 0: cli_print( - "Iteration {}/{}, cost: {}".format(i + 1, iters, np.min(self.swarm.best_cost)), + "Iteration {}/{}, cost: {}".format( + i + 1, iters, np.min(self.swarm.best_cost) + ), verbose, 2, logger=self.logger, diff --git a/pyswarms/utils/__init__.py b/pyswarms/utils/__init__.py index feb2c0bd..ea80fdaf 100644 --- a/pyswarms/utils/__init__.py +++ b/pyswarms/utils/__init__.py @@ -1 +1,5 @@ """ Pacakge for various utilities """ + +from .reporter.reporter import Reporter + +__all__ = ["Reporter"] diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index 78e1173b..027c14a5 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -62,11 +62,11 @@ class Designer(object): default=["x-axis", "y-axis", "z-axis"], ) limits = attrib( - validator=instance_of((list, tuple)), default=[(-1, 1), (-1, 1), (-1, 1)] + validator=instance_of((list, tuple)), + default=[(-1, 1), (-1, 1), (-1, 1)], ) colormap = attrib( - validator=instance_of(colors.Colormap), - default=cm.viridis, + validator=instance_of(colors.Colormap), default=cm.viridis ) diff --git a/pyswarms/utils/reporter/__init__.py b/pyswarms/utils/reporter/__init__.py new file mode 100644 index 00000000..15390076 --- /dev/null +++ b/pyswarms/utils/reporter/__init__.py @@ -0,0 +1,5 @@ +"""Reporter module""" + +from .reporter import Reporter + +__all__ = ["Reporter"] diff --git a/pyswarms/utils/reporter/reporter.py b/pyswarms/utils/reporter/reporter.py new file mode 100644 index 00000000..61bffe6e --- /dev/null +++ b/pyswarms/utils/reporter/reporter.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +import os +import yaml +import pprint +import logging + + +class Reporter(object): + """A Reporter object that abstracts various logging capabilities + + To set-up a Reporter, simply perform the following tasks: + + .. code-block:: python + + from pyswarms.utils import Reporter + + rep = Reporter() + rep.log("Here's my message", lvl=20) + + This will set-up a reporter with a default configuration that + logs to a file, `report.log`, on the current working directory. + You can change the log path by passing a string to the `log_path` + parameter: + + .. code-block:: python + + from pyswarms.utils import Reporter + + rep = Reporter(log_path="/path/to/log/file.log") + rep.log("Here's my message", lvl=20) + + If you are working on a module and you have an existing logger, + you can pass that logger instance during initialization: + + .. code-block:: python + + # mymodule.py + from pyswarms.utils import Reporter + + # An existing logger in a module + logger = logging.getLogger(__name__) + rep = Reporter(logger=logger) + + Lastly, if you have your own logger configuration (YAML file), + then simply pass that to the `config_path` parameter. This + overrides the default configuration (including `log_path`): + + .. code-block:: python + + from pyswarms.utils import Reporter + + rep = Reporter(config_path="/path/to/config/file.yml") + rep.log("Here's my message", lvl=20) + + """ + + def __init__( + self, log_path=None, config_path=None, logger=None, printer=None + ): + """Initialize the reporter + + Attributes + ---------- + log_path : str (default is :code:`None`) + Sets the default log path (overriden when :code:`path` is given to + :code:`_setup_logger()`) + config_path : str (default is :code:`None`) + Sets the configuration path for custom loggers + logger : logging.Logger (default is :code:`None`) + The logger object. By default, it creates a new :code:`Logger` + instance + printer : pprint.PrettyPrinter (default is :code:`None`) + A printer object. By default, it creates a :code:`PrettyPrinter` + instance with default values + """ + self.logger = logger or logging.getLogger(__name__) + self.printer = printer or pprint.PrettyPrinter() + self.log_path = log_path or (os.getcwd() + "/report.log") + self._env_key = "LOG_CFG" + self._default_config = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "standard": { + "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + } + }, + "handlers": { + "default": { + "level": "INFO", + "class": "logging.StreamHandler", + "formatter": "standard", + }, + "file_default": { + "level": "INFO", + "formatter": "standard", + "class": "logging.handlers.RotatingFileHandler", + "filename": self.log_path, + "encoding": "utf8", + "maxBytes": 10485760, + "backupCount": 20, + }, + }, + "loggers": { + "": { + "handlers": ["default", "file_default"], + "level": "INFO", + "propagate": True, + } + }, + } + self._setup_logger(config_path) + + def log(self, msg, lvl=20, *args, **kwargs): + """Log a message within a set level + + This method abstracts the logging.Logger.log() method. We use this + method during major state changes, errors, or critical events during + the optimization run. + + You can check logging levels on this `link`_. In essence, DEBUG is 10, + INFO is 20, WARNING is 30, ERROR is 40, and CRITICAL is 50. + + .. link: https://docs.python.org/3/library/logging.html#logging-levels + + Parameters + ---------- + msg : str + Message to be logged + lvl : int (default is 20) + Logging level + """ + self.logger.log(lvl, msg, *args, **kwargs) + + def print(self, msg, verbosity, threshold=0): + """Print a message into console + + This method can be called during non-system calls or minor state + changes. In practice, we call this method when reporting the cost + on a given timestep. + + Parameters + ---------- + msg : str + Message to be printed + verbosity : int + Verbosity parameter, prints message when it's greater than the + threshold + threshold : int (default is 0) + Threshold parameer, prints message when it's lesser than the + verbosity + """ + if verbosity > threshold: + self.printer.pprint(msg) + else: + pass + + def _setup_logger(self, path=None): + """Set-up the logger with default values + + This method is called right after initializing the Reporter module. + If no path is supplied, then it loads a default configuration. + You can view the defaults via the Reporter._default_config attribute. + + + Parameters + ---------- + path : str + Path to a YAML configuration. If not supplied, uses + a default config. + """ + value = path or os.getenv(self._env_key, None) + try: + with open(path, "rt") as f: + config = yaml.safe_load(f.read()) + logging.config.dictConfig(config) + except (TypeError, FileNotFoundError): + self._load_defaults() + + def _load_defaults(self): + """Load default logging configuration""" + logging.config.dictConfig(self._default_config) diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index 4aade1b1..be5a5014 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -14,6 +14,7 @@ @pytest.fixture(scope="module") 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], @@ -53,4 +54,5 @@ def swarm(): "best_pos": np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]), "options": {'c1': 0.5, 'c2': 0.3, 'w': 0.9}, } + # fmt: on return Swarm(**attrs_at_t) diff --git a/tests/backend/topology/test_pyramid.py b/tests/backend/topology/test_pyramid.py index 31583428..cce94e19 100644 --- a/tests/backend/topology/test_pyramid.py +++ b/tests/backend/topology/test_pyramid.py @@ -17,7 +17,7 @@ def test_compute_gbest_return_values(swarm, static): 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)) + assert pos == pytest.approx(expected_pos) @pytest.mark.parametrize("static", [True, False]) diff --git a/tests/backend/topology/test_random.py b/tests/backend/topology/test_random.py index 1b12a5dc..9cc406f8 100644 --- a/tests/backend/topology/test_random.py +++ b/tests/backend/topology/test_random.py @@ -18,7 +18,7 @@ def test_update_gbest_neighborhood(swarm, k, static): 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)) + assert pos == pytest.approx(expected_pos) @pytest.mark.parametrize("static", [True, False]) @@ -63,6 +63,7 @@ def test_compute_neighbors_adjacency_matrix(swarm, k, static): np.random.seed(1) topology = Random(static=static) adj_matrix = topology._Random__compute_neighbors(swarm, 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], @@ -74,6 +75,7 @@ def test_compute_neighbors_adjacency_matrix(swarm, k, static): [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]) diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index c7723724..9dc6e7a5 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -19,7 +19,7 @@ def test_update_gbest_neighborhood(swarm, p, k, static): 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)) + assert pos == pytest.approx(expected_pos) @pytest.mark.parametrize("static", [True, False]) diff --git a/tests/backend/topology/test_star.py b/tests/backend/topology/test_star.py index 1dc72415..957ecdad 100644 --- a/tests/backend/topology/test_star.py +++ b/tests/backend/topology/test_star.py @@ -16,7 +16,7 @@ def test_compute_gbest_return_values(swarm): 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)) + assert pos == pytest.approx(expected_pos) @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) diff --git a/tests/backend/topology/test_von_neumann.py b/tests/backend/topology/test_von_neumann.py index e34a9000..1e27cd9a 100644 --- a/tests/backend/topology/test_von_neumann.py +++ b/tests/backend/topology/test_von_neumann.py @@ -18,7 +18,7 @@ def test_update_gbest_neighborhood(swarm, p, 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)) + assert pos == pytest.approx(expected_pos) @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) @@ -56,11 +56,12 @@ def test_neighbor_idx(swarm, p, r): @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]) - print(VonNeumann.delannoy(m, n)) + # fmt: on assert VonNeumann.delannoy(m, n) in expected_values diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py index 0d278d4f..cb78cf1f 100644 --- a/tests/optimizers/conftest.py +++ b/tests/optimizers/conftest.py @@ -18,7 +18,9 @@ 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 = GeneralOptimizerPSO( + 10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=topology + ) pso.optimize(sphere, 1000, verbose=0) return pso @@ -27,7 +29,9 @@ def general_opt_history(topology): 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 = GeneralOptimizerPSO( + 10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=topology + ) pso.optimize(sphere, 10, verbose=0) pso.reset() return pso @@ -97,6 +101,7 @@ def options(): return options_ +# fmt: off @pytest.fixture(params=[ Star(), Ring(static=False), Ring(static=True), @@ -104,6 +109,7 @@ def options(): Random(static=False), Random(static=True), VonNeumann() ]) +# fmt: on def topology(request): """Parametrized topology parameter""" topology_ = request.param diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py index 779dad88..eac1d5b2 100644 --- a/tests/optimizers/test_general_optimizer.py +++ b/tests/optimizers/test_general_optimizer.py @@ -108,7 +108,7 @@ def test_invalid_r_or_p_values(options): [ {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": -1}, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 6}, - {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 0.5} + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 0.5}, ], ) def test_invalid_k_value(options, static): @@ -118,10 +118,7 @@ def test_invalid_k_value(options, static): GeneralOptimizerPSO(5, 2, options, Random(static=static)) -@pytest.mark.parametrize( - "topology", - [object(), int(), dict()] -) +@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): @@ -139,7 +136,9 @@ def test_topology_type_exception(options, topology): 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) + GeneralOptimizerPSO( + 5, 2, options=options, topology=topology, bounds=bounds + ) @pytest.mark.parametrize( @@ -152,7 +151,9 @@ def test_bounds_size_exception(bounds, options, topology): 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) + GeneralOptimizerPSO( + 5, 2, options=options, topology=topology, bounds=bounds + ) @pytest.mark.parametrize( @@ -165,7 +166,9 @@ def test_bounds_maxmin_exception(bounds, options, topology): 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) + GeneralOptimizerPSO( + 5, 2, options=options, topology=topology, bounds=bounds + ) @pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) @@ -173,7 +176,13 @@ 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, options=options, topology=topology) + GeneralOptimizerPSO( + 5, + 2, + velocity_clamp=velocity_clamp, + options=options, + topology=topology, + ) @pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) @@ -181,14 +190,22 @@ 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, options=options, topology=topology) + GeneralOptimizerPSO( + 5, + 2, + velocity_clamp=velocity_clamp, + options=options, + topology=topology, + ) @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) + GeneralOptimizerPSO( + 5, 2, center=center, options=options, topology=topology + ) def test_reset_default_values(gbest_reset): @@ -216,6 +233,8 @@ def test_training_history_shape(gbest_history, history, 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 = GeneralOptimizerPSO( + 10, 2, options=options, topology=topology, ftol=1e-1 + ) pso.optimize(sphere, 2000, verbose=0) assert np.array(pso.cost_history).shape != (2000,) diff --git a/tests/optimizers/test_objective_func_with_kwargs.py b/tests/optimizers/test_objective_func_with_kwargs.py index c73442dc..169fd198 100644 --- a/tests/optimizers/test_objective_func_with_kwargs.py +++ b/tests/optimizers/test_objective_func_with_kwargs.py @@ -16,64 +16,66 @@ def rosenbrock_with_args(x, a, b): return f -@pytest.mark.parametrize('func', [ - rosenbrock_with_args -]) +@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} + 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) + opt_ps = GlobalBestPSO( + n_particles=100, dimensions=2, options=options, bounds=bounds + ) # run it - cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1 , b=100) + cost, pos = opt_ps.optimize( + func, 1000, print_step=10, verbose=3, 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 -]) +@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} + 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) + opt_ps = GlobalBestPSO( + n_particles=100, dimensions=2, options=options, bounds=bounds + ) # run it - cost, pos = opt_ps.optimize(func, 1000, verbose=3, a=1 , b=100) + cost, pos = opt_ps.optimize(func, 1000, verbose=3, 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 -]) +@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} + 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) + opt_ps = GlobalBestPSO( + n_particles=100, dimensions=2, options=options, bounds=bounds + ) # run it cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3) @@ -83,41 +85,43 @@ def test_global_no_kwargs(func): assert np.isclose(pos[1], 1.0, rtol=1e-03) -@pytest.mark.parametrize('func', [ - rosenbrock_with_args -]) +@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} + 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) + opt_ps = LocalBestPSO( + n_particles=100, dimensions=2, options=options, bounds=bounds + ) # run it - cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1, b=100) + cost, pos = opt_ps.optimize( + func, 1000, print_step=10, verbose=3, 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 -]) +@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} + 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) + opt_ps = LocalBestPSO( + n_particles=100, dimensions=2, options=options, bounds=bounds + ) # run it cost, pos = opt_ps.optimize(func, iters=1000, print_step=10, verbose=3) @@ -127,121 +131,125 @@ def test_local_no_kwargs(func): assert np.isclose(pos[1], 1.0, rtol=1e-03) -@pytest.mark.parametrize('func', [ - rosenbrock -]) +@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} + 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) + 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, print_step=10, verbose=3, a=1) - assert 'unexpected keyword' in str(excinfo.value) + assert "unexpected keyword" in str(excinfo.value) -@pytest.mark.parametrize('func', [ - rosenbrock_with_args -]) +@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} + 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) + 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, print_step=10, verbose=3, a=1) - assert 'missing 1 required positional argument' in str(excinfo.value) + assert "missing 1 required positional argument" in str(excinfo.value) -@pytest.mark.parametrize('func', [ - rosenbrock -]) +@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} + 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) + 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, verbose=3, a=1) - assert 'unexpected keyword' in str(excinfo.value) + assert "unexpected keyword" in str(excinfo.value) -@pytest.mark.parametrize('func', [ - rosenbrock_with_args -]) +@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} + 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) + 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, verbose=3, a=1) - assert 'missing 1 required positional argument' in str(excinfo.value) + assert "missing 1 required positional argument" in str(excinfo.value) -@pytest.mark.parametrize('func', [ - rosenbrock_with_args -]) +@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} + 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) + 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, verbose=3, c=1, d=100) - assert 'unexpected keyword' in str(excinfo.value) + cost, pos = opt_ps.optimize( + func, 1000, print_step=10, verbose=3, c=1, d=100 + ) + assert "unexpected keyword" in str(excinfo.value) -@pytest.mark.parametrize('func', [ - rosenbrock_with_args -]) +@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} + 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) + 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, print_step=10, verbose=3, c=1, d=100) - assert 'unexpected keyword' in str(excinfo.value) + cost, pos = opt_ps.optimize( + func, 1000, print_step=10, verbose=3, c=1, d=100 + ) + assert "unexpected keyword" in str(excinfo.value) diff --git a/tests/utils/functions/test_singleobj_bounds.py b/tests/utils/functions/test_singleobj_bounds.py index 5390f7f9..17fa9379 100644 --- a/tests/utils/functions/test_singleobj_bounds.py +++ b/tests/utils/functions/test_singleobj_bounds.py @@ -33,7 +33,6 @@ } - def test_ackley_bound_fail(outbound): """Test ackley bound exception""" with pytest.raises(ValueError): diff --git a/tests/utils/functions/test_singleobj_dims.py b/tests/utils/functions/test_singleobj_dims.py index 9ab0e38d..a7f7c328 100644 --- a/tests/utils/functions/test_singleobj_dims.py +++ b/tests/utils/functions/test_singleobj_dims.py @@ -41,6 +41,7 @@ def test_easom_dim_fail(outdim): with pytest.raises(IndexError): fx.easom(outdim) + def test_goldstein_dim_fail(outdim): """Test goldstein dim exception""" with pytest.raises(IndexError): diff --git a/tests/utils/functions/test_singleobj_return.py b/tests/utils/functions/test_singleobj_return.py index 3aced1b5..e3cf8c74 100644 --- a/tests/utils/functions/test_singleobj_return.py +++ b/tests/utils/functions/test_singleobj_return.py @@ -10,9 +10,6 @@ from pyswarms.utils.functions import single_obj as fx - - - def test_ackley_output(common_minima): """Tests ackley function output.""" assert np.isclose(fx.ackley(common_minima), np.zeros(3)).all() @@ -20,45 +17,38 @@ def test_ackley_output(common_minima): def test_beale_output(common_minima2): """Tests beale function output.""" - assert np.isclose( - fx.beale([3, 0.5] * common_minima2), np.zeros(3) - ).all() + assert np.isclose(fx.beale([3, 0.5] * common_minima2), np.zeros(3)).all() def test_booth_output(common_minima2): """Test booth function output.""" - assert np.isclose( - fx.booth([1, 3] * common_minima2), np.zeros(3) - ).all() + assert np.isclose(fx.booth([1, 3] * common_minima2), np.zeros(3)).all() def test_bukin6_output(common_minima2): """Test bukin function output.""" - assert np.isclose( - fx.bukin6([-10, 1] * common_minima2), np.zeros(3) - ).all() + assert np.isclose(fx.bukin6([-10, 1] * common_minima2), np.zeros(3)).all() @pytest.mark.parametrize( "x", [ - np.array([[1.34941, -1.34941], - [1.34941, 1.34941], - [-1.34941, 1.34941], - [-1.34941, -1.34941]]) + np.array( + [ + [1.34941, -1.34941], + [1.34941, 1.34941], + [-1.34941, 1.34941], + [-1.34941, -1.34941], + ] + ) ], ) @pytest.mark.parametrize( - "minima", - [ - np.array([-2.06261, -2.06261, -2.06261, -2.06261]) - ], + "minima", [np.array([-2.06261, -2.06261, -2.06261, -2.06261])] ) def test_crossintray_output(x, minima): """Tests crossintray function output.""" - assert np.isclose( - fx.crossintray(x), minima - ).all() + assert np.isclose(fx.crossintray(x), minima).all() def test_easom_output(common_minima2): @@ -71,7 +61,8 @@ def test_easom_output(common_minima2): def test_eggholder_output(common_minima2): """Tests eggholder function output.""" assert np.isclose( - fx.eggholder([512, 404.3219] * common_minima2), (-959.6407 * np.ones(3)) + fx.eggholder([512, 404.3219] * common_minima2), + (-959.6407 * np.ones(3)), ).all() @@ -85,39 +76,40 @@ def test_goldstein_output(common_minima2): @pytest.mark.parametrize( "x", [ - np.array([[3.0, 2.0], - [-2.805118, 3.131312], - [-3.779310, -3.283186], - [3.584428, -1.848126]]) + np.array( + [ + [3.0, 2.0], + [-2.805118, 3.131312], + [-3.779310, -3.283186], + [3.584428, -1.848126], + ] + ) ], ) def test_himmelblau_output(x): """Tests himmelblau function output.""" - assert np.isclose( - fx.himmelblau(x), np.zeros(4) - ).all() + assert np.isclose(fx.himmelblau(x), np.zeros(4)).all() @pytest.mark.parametrize( "x", [ - np.array([[8.05502, 9.66459], - [-8.05502, 9.66459], - [8.05502, -9.66459], - [-8.05502, -9.66459]]) + np.array( + [ + [8.05502, 9.66459], + [-8.05502, 9.66459], + [8.05502, -9.66459], + [-8.05502, -9.66459], + ] + ) ], ) @pytest.mark.parametrize( - "minima", - [ - np.array([-19.2085, -19.2085, -19.2085, -19.2085]) - ], + "minima", [np.array([-19.2085, -19.2085, -19.2085, -19.2085])] ) def test_holdertable_output(x, minima): """Tests holdertable function output.""" - assert np.isclose( - fx.holdertable(x), minima - ).all() + assert np.isclose(fx.holdertable(x), minima).all() def test_levi_output(common_minima2): diff --git a/travis_pypi_setup.py b/travis_pypi_setup.py index 844491f9..a0354fde 100644 --- a/travis_pypi_setup.py +++ b/travis_pypi_setup.py @@ -20,9 +20,10 @@ from urllib.request import urlopen -GITHUB_REPO = 'ljvmiranda921/pyswarms' +GITHUB_REPO = "ljvmiranda921/pyswarms" TRAVIS_CONFIG_FILE = os.path.join( - os.path.dirname(os.path.abspath(__file__)), '.travis.yml') + os.path.dirname(os.path.abspath(__file__)), ".travis.yml" +) def load_key(pubkey): @@ -37,7 +38,7 @@ def load_key(pubkey): return load_pem_public_key(pubkey.encode(), default_backend()) except ValueError: # workaround for https://github.com/travis-ci/travis-api/issues/196 - pubkey = pubkey.replace('BEGIN RSA', 'BEGIN').replace('END RSA', 'END') + pubkey = pubkey.replace("BEGIN RSA", "BEGIN").replace("END RSA", "END") return load_pem_public_key(pubkey.encode(), default_backend()) @@ -57,13 +58,13 @@ def fetch_public_key(repo): Travis API docs: http://docs.travis-ci.com/api/#repository-keys """ - keyurl = 'https://api.travis-ci.org/repos/{0}/key'.format(repo) + keyurl = "https://api.travis-ci.org/repos/{0}/key".format(repo) data = json.loads(urlopen(keyurl).read().decode()) - if 'key' not in data: + if "key" not in data: errmsg = "Could not find public key for repo: {}.\n".format(repo) errmsg += "Have you already added your GitHub repo to Travis?" raise ValueError(errmsg) - return data['key'] + return data["key"] def prepend_line(filepath, line): @@ -73,7 +74,7 @@ def prepend_line(filepath, line): lines.insert(0, line) - with open(filepath, 'w') as f: + with open(filepath, "w") as f: f.writelines(lines) @@ -85,7 +86,7 @@ def load_yaml_config(filepath): def save_yaml_config(filepath, config): """Save yaml config file at the given path.""" - with open(filepath, 'w') as f: + with open(filepath, "w") as f: yaml.dump(config, f, default_flow_style=False) @@ -93,12 +94,14 @@ def update_travis_deploy_password(encrypted_password): """Put `encrypted_password` into the deploy section of .travis.yml.""" config = load_yaml_config(TRAVIS_CONFIG_FILE) - config['deploy']['password'] = dict(secure=encrypted_password) + config["deploy"]["password"] = dict(secure=encrypted_password) save_yaml_config(TRAVIS_CONFIG_FILE, config) - line = ('# This file was autogenerated and will overwrite' - ' each time you run travis_pypi_setup.py\n') + line = ( + "# This file was autogenerated and will overwrite" + " each time you run travis_pypi_setup.py\n" + ) prepend_line(TRAVIS_CONFIG_FILE, line) @@ -110,18 +113,23 @@ def main(args): password. """ public_key = fetch_public_key(args.repo) - password = args.password or getpass('PyPI password: ') + password = args.password or getpass("PyPI password: ") update_travis_deploy_password(encrypt(public_key, password.encode())) print("Wrote encrypted password to .travis.yml -- you're ready to deploy") -if '__main__' == __name__: +if "__main__" == __name__: import argparse + parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--repo', default=GITHUB_REPO, - help='GitHub repo (default: %s)' % GITHUB_REPO) - parser.add_argument('--password', - help='PyPI password (will prompt if not provided)') + parser.add_argument( + "--repo", + default=GITHUB_REPO, + help="GitHub repo (default: %s)" % GITHUB_REPO, + ) + parser.add_argument( + "--password", help="PyPI password (will prompt if not provided)" + ) args = parser.parse_args() main(args) From 3fb53c69ad81cae7e2f80bd80e1ed7188f577bf8 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Fri, 17 Aug 2018 19:26:56 +0900 Subject: [PATCH 2/8] [WIP] Add tqdm for progress bars --- pyswarms/utils/reporter/reporter.py | 40 ++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/pyswarms/utils/reporter/reporter.py b/pyswarms/utils/reporter/reporter.py index 61bffe6e..c165d53d 100644 --- a/pyswarms/utils/reporter/reporter.py +++ b/pyswarms/utils/reporter/reporter.py @@ -3,6 +3,7 @@ import yaml import pprint import logging +from tqdm import trange class Reporter(object): @@ -76,6 +77,7 @@ def __init__( self.logger = logger or logging.getLogger(__name__) self.printer = printer or pprint.PrettyPrinter() self.log_path = log_path or (os.getcwd() + "/report.log") + self._bar_fmt = "{l_bar}{bar}|{n_fmt}/{total_fmt}[{postfix}]" self._env_key = "LOG_CFG" self._default_config = { "version": 1, @@ -171,7 +173,7 @@ def _setup_logger(self, path=None): """ value = path or os.getenv(self._env_key, None) try: - with open(path, "rt") as f: + with open(value, "rt") as f: config = yaml.safe_load(f.read()) logging.config.dictConfig(config) except (TypeError, FileNotFoundError): @@ -180,3 +182,39 @@ def _setup_logger(self, path=None): def _load_defaults(self): """Load default logging configuration""" logging.config.dictConfig(self._default_config) + + def pbar(self, iters, desc=None): + """Create a tqdm iterable + + You can use this method to create progress bars. It uses a set + of abstracted methods from tqdm: + + .. code-block:: python + + from pyswarms.utils import Reporter + + rep = Reporter + # Create a progress bar + for i in rep.pbar(100, name="My progress bar") + pass + """ + self.t = trange(iters, desc=desc, bar_format=self._bar_fmt) + return self.t + + def hook(self, *args, **kwargs): + """Set a hook on the progress bar + + Method for creating a postfix in tqdm. In practice we use this + to report the best cost found during an iteration: + + .. code-block:: python + + from pyswarms.utils import Reporter + + rep = Reporter + # Create a progress bar + for i in rep.pbar(100, name="My progress bar") + global_best = compute() + rep.hook(global_best=global_best) + """ + self.t.set_postfix(*args, **kwargs) From 28d430adefb4de5f0a583efd499875727a056069 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Fri, 17 Aug 2018 19:32:39 +0900 Subject: [PATCH 3/8] [WIP] Add tqdm in requirements --- requirements_dev.txt | 3 ++- setup.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index a2d2ed01..cf24109b 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -12,7 +12,8 @@ PyYAML==3.13 # pyup: ignore future==0.16.0 scipy>=0.17.0 numpy>=1.13.0 +tqdm==4.24.0 matplotlib>=1.3.1 pytest==3.7.1 attrs==18.1.0 -pre-commit==1.10.5 \ No newline at end of file +pre-commit==1.10.5 diff --git a/setup.py b/setup.py index 44fc25c5..f8574a13 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ "mock==2.0.0", "pytest==3.6.4", "attrs==18.1.0", + "tqdm==4.24.0", "pre-commit", ] @@ -31,6 +32,7 @@ "numpy>=1.13.0", "matplotlib>=1.3.1", "mock==2.0.0", + "tqdm==4.24.0", "pytest==3.6.4", "attrs==18.1.0", "pre-commit", From 8ef2ae467d675a3deeadac58cb945e6daeca3c61 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Fri, 17 Aug 2018 19:37:30 +0900 Subject: [PATCH 4/8] [WIP] Better docstrings for tqdm methods --- pyswarms/utils/reporter/reporter.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pyswarms/utils/reporter/reporter.py b/pyswarms/utils/reporter/reporter.py index c165d53d..33868c5e 100644 --- a/pyswarms/utils/reporter/reporter.py +++ b/pyswarms/utils/reporter/reporter.py @@ -193,10 +193,22 @@ def pbar(self, iters, desc=None): from pyswarms.utils import Reporter - rep = Reporter + rep = Reporter() # Create a progress bar - for i in rep.pbar(100, name="My progress bar") + for i in rep.pbar(100, name="Optimizer") pass + + Parameters + ---------- + iters : iterable + Iterable passed to the tqdm instance + desc : str + Name of the progress bar that will be displayed + + Returns + ------- + tqdm._tqdm.tqdm + A tqdm iterable """ self.t = trange(iters, desc=desc, bar_format=self._bar_fmt) return self.t @@ -211,10 +223,10 @@ def hook(self, *args, **kwargs): from pyswarms.utils import Reporter - rep = Reporter + rep = Reporter() # Create a progress bar - for i in rep.pbar(100, name="My progress bar") - global_best = compute() - rep.hook(global_best=global_best) + for i in rep.pbar(100, name="Optimizer") + best_cost = compute() + rep.hook(best_cost=best_cost) """ self.t.set_postfix(*args, **kwargs) From c83460f6a9e02e400c788e67e0cfa7ea3d3cf602 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Fri, 17 Aug 2018 20:01:23 +0900 Subject: [PATCH 5/8] [WIP] Better docstrings --- pyswarms/utils/reporter/reporter.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyswarms/utils/reporter/reporter.py b/pyswarms/utils/reporter/reporter.py index 33868c5e..01853c5d 100644 --- a/pyswarms/utils/reporter/reporter.py +++ b/pyswarms/utils/reporter/reporter.py @@ -3,6 +3,7 @@ import yaml import pprint import logging +import logging.config from tqdm import trange @@ -77,7 +78,7 @@ def __init__( self.logger = logger or logging.getLogger(__name__) self.printer = printer or pprint.PrettyPrinter() self.log_path = log_path or (os.getcwd() + "/report.log") - self._bar_fmt = "{l_bar}{bar}|{n_fmt}/{total_fmt}[{postfix}]" + self._bar_fmt = "{l_bar}{bar}|{n_fmt}/{total_fmt}{postfix}" self._env_key = "LOG_CFG" self._default_config = { "version": 1, @@ -200,8 +201,8 @@ def pbar(self, iters, desc=None): Parameters ---------- - iters : iterable - Iterable passed to the tqdm instance + iters : int + Maximum range passed to the tqdm instance desc : str Name of the progress bar that will be displayed From 57b8dcd80b63435aa84bce7f1950f71501128718 Mon Sep 17 00:00:00 2001 From: Lj Miranda <12949683+ljvmiranda921@users.noreply.github.com> Date: Sat, 18 Aug 2018 16:20:22 +0900 Subject: [PATCH 6/8] Port logging to reporter (#230) --- pyswarms/backend/topology/base.py | 16 ++--- pyswarms/base/base_discrete.py | 46 ++------------ pyswarms/base/base_single.py | 47 ++------------ pyswarms/discrete/binary.py | 48 ++++++-------- pyswarms/single/general_optimizer.py | 54 +++++++--------- pyswarms/single/global_best.py | 51 ++++++--------- pyswarms/single/local_best.py | 52 +++++++-------- pyswarms/utils/console_utils.py | 63 ------------------- tests/optimizers/conftest.py | 21 +++---- tests/optimizers/test_general_optimizer.py | 8 +-- tests/optimizers/test_global_best.py | 6 +- tests/optimizers/test_local_best.py | 6 +- .../test_objective_func_with_kwargs.py | 32 ++++------ 13 files changed, 126 insertions(+), 324 deletions(-) delete mode 100644 pyswarms/utils/console_utils.py diff --git a/pyswarms/backend/topology/base.py b/pyswarms/backend/topology/base.py index 38a670a7..63129ad5 100644 --- a/pyswarms/backend/topology/base.py +++ b/pyswarms/backend/topology/base.py @@ -12,12 +12,10 @@ :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): @@ -25,19 +23,17 @@ 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 diff --git a/pyswarms/base/base_discrete.py b/pyswarms/base/base_discrete.py index 4f8ffd7f..9ac54099 100644 --- a/pyswarms/base/base_discrete.py +++ b/pyswarms/base/base_discrete.py @@ -28,14 +28,11 @@ """ -import os import abc -import yaml -import logging -import numpy as np -import logging.config from collections import namedtuple +import numpy as np + # Import from package from ..backend import create_swarm @@ -75,34 +72,6 @@ def assertions(self): if not all(key in self.options for key in ("c1", "c2", "w")): raise KeyError("Missing either c1, c2, or w in options") - def setup_logging( - self, - default_path="./config/logging.yaml", - default_level=logging.INFO, - env_key="LOG_CFG", - ): - """Setup logging configuration - - Parameters - ---------- - default_path : str (default is `./config/logging.yaml`) - the path where the logging configuration is stored - default_level: logging.LEVEL (default is `logging.INFO`) - the default logging level - env_key : str - the environment key for accessing the setup - """ - path = default_path - value = os.getenv(env_key, None) - if value: - path = value - if os.path.exists(path): - with open(path, "rt") as f: - config = yaml.safe_load(f.read()) - logging.config.dictConfig(config) - else: - logging.basicConfig(level=default_level) - def __init__( self, n_particles, @@ -147,7 +116,6 @@ def __init__( a dictionary containing the parameters for a specific optimization technique """ - self.setup_logging() # Initialize primary swarm attributes self.n_particles = n_particles self.dimensions = dimensions @@ -195,9 +163,7 @@ def _populate_history(self, hist): self.velocity_history.append(hist.velocity) @abc.abstractmethod - def optimize( - self, objective_func, iters, print_step=1, verbose=1, **kwargs - ): + def optimize(self, objective_func, iters, fast=False, **kwargs): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -210,10 +176,8 @@ def optimize( 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. + fast : bool (default is False) + if True, time.sleep is not executed kwargs : dict arguments for objective function diff --git a/pyswarms/base/base_single.py b/pyswarms/base/base_single.py index 2cee6cad..b804593b 100644 --- a/pyswarms/base/base_single.py +++ b/pyswarms/base/base_single.py @@ -30,15 +30,11 @@ :mod:`pyswarms.single.general_optimizer`: a more general PSO implementation with a custom topology """ -import os import abc -import yaml -import logging -import numpy as np -import logging.config from collections import namedtuple -# Import from package +import numpy as np + from ..backend import create_swarm @@ -108,34 +104,6 @@ def assertions(self): if not all(key in self.options for key in ("c1", "c2", "w")): raise KeyError("Missing either c1, c2, or w in options") - def setup_logging( - self, - default_path="./config/logging.yaml", - default_level=logging.INFO, - env_key="LOG_CFG", - ): - """Setup logging configuration - - Parameters - ---------- - default_path : str (default is `./config/logging.yaml`) - the path where the logging configuration is stored - default_level: logging.LEVEL (default is `logging.INFO`) - the default logging level - env_key : str - the environment key for accessing the setup - """ - path = default_path - value = os.getenv(env_key, None) - if value: - path = value - if os.path.exists(path): - with open(path, "rt") as f: - config = yaml.safe_load(f.read()) - logging.config.dictConfig(config) - else: - logging.basicConfig(level=default_level) - def __init__( self, n_particles, @@ -179,7 +147,6 @@ def __init__( ftol : float relative error in objective_func(best_pos) acceptable for convergence """ - self.setup_logging() # Initialize primary swarm attributes self.n_particles = n_particles self.dimensions = dimensions @@ -227,9 +194,7 @@ def _populate_history(self, hist): self.velocity_history.append(hist.velocity) @abc.abstractmethod - def optimize( - self, objective_func, iters, print_step=1, verbose=1, **kwargs - ): + def optimize(self, objective_func, iters, fast=False, **kwargs): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -242,10 +207,8 @@ def optimize( 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. + fast : bool (default is False) + if True, time.sleep is not executed kwargs : dict arguments for objective function diff --git a/pyswarms/discrete/binary.py b/pyswarms/discrete/binary.py index 93c481e6..be4d106d 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -51,17 +51,15 @@ Conference on Systems, Man, and Cybernetics, 1997. """ -# Import from stdlib import logging +from time import sleep -# Import modules import numpy as np -# Import from package from ..base import DiscreteSwarmOptimizer from ..backend.operators import compute_pbest from ..backend.topology import Ring -from ..utils.console_utils import cli_print, end_report +from ..utils.reporter import Reporter class BinaryPSO(DiscreteSwarmOptimizer): @@ -129,7 +127,7 @@ def __init__( the Euclidean (or L2) distance. """ # Initialize logger - self.logger = logging.getLogger(__name__) + self.rep = Reporter(logger=logging.getLogger(__name__)) # Assign k-neighbors and p-value as attributes self.k, self.p = options["k"], options["p"] # Initialize parent class @@ -148,10 +146,9 @@ def __init__( self.reset() # Initialize the topology self.top = Ring(static=False) + self.name = __name__ - def optimize( - self, objective_func, iters, print_step=1, verbose=1, **kwargs - ): + def optimize(self, objective_func, iters, fast=False, **kwargs): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -163,10 +160,8 @@ def optimize( 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. + fast : bool (default is False) + if True, time.sleep is not executed kwargs : dict arguments for objective function @@ -176,14 +171,14 @@ def optimize( the local best cost and the local best position among the swarm. """ - cli_print( - "Arguments Passed to Objective Function: {}".format(kwargs), - verbose, - 2, - logger=self.logger, + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log( + "Optimize for {} iters with {}".format(iters, self.options), lvl=20 ) - for i in range(iters): + 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 @@ -200,15 +195,7 @@ def optimize( self.swarm, self.p, self.k ) # Print to console - if i % print_step == 0: - cli_print( - "Iteration {}/{}, cost: {}".format( - i + 1, iters, np.min(self.swarm.best_cost) - ), - verbose, - 2, - logger=self.logger, - ) + self.rep.hook(best_cost=self.swarm.best_cost) # Save to history hist = self.ToHistory( best_cost=self.swarm.best_cost, @@ -233,8 +220,11 @@ def optimize( # Obtain the final best_cost and the final best_position final_best_cost = self.swarm.best_cost.copy() final_best_pos = self.swarm.best_pos.copy() - end_report( - final_best_cost, final_best_pos, verbose, logger=self.logger + self.rep.log( + "Optimization finished | best cost: {}, best pos: {}".format( + final_best_cost, final_best_pos + ), + lvl=20, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py index 27edc268..af56bb54 100644 --- a/pyswarms/single/general_optimizer.py +++ b/pyswarms/single/general_optimizer.py @@ -55,17 +55,16 @@ Proceedings of the IEEE International Joint Conference on Neural Networks, 1995, pp. 1942-1948. """ -# Import from stdlib + import logging +from time import sleep -# Import modules import numpy as np -# Import from package -from ..base import SwarmOptimizer from ..backend.operators import compute_pbest -from ..backend.topology import Topology, Ring, Random, VonNeumann -from ..utils.console_utils import cli_print, end_report +from ..backend.topology import Random, Ring, Topology, VonNeumann +from ..base import SwarmOptimizer +from ..utils.reporter import Reporter class GeneralOptimizerPSO(SwarmOptimizer): @@ -158,7 +157,7 @@ def __init__( ) # Initialize logger - self.logger = logging.getLogger(__name__) + self.rep = Reporter(logger=logging.getLogger(__name__)) # Invoke assertions self.assertions() # Initialize the resettable attributes @@ -214,10 +213,9 @@ def __init__( "Delannoy number (number of neighbours) is" "between 0 and the no. of particles." ) + self.name = __name__ - def optimize( - self, objective_func, iters, print_step=1, verbose=1, **kwargs - ): + def optimize(self, objective_func, iters, fast=False, **kwargs): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -229,10 +227,8 @@ def optimize( objective function to be evaluated iters : int number of iterations - print_step : int (default is 1) - amount of steps for printing into console. - verbose : int (default is 1) - verbosity setting. + fast : bool (default is False) + if True, time.sleep is not executed kwargs : dict arguments for the objective function @@ -241,15 +237,15 @@ def optimize( tuple the global best cost and the global best position. """ + if not fast: + sleep(0.01) - cli_print( - "Arguments Passed to Objective Function: {}".format(kwargs), - verbose, - 2, - logger=self.logger, + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log( + "Optimize for {} iters with {}".format(iters, self.options), lvl=20 ) - for i in range(iters): + 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 @@ -289,16 +285,7 @@ def optimize( self.swarm ) # Print to console - if i % print_step == 0: - cli_print( - "Iteration {}/{}, cost: {}".format( - i + 1, iters, self.swarm.best_cost - ), - verbose, - 2, - logger=self.logger, - ) - # Save to history + self.rep.hook(best_cost=self.swarm.best_cost) hist = self.ToHistory( best_cost=self.swarm.best_cost, mean_pbest_cost=np.mean(self.swarm.pbest_cost), @@ -325,7 +312,10 @@ def optimize( final_best_cost = self.swarm.best_cost.copy() final_best_pos = self.swarm.best_pos.copy() # Write report in log and return final cost and position - end_report( - final_best_cost, final_best_pos, verbose, logger=self.logger + self.rep.log( + "Optimization finished | best cost: {}, best pos: {}".format( + final_best_cost, final_best_pos + ), + lvl=20, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/single/global_best.py b/pyswarms/single/global_best.py index dbbc35ba..ebe0ffb0 100644 --- a/pyswarms/single/global_best.py +++ b/pyswarms/single/global_best.py @@ -55,17 +55,15 @@ Networks, 1995, pp. 1942-1948. """ -# Import from stdlib import logging +from time import sleep -# Import modules import numpy as np -# Import from package -from ..base import SwarmOptimizer from ..backend.operators import compute_pbest from ..backend.topology import Star -from ..utils.console_utils import cli_print, end_report +from ..base import SwarmOptimizer +from ..utils.reporter import Reporter class GlobalBestPSO(SwarmOptimizer): @@ -123,17 +121,16 @@ def __init__( ) # Initialize logger - self.logger = logging.getLogger(__name__) + self.rep = Reporter(logger=logging.getLogger(__name__)) # Invoke assertions self.assertions() # Initialize the resettable attributes self.reset() # Initialize the topology self.top = Star() + self.name = __name__ - def optimize( - self, objective_func, iters, print_step=1, verbose=1, **kwargs - ): + def optimize(self, objective_func, iters, fast=False, **kwargs): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -145,10 +142,8 @@ def optimize( objective function to be evaluated iters : int number of iterations - print_step : int (default is 1) - amount of steps for printing into console. - verbose : int (default is 1) - verbosity setting. + fast : bool (default is False) + if True, time.sleep is not executed kwargs : dict arguments for the objective function @@ -158,14 +153,14 @@ def optimize( the global best cost and the global best position. """ - cli_print( - "Arguments Passed to Objective Function: {}".format(kwargs), - verbose, - 2, - logger=self.logger, + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log( + "Optimize for {} iters with {}".format(iters, self.options), lvl=20 ) - for i in range(iters): + 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 @@ -182,16 +177,7 @@ def optimize( self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( self.swarm ) - # Print to console - if i % print_step == 0: - cli_print( - "Iteration {}/{}, cost: {}".format( - i + 1, iters, self.swarm.best_cost - ), - verbose, - 2, - logger=self.logger, - ) + self.rep.hook(best_cost=self.swarm.best_cost) # Save to history hist = self.ToHistory( best_cost=self.swarm.best_cost, @@ -219,7 +205,10 @@ def optimize( final_best_cost = self.swarm.best_cost.copy() final_best_pos = self.swarm.best_pos.copy() # Write report in log and return final cost and position - end_report( - final_best_cost, final_best_pos, verbose, logger=self.logger + self.rep.log( + "Optimization finished | best cost: {}, best pos: {}".format( + final_best_cost, final_best_pos + ), + lvl=20, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/single/local_best.py b/pyswarms/single/local_best.py index 06cb8ad2..3716c3db 100644 --- a/pyswarms/single/local_best.py +++ b/pyswarms/single/local_best.py @@ -64,17 +64,15 @@ Symposium on Micromachine and Human Science, 1995, pp. 39–43. """ -# Import from stdlib import logging +from time import sleep -# Import modules import numpy as np -# Import from package -from ..base import SwarmOptimizer from ..backend.operators import compute_pbest from ..backend.topology import Ring -from ..utils.console_utils import cli_print, end_report +from ..base import SwarmOptimizer +from ..utils.reporter import Reporter class LocalBestPSO(SwarmOptimizer): @@ -171,16 +169,17 @@ def __init__( ftol=ftol, init_pos=init_pos, ) + # 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) + self.name = __name__ - def optimize( - self, objective_func, iters, print_step=1, verbose=1, **kwargs - ): + def optimize(self, objective_func, iters, fast=False, **kwargs): """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective @@ -192,10 +191,8 @@ def optimize( objective function to be evaluated iters : int number of iterations - print_step : int (default is 1) - amount of steps for printing into console. - verbose : int (default is 1) - verbosity setting. + fast : bool (default is False) + if True, time.sleep is not executed kwargs : dict arguments for the objective function @@ -205,14 +202,14 @@ def optimize( the local best cost and the local best position among the swarm. """ - cli_print( - "Arguments Passed to Objective Function: {}".format(kwargs), - verbose, - 2, - logger=self.logger, + self.rep.log("Obj. func. args: {}".format(kwargs), lvl=10) + self.rep.log( + "Optimize for {} iters with {}".format(iters, self.options), lvl=20 ) - for i in range(iters): + 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 @@ -228,16 +225,7 @@ def optimize( self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( self.swarm, self.p, self.k ) - # Print to console - if i % print_step == 0: - cli_print( - "Iteration {}/{}, cost: {}".format( - i + 1, iters, np.min(self.swarm.best_cost) - ), - verbose, - 2, - logger=self.logger, - ) + self.rep.hook(best_cost=np.min(self.swarm.best_cost)) # Save to history hist = self.ToHistory( best_cost=self.swarm.best_cost, @@ -264,7 +252,11 @@ def optimize( # Obtain the final best_cost and the final best_position final_best_cost = self.swarm.best_cost.copy() final_best_pos = self.swarm.best_pos.copy() - end_report( - final_best_cost, final_best_pos, verbose, logger=self.logger + # Write report in log and return final cost and position + self.rep.log( + "Optimization finished | best cost: {}, best pos: {}".format( + final_best_cost, final_best_pos + ), + lvl=20, ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/utils/console_utils.py b/pyswarms/utils/console_utils.py deleted file mode 100644 index 22ae118d..00000000 --- a/pyswarms/utils/console_utils.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- - -""" console_utils.py: various tools for printing into console """ - -# Import from __future__ -from __future__ import with_statement -from __future__ import absolute_import -from __future__ import print_function - -# Import modules - - -def cli_print(message, verbosity, threshold, logger): - """Helper function to print console output - - Parameters - ---------- - message : str - the message to be printed into the console - verbosity : int - verbosity setting of the user - threshold : int - threshold for printing - logger : logging.getLogger - logger instance - - """ - if verbosity >= threshold: - logger.info(message) - else: - pass - - -def end_report(cost, pos, verbosity, logger): - """Helper function to print a simple report at the end of the - run. This always has a threshold of 1. - - Parameters - ---------- - cost : float - final cost from the optimization procedure. - pos : numpy.ndarray or list - best position found - verbosity : int - verbosity setting of the user. - logger : logging.getLogger - logger instance - """ - - # Cuts the length of the best position if it's too long - if len(list(pos)) > 3: - out = ("[ " + 3 * "{:3f} " + "...]").format(*list(pos)) - else: - out = list(pos) - - template = ( - "================================\n" - "Optimization finished!\n" - "Final cost: {:06.4f}\n" - "Best value: {}\n" - ).format(cost, out) - if verbosity >= 1: - logger.info(template) diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py index cb78cf1f..58cc5e78 100644 --- a/tests/optimizers/conftest.py +++ b/tests/optimizers/conftest.py @@ -3,15 +3,12 @@ """Fixtures for tests""" -# Import modules import pytest -import numpy as np -# Import from package -from pyswarms.single import GlobalBestPSO, LocalBestPSO, GeneralOptimizerPSO +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 -from pyswarms.backend.topology import Star, Ring, Pyramid, Random, VonNeumann @pytest.fixture(scope="module") @@ -21,7 +18,7 @@ def general_opt_history(topology): pso = GeneralOptimizerPSO( 10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=topology ) - pso.optimize(sphere, 1000, verbose=0) + pso.optimize(sphere, 1000) return pso @@ -42,7 +39,7 @@ 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, verbose=0) + pso.optimize(sphere, 1000) return pso @@ -51,7 +48,7 @@ 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, verbose=0) + pso.optimize(sphere, 10) pso.reset() return pso @@ -61,7 +58,7 @@ 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, verbose=0) + pso.optimize(sphere, 1000) return pso @@ -70,7 +67,7 @@ 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, verbose=0) + pso.optimize(sphere, 10) pso.reset() return pso @@ -80,7 +77,7 @@ 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, verbose=0) + pso.optimize(sphere, 1000) return pso @@ -89,7 +86,7 @@ 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, verbose=0) + pso.optimize(sphere, 10) pso.reset() return pso diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py index eac1d5b2..7936a114 100644 --- a/tests/optimizers/test_general_optimizer.py +++ b/tests/optimizers/test_general_optimizer.py @@ -1,13 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Import modules -import pytest import numpy as np +import pytest -# Import from package +from pyswarms.backend.topology import Random, Ring, VonNeumann from pyswarms.single import GeneralOptimizerPSO -from pyswarms.backend.topology import Star, Ring, Pyramid, Random, VonNeumann from pyswarms.utils.functions.single_obj import sphere @@ -236,5 +234,5 @@ def test_ftol_effect(options, topology): pso = GeneralOptimizerPSO( 10, 2, options=options, topology=topology, ftol=1e-1 ) - pso.optimize(sphere, 2000, verbose=0) + pso.optimize(sphere, 2000) assert np.array(pso.cost_history).shape != (2000,) diff --git a/tests/optimizers/test_global_best.py b/tests/optimizers/test_global_best.py index ef10f1d0..5a2020a5 100644 --- a/tests/optimizers/test_global_best.py +++ b/tests/optimizers/test_global_best.py @@ -1,11 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Import modules -import pytest import numpy as np +import pytest -# Import from package from pyswarms.single import GlobalBestPSO from pyswarms.utils.functions.single_obj import sphere @@ -109,5 +107,5 @@ def test_training_history_shape(gbest_history, history, 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, verbose=0) + pso.optimize(sphere, 2000) assert np.array(pso.cost_history).shape != (2000,) diff --git a/tests/optimizers/test_local_best.py b/tests/optimizers/test_local_best.py index b85db283..1a991df4 100644 --- a/tests/optimizers/test_local_best.py +++ b/tests/optimizers/test_local_best.py @@ -1,11 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Import modules -import pytest import numpy as np +import pytest -# Import from package from pyswarms.single import LocalBestPSO from pyswarms.utils.functions.single_obj import sphere @@ -130,5 +128,5 @@ def test_training_history_shape(lbest_history, history, 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, verbose=0) + pso.optimize(sphere, 2000) assert np.array(pso.cost_history).shape != (2000,) diff --git a/tests/optimizers/test_objective_func_with_kwargs.py b/tests/optimizers/test_objective_func_with_kwargs.py index 169fd198..96228765 100644 --- a/tests/optimizers/test_objective_func_with_kwargs.py +++ b/tests/optimizers/test_objective_func_with_kwargs.py @@ -1,11 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Import modules import pytest import numpy as np -# Import from package from pyswarms.single import GlobalBestPSO, LocalBestPSO from pyswarms.utils.functions.single_obj import rosenbrock @@ -31,9 +29,7 @@ def test_global_kwargs(func): ) # run it - cost, pos = opt_ps.optimize( - func, 1000, print_step=10, verbose=3, a=1, b=100 - ) + 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) @@ -56,7 +52,7 @@ def test_global_kwargs_without_named_arguments(func): ) # run it - cost, pos = opt_ps.optimize(func, 1000, verbose=3, a=1, b=100) + 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) @@ -78,7 +74,7 @@ def test_global_no_kwargs(func): ) # run it - cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3) + 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) @@ -100,9 +96,7 @@ def test_local_kwargs(func): ) # run it - cost, pos = opt_ps.optimize( - func, 1000, print_step=10, verbose=3, a=1, b=100 - ) + 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) @@ -124,7 +118,7 @@ def test_local_no_kwargs(func): ) # run it - cost, pos = opt_ps.optimize(func, iters=1000, print_step=10, verbose=3) + 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) @@ -147,7 +141,7 @@ def test_global_uneeded_kwargs(func): # run it with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + cost, pos = opt_ps.optimize(func, 1000, a=1) assert "unexpected keyword" in str(excinfo.value) @@ -167,7 +161,7 @@ def test_global_missed_kwargs(func): # run it with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + cost, pos = opt_ps.optimize(func, 1000, a=1) assert "missing 1 required positional argument" in str(excinfo.value) @@ -187,7 +181,7 @@ def test_local_uneeded_kwargs(func): # run it with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + cost, pos = opt_ps.optimize(func, 1000, a=1) assert "unexpected keyword" in str(excinfo.value) @@ -207,7 +201,7 @@ def test_local_missed_kwargs(func): # run it with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + cost, pos = opt_ps.optimize(func, 1000, a=1) assert "missing 1 required positional argument" in str(excinfo.value) @@ -227,9 +221,7 @@ def test_local_wrong_kwargs(func): # run it with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize( - func, 1000, print_step=10, verbose=3, c=1, d=100 - ) + cost, pos = opt_ps.optimize(func, 1000, print_step=10, c=1, d=100) assert "unexpected keyword" in str(excinfo.value) @@ -249,7 +241,5 @@ def test_global_wrong_kwargs(func): # run it with pytest.raises(TypeError) as excinfo: - cost, pos = opt_ps.optimize( - func, 1000, print_step=10, verbose=3, c=1, d=100 - ) + cost, pos = opt_ps.optimize(func, 1000, c=1, d=100) assert "unexpected keyword" in str(excinfo.value) From b8d96959594db44545df4a63707855c9d02a488e Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Sat, 18 Aug 2018 20:41:28 +0900 Subject: [PATCH 7/8] [WIP] Update RTD --- docs/api/_pyswarms.utils.rst | 1 + docs/api/pyswarms.utils.reporter.rst | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 docs/api/pyswarms.utils.reporter.rst diff --git a/docs/api/_pyswarms.utils.rst b/docs/api/_pyswarms.utils.rst index bf8dd659..131289e2 100644 --- a/docs/api/_pyswarms.utils.rst +++ b/docs/api/_pyswarms.utils.rst @@ -10,3 +10,4 @@ functionalities. pyswarms.utils.functions pyswarms.utils.search pyswarms.utils.plotters + pyswarms.utils.reporter diff --git a/docs/api/pyswarms.utils.reporter.rst b/docs/api/pyswarms.utils.reporter.rst new file mode 100644 index 00000000..ba18e082 --- /dev/null +++ b/docs/api/pyswarms.utils.reporter.rst @@ -0,0 +1,10 @@ +pyswarms.utils.reporter package +================================ + +.. automodule:: pyswarms.utils.reporter.reporter + :members: + :undoc-members: + :show-inheritance: + :private-members: + :special-members: __init__ + From f586b74533783d112631f65260883f52ad7ebd62 Mon Sep 17 00:00:00 2001 From: Lj Miranda <12949683+ljvmiranda921@users.noreply.github.com> Date: Sun, 19 Aug 2018 00:25:25 +0900 Subject: [PATCH 8/8] Port topologies to reporter (#231) --- pyswarms/backend/generators.py | 26 +++++++++++++++++---- pyswarms/backend/operators.py | 29 +++++++++++------------- pyswarms/backend/swarms.py | 3 +-- pyswarms/backend/topology/pyramid.py | 13 ++++------- pyswarms/backend/topology/random.py | 15 ++++-------- pyswarms/backend/topology/ring.py | 13 ++++------- pyswarms/backend/topology/star.py | 13 ++++------- pyswarms/backend/topology/von_neumann.py | 6 ++--- pyswarms/utils/functions/single_obj.py | 6 ----- pyswarms/utils/plotters/formatters.py | 3 +-- pyswarms/utils/plotters/plotters.py | 9 ++++---- 11 files changed, 60 insertions(+), 76 deletions(-) diff --git a/pyswarms/backend/generators.py b/pyswarms/backend/generators.py index d475fbab..91f7dbf6 100644 --- a/pyswarms/backend/generators.py +++ b/pyswarms/backend/generators.py @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/pyswarms/backend/operators.py b/pyswarms/backend/operators.py index fb42ecd2..7714f241 100644 --- a/pyswarms/backend/operators.py +++ b/pyswarms/backend/operators.py @@ -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): @@ -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) @@ -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 @@ -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 diff --git a/pyswarms/backend/swarms.py b/pyswarms/backend/swarms.py index a6d4db06..71a6d730 100644 --- a/pyswarms/backend/swarms.py +++ b/pyswarms/backend/swarms.py @@ -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 diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index f1af7713..53b0fea4 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -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): @@ -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 @@ -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) diff --git a/pyswarms/backend/topology/random.py b/pyswarms/backend/topology/random.py index 91464ee6..6794af22 100644 --- a/pyswarms/backend/topology/random.py +++ b/pyswarms/backend/topology/random.py @@ -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): @@ -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 @@ -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) diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index 660899c4..54425e90 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -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): @@ -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 @@ -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) diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index 8a8f3750..b341f7d9 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -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 @@ -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) diff --git a/pyswarms/backend/topology/von_neumann.py b/pyswarms/backend/topology/von_neumann.py index 168ec8ec..bf33cabd 100644 --- a/pyswarms/backend/topology/von_neumann.py +++ b/pyswarms/backend/topology/von_neumann.py @@ -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 diff --git a/pyswarms/utils/functions/single_obj.py b/pyswarms/utils/functions/single_obj.py index 63acd877..4dfc1234 100644 --- a/pyswarms/utils/functions/single_obj.py +++ b/pyswarms/utils/functions/single_obj.py @@ -36,12 +36,6 @@ - Three Hump Camel, threehump """ -# Import from __future__ -from __future__ import with_statement -from __future__ import absolute_import -from __future__ import print_function - -# Import modules import numpy as np diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index 027c14a5..b240ce76 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -6,9 +6,8 @@ This module implements helpful classes to format your plots or create meshes. """ -# Import modules import numpy as np -from attr import attrs, attrib +from attr import attrib, attrs from attr.validators import instance_of from matplotlib import cm, colors diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py index 5d92d55c..6d87e395 100644 --- a/pyswarms/utils/plotters/plotters.py +++ b/pyswarms/utils/plotters/plotters.py @@ -65,7 +65,6 @@ speed of animation. """ -# Import modules import logging import matplotlib.pyplot as plt @@ -73,11 +72,10 @@ from matplotlib import animation, cm from mpl_toolkits.mplot3d import Axes3D -# Import from package from .formatters import Designer, Animator +from ..reporter import Reporter -# Initialize logger -logger = logging.getLogger(__name__) +rep = Reporter(logger=logging.getLogger(__name__)) def plot_cost_history( @@ -132,6 +130,7 @@ def plot_cost_history( ax.set_ylabel(designer.label[1], fontsize=designer.text_fontsize) ax.tick_params(labelsize=designer.text_fontsize) except TypeError: + rep.logger.exception("Please check your input type") raise else: return ax @@ -234,6 +233,7 @@ def plot_contour( repeat_delay=animator.repeat_delay, ) except TypeError: + rep.logger.exception("Please check your input type") raise else: return anim @@ -376,6 +376,7 @@ def plot_surface( repeat_delay=animator.repeat_delay, ) except TypeError: + rep.logger.exception("Please check your input type") raise else: return anim