From 3e36021ce3ef5660a7e2f84dc63104bd7b978f5f Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Mon, 11 Jun 2018 18:59:56 +0900 Subject: [PATCH 01/38] Modify if-else statements in generators This commit makes the if-else statements in generators more concise. To be honest, I didn't know what came into my mind as to why I nested those if statements. It turns out that using the "and" operator is much, much better. Signed-off-by: Lester James V. Miranda --- pyswarms/backend/generators.py | 53 ++++++++++++++-------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/pyswarms/backend/generators.py b/pyswarms/backend/generators.py index b5b1c44a..7b31488f 100644 --- a/pyswarms/backend/generators.py +++ b/pyswarms/backend/generators.py @@ -41,24 +41,20 @@ def generate_swarm(n_particles, dimensions, bounds=None, center=1.00, init_pos=N swarm matrix of shape (n_particles, n_dimensions) """ try: - if init_pos is not None: - # There is user-defined initial position - if bounds is None: - pos = init_pos - else: - if not (np.all(bounds[0] <= init_pos) and np.all(init_pos <= bounds[1])): - raise ValueError('User-defined init_pos is out of bounds.') - pos = init_pos + if (init_pos is not None) and (bounds is None): + pos = init_pos + elif (init_pos is not None) and (bounds is not None): + if not (np.all(bounds[0] <= init_pos) and np.all(init_pos <= bounds[1])): + raise ValueError('User-defined init_pos is out of bounds.') + pos = init_pos + elif (init_pos is None) and (bounds is None): + pos = center * np.random.uniform(low=0.0, high=1.0, size=(n_particles, dimensions)) else: - # There is no user-defined initial position - if bounds is None: - pos = center * np.random.uniform(low=0.0, high=1.0, size=(n_particles, dimensions)) - else: - lb, ub = bounds - min_bounds = np.repeat(np.array(lb)[np.newaxis, :], n_particles, axis=0) - max_bounds = np.repeat(np.array(ub)[np.newaxis, :], n_particles, axis=0) - pos = center * np.random.uniform(low=min_bounds, high=max_bounds, - size=(n_particles, dimensions)) + lb, ub = bounds + min_bounds = np.repeat(np.array(lb)[np.newaxis, :], n_particles, axis=0) + max_bounds = np.repeat(np.array(ub)[np.newaxis, :], n_particles, axis=0) + pos = center * np.random.uniform(low=min_bounds, high=max_bounds, + size=(n_particles, dimensions)) except ValueError: raise else: @@ -80,21 +76,16 @@ def generate_discrete_swarm(n_particles, dimensions, binary=False, init_pos=None :code:`None` if you wish to generate the particles randomly. """ try: - if init_pos is not None: - # There is user-defined initial position - if binary: - # Check if the initialized position is binary - if not len(np.unique(init_pos)) == 2: - raise ValueError('User-defined init_pos is not binary!') - pos = init_pos - else: - pos = init_pos + if (init_pos is not None) and binary: + if not len(np.unique(init_pos)) == 2: + raise ValueError('User-defined init_pos is not binary!') + pos = init_pos + elif (init_pos is not None) and not binary: + pos = init_pos + elif (init_pos is None) and binary: + pos = np.random.randint(2, size=(n_particles, dimensions)) else: - # There is no user-defined initial position - if binary: - pos = np.random.randint(2, size=(n_particles, dimensions)) - else: - pos = np.random.random_sample(size=(n_particles, dimensions)).argsort(axis=1) + pos = np.random.random_sample(size=(n_particles, dimensions)).argsort(axis=1) except ValueError: raise else: From 767f5e59ce80960e015c1755ea2fe93da7a6abca Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Tue, 12 Jun 2018 11:07:20 +0900 Subject: [PATCH 02/38] Add DeprecationWarning to PlotEnvironment module In this commit, we add a DeprecationWarning for the PlotEnvironment module. Although useful, I realized that it might be a hassle when the environment still optimizes the optimizer if we just want to plot it. What if a swarm iteration takes a long time? Will plotting take the same amount of time? We want to decouple the optimization part from the visualization. It is better to just have the user supply the position and cost histories and we just have the module do what it knows. Signed-off-by: Lester James V. Miranda --- pyswarms/utils/environments/plot_environment.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyswarms/utils/environments/plot_environment.py b/pyswarms/utils/environments/plot_environment.py index c256b2f6..9606d91d 100644 --- a/pyswarms/utils/environments/plot_environment.py +++ b/pyswarms/utils/environments/plot_environment.py @@ -57,6 +57,7 @@ # Import modules import logging +import warnings import numpy as np import matplotlib.pyplot as plt from past.builtins import xrange @@ -64,6 +65,8 @@ from collections import namedtuple from mpl_toolkits.mplot3d import Axes3D +warnings.simplefilter('default') +warnings.warn("The pyswarms.environments module is deprecated and will be removed in v.0.2.5. For visualization, please use pyswarms.plotters", DeprecationWarning, stacklevel=2) class PlotEnvironment(object): From 7caded43fabedf3ce63a4fb9185abcce061734da Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Tue, 12 Jun 2018 22:25:46 +0900 Subject: [PATCH 03/38] Remove @property in history In this commit, we decided to remove the @property in storing the dependency. I found this very redundant and violates Python's rule of having only one way of doing things. Instead, we just access the histories from the class/instance attributes Signed-off-by: Lester James V. Miranda --- docs/assets/inheritance.aux | 2 + docs/assets/inheritance.fdb_latexmk | 155 ++++++++++++++ docs/assets/inheritance.fls | 250 ++++++++++++++++++++++ docs/assets/optimization_loop.aux | 2 + docs/assets/optimization_loop.fdb_latexmk | 156 ++++++++++++++ docs/assets/optimization_loop.fls | 248 +++++++++++++++++++++ docs/assets/pyswarms_api.aux | 2 + docs/assets/pyswarms_api.fdb_latexmk | 128 +++++++++++ docs/assets/pyswarms_api.fls | 204 ++++++++++++++++++ pyswarms/base/base_discrete.py | 56 +---- pyswarms/base/base_single.py | 25 --- 11 files changed, 1155 insertions(+), 73 deletions(-) create mode 100644 docs/assets/inheritance.aux create mode 100644 docs/assets/inheritance.fdb_latexmk create mode 100644 docs/assets/inheritance.fls create mode 100644 docs/assets/optimization_loop.aux create mode 100644 docs/assets/optimization_loop.fdb_latexmk create mode 100644 docs/assets/optimization_loop.fls create mode 100644 docs/assets/pyswarms_api.aux create mode 100644 docs/assets/pyswarms_api.fdb_latexmk create mode 100644 docs/assets/pyswarms_api.fls diff --git a/docs/assets/inheritance.aux b/docs/assets/inheritance.aux new file mode 100644 index 00000000..7c14129a --- /dev/null +++ b/docs/assets/inheritance.aux @@ -0,0 +1,2 @@ +\relax +\gdef \sa@multi@numpages {0} diff --git a/docs/assets/inheritance.fdb_latexmk b/docs/assets/inheritance.fdb_latexmk new file mode 100644 index 00000000..1b01ed13 --- /dev/null +++ b/docs/assets/inheritance.fdb_latexmk @@ -0,0 +1,155 @@ +# Fdb version 3 +["pdflatex"] 1528613815 "inheritance.tex" "inheritance.pdf" "inheritance" 1528613816 + "/dev/null" 1528595442 0 d41d8cd98f00b204e9800998ecf8427e "" + "/etc/texmf/web2c/texmf.cnf" 1515379388 475 c0e671620eb5563b2130f56340a5fde8 "" + "/home/ljvm/.texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1515762570 182396 f7dec8867b39ce26f1cba3e9650c3aa6 "" + "/home/ljvm/texmf/tex/generic/oberdiek/etexcmds.sty" 1335995445 7612 c47308d923ec19888707b0f1792b326a "" + "/home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty" 1303254447 7140 ece2cc23d9f20e1f53975ac167f42d3e "" + "/home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty" 1335995445 8253 3bdedc8409aa5d290a2339be6f09af03 "" + "/home/ljvm/texmf/tex/generic/oberdiek/kvdefinekeys.sty" 1335995445 5152 387d9200f396b498d5fd679ae44ed898 "" + "/home/ljvm/texmf/tex/generic/oberdiek/kvsetkeys.sty" 1335995445 14040 8de9f47fabc4ca3bd69b6d795e32751c "" + "/home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty" 1335995445 18425 775b341047ce304520cc7c11ca41392e "" + "/home/ljvm/texmf/tex/generic/oberdiek/pdftexcmds.sty" 1335995445 19987 01cb2f3c1d21e5f05711b7fd50b17f2a "" + "/home/ljvm/texmf/tex/generic/xstring/xstring.sty" 1269905706 144 0ca8d67b000b795a4d9ec000e0fd09c7 "" + "/home/ljvm/texmf/tex/generic/xstring/xstring.tex" 1381789620 54373 fd4487ae3e45d4074bc89aea1d2b6807 "" + "/home/ljvm/texmf/tex/latex/graphics/dvipsnam.def" 1454284088 4945 3b3a8face751255856839a3489e83341 "" + "/home/ljvm/texmf/tex/latex/graphics/graphics.sty" 1454284088 14337 b66dff1d80f6c21e70858a2b3c2d327d "" + "/home/ljvm/texmf/tex/latex/graphics/graphicx.sty" 1428932888 8125 557ab9f1bfa80d369fb45a914aa8a3b4 "" + "/home/ljvm/texmf/tex/latex/graphics/trig.sty" 1454284088 3980 0a268fbfda01e381fa95821ab13b6aee "" + "/home/ljvm/texmf/tex/latex/oberdiek/epstopdf-base.sty" 1303254447 12029 04d7fdf76e0464c23b5aa3a727952d7c "" + "/home/ljvm/texmf/tex/latex/oberdiek/grfext.sty" 1335995445 7075 bd0c34fbf1ae8fd1debd2a554e41b2d5 "" + "/home/ljvm/texmf/tex/latex/oberdiek/kvoptions.sty" 1335995445 22417 c74ff4af6a1aa2b65d1924020edbbe11 "" + "/home/ljvm/texmf/tex/latex/preview/preview.sty" 1447630789 13687 0cb5888b46d12f19ed3b85a16d43470e "" + "/home/ljvm/texmf/tex/latex/preview/prtightpage.def" 1266794019 4841 763a1efd128d3821c07232f7b2638b7b "" + "/home/ljvm/texmf/tex/latex/standalone/standalone.cfg" 1437172902 902 f9a15737aea33182ec1f3542ca20dbfe "" + "/home/ljvm/texmf/tex/latex/standalone/standalone.cls" 1437172902 27291 b798d344bcba4430e7f7dc7561c517d5 "" + "/home/ljvm/texmf/tex/latex/tools/calc.sty" 1454284088 10214 d03d065f799d54f6b7e9b175f8d84279 "" + "/home/ljvm/texmf/tex/latex/xcolor/xcolor.sty" 1169481954 55224 a43bab84e0ac5e6efcaf9a98bde73a94 "" + "/usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map" 1272929888 3287 e6b82fe08f5336d4d5ebc73fb1152e87 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1136768653 1512 f21f83efb36853c0b70002322c1ab3ad "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi9.tfm" 1136768653 1524 d89e2d087a9828407a196f428428ef4a "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1136768653 1300 b62933e007d01cfd073f79b963c01526 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr9.tfm" 1136768653 1292 6b21b9c2c7bebb38aa2273f7ca0fb3af "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss10.tfm" 1136768653 1316 b636689f1933f24d1294acdf6041daaa "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss9.tfm" 1136768653 1320 49357c421c0d469f88b867dd0c3d10e8 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmssbx10.tfm" 1136768653 1272 e2d13f0df30bf3ad990bb9d028e37f34 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1136768653 1116 933a60c408fc0a863a92debe84b2d294 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy9.tfm" 1136768653 1116 25a7bf822c58caf309a702ef79f4afbb "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss9.pfb" 1248133631 24373 a91d375736817a75026663adcb2190c1 "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmssbx10.pfb" 1248133631 28902 2f5c04fd2884d1878057baa5aad22765 "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy9.pfb" 1248133631 32442 c975af247b6702f7ca0c299af3616b80 "" + "/usr/share/texlive/texmf-dist/tex/context/base/supp-pdf.mkii" 1337017135 71627 94eb9990bed73c364d7f53f960cc8c5b "" + "/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty" 1284331290 1458 43ab4710dc82f3edeabecd0d099626b2 "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty" 1303254447 7324 11d14f318d865f420e692d4e6c9c18c3 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex" 1288312291 1006 b103be0bfc8c1682ff1fa9760697a329 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex" 1439074469 43226 167a99346bfe2676e3efcdde2d81fe45 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex" 1439074469 19302 4f089dc590e71f7331e6d5b5ea85273b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex" 1439074469 6068 edae1e768a7d8d8f0f00e953d2b0153e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex" 1393459310 7041 a891ad72049e17c4e366c40ca37b0ccb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex" 1393459310 4625 40c07e9f6f2f7c674704b3f2055560ce "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex" 1203877327 2631 7eefa6cdbefd8d4e2bad7262cf1094cd "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex" 1393459310 43477 81143b33d9ebafdeead07ede13372427 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex" 1393459310 17436 8d99d4113be311daf23deff86991ee7d "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex" 1439074469 20772 c57e34db4aa7b1da013169d04b743eac "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex" 1393459310 9641 711f0edc22c180a5caf168b6e8970057 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex" 1393459310 34516 658a71478d21df554bce9d9cd436203a "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex" 1288312291 3052 e5672c657232fd63b0a9853b0746297c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex" 1439074469 16669 4ec6e40088fc6de6334b443fe2dc59f0 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex" 1393459310 21541 4cd19f8ff7dd74d5aa7d803a6397af84 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex" 1439074469 19998 d77fef95c7369827753d17fd11be19c4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex" 1393459310 8943 2e2495b057f8f0035b5568394d489963 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex" 1203727794 437 cf40f841f40822be6cb995f8b47112fd "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex" 1393459310 4611 b858a4e5bd5442802c91a13027dc25bb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex" 1393459310 15934 b941bd3ae7b33179029513707d1f0ff6 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex" 1393459310 5484 4bb4a5cbbd05d6f17a261b59dbd014f1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex" 1203727794 782 2479083eef1ef47450770d40ad81f937 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex" 1288312291 1298 83d7449064b0f0f089f1898a244b6d16 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex" 1393459310 3725 36db4c06798413d051778705f3255eea "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex" 1288312291 4034 0a8cd33cf30d262ec971380666acb2d0 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex" 1203727794 3001 d54bab2f783098ed890fabbeb437b04f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex" 1203727794 527 a8d3e34fbab3dc317cf9b06aa5cdc2e4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex" 1203727794 1158 d6338189706f4587fbc6175c0fb41f17 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex" 1203727794 607 40dc15d3efcf10f095866a94bd544bc1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex" 1203727794 457 ffe9f8b9d108b5f729fd86c78c63589a "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex" 1203727794 447 e87a0add254801e837fa6c18f61f340f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex" 1203727794 1004 86af66805a9d0b62bd41ea0796a64d50 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex" 1203727794 590 7e11000a24bbee9ae2a4cd0e5d88e58c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex" 1288312291 11599 d694704a88e2f9007c996d3a6a4d629c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex" 1439074469 176652 1c2926908e2b356d454795c35385d580 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex" 1393459310 5181 c2b736d254ec36204f8fffd5a45bbd41 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex" 1393459310 31927 7acd27f90dd95ce67ad32166cd0b95ec "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex" 1203727794 2647 defb4a59c2a1d36127a1ac6eebb4a5c1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex" 1393459310 32969 dbcfd5a7de6a0f7255c333ef60287d59 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex" 1288312291 69900 cbd9fafb795a493fb2a3b73713994b78 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex" 1393459310 28333 0189c4cfb5044e700e6ba65a32295f01 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex" 1288312291 132566 291d42c3b23fdb5c47e51b36a5fea0c4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex" 1393459310 37737 ea6cb0b4e615f6048f20ee7153b3cc78 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex" 1288312291 49891 e74f8181c57d9359c941b6bee48fccc2 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex" 1393459310 90791 0f3e73cae9286c96d9fcb2161cc223bc "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex" 1393459310 454 9e9e7c99f4da4f41698be21eaef4938e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex" 1393459310 13416 940ea6971d7a65dc440d3479939c66ae "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex" 1439074469 94097 62ac62cda46eb715560dc27f9ed6e8b1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex" 1393459310 9375 5adc70f722abd29fc250d59e0694b548 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex" 1439074469 22069 7c21c42b15718ce922f36235be360490 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex" 1439074469 8210 a7be5b52ef3d2c087b7dc3d52898b67e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex" 1288312291 3534 c7f28fbac13616513e513efe93b8569b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex" 1393459310 3167 7c9394e79aac27db96a92f9b2792b858 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex" 1439074469 9289 261407875b9dbb0194691c3eb893610f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex" 1439074469 7078 946ddf4a7e57219b6afdbad98eb6731b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex" 1288312291 2688 139c6abc86761a6190c2f4bef5d752be "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex" 1439074469 92284 dcf023dbaa84e6c50e11c2f79fe8cfa6 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex" 1439074469 35430 046e15fbb65e74d8f0e7945f99741fdb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex" 1393459310 7099 f44d505bae6c7c2b933cdd63441db4b9 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex" 1393459310 71902 658cc1e13f73daec4225b8fc1c27600b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex" 1393459310 20934 2328bd2e04520e1ab077ac4ee13b8935 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex" 1439074469 16203 83cbe1220e389eeee283a6168f9a567b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex" 1439074469 42906 d54376d96df1a2ae2d33fb722236d8e9 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg" 1288312291 978 15af626ebd3d4d790aac19170dac04f2 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def" 1393459310 5437 d91f93ed61ecdc57e119849b2d784a0b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def" 1439074469 13507 809d848d9262638e1b1705a68a73c566 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex" 1439074469 35113 2ccc50c1c9573e4bac9230d030f9c67c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex" 1203877327 1983 b5994ebbcee17f1ba3d29bb1bd696fcf "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex" 1393459310 7881 d459d6057e13d10ce7a227ae44b7295e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex" 1393459310 22211 d696ef78c12269178882d218b2cf191d "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex" 1393459310 36194 e194ef4e0b396b531a3891feb4b1cc22 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeysfiltered.code.tex" 1393459310 33377 af391d6ad1bfcbe2278e191f48e43ba8 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex" 1440888734 2536 a3b0529d815a2759ba157b56610a6377 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common-lists.tex" 1393459310 6833 114eda2cf1d348e0e7e477a1a4dc1941 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex" 1439074469 16501 ab0135765e27b6b8dae047831fe84818 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def" 1439074469 5544 294baac9629ba59f675b1f2027ad7136 "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/keyval.tex" 1403829539 2725 fc34ef3ccb37ba15a640e8fca6190bca "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkeyval.tex" 1417732693 19231 26434a5656c684f5ffb1f26f98006baa "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkvutils.tex" 1403829539 7677 6f5ce7c1124cad7ec57d05b2562bd8fe "" + "/usr/share/texlive/texmf-dist/tex/latex/base/article.cls" 1454284088 20708 39fdf9e2fb65617012fa7382a351f485 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty" 1454284088 5159 a08c9bbd48fc492f15b22e458bef961f "" + "/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo" 1454284088 9179 4cd3c5f593e63512893b8ac0123f1bd7 "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg" 1254097189 802 7b8c8d72c24d795ed7720e4dfd29bff3 "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg" 1279039959 678 4792914a8f45be57bb98413425e4c7af "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg" 1278958963 3563 d35e897cae3b8c6848f6677b73370b54 "" + "/usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty" 1177890616 3878 6aa7c08ff2621006e0603349e40a30a8 "" + "/usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def" 1306616590 55368 3c8a0d99822330f2dfabc0dfb09ce897 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf-blur/tikzlibraryshadows.blur.code.tex" 1335333685 7525 063a37c856ae2c38332d93e3d457a299 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty" 1439074469 1197 8a80cdde14696a9198f1793a55dcf332 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty" 1288312291 410 5bf12ea7330e5f12c445332a4fe9a263 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty" 1203877327 21115 facf03b7dbe5ea2f5f1dce1ac84b5d05 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty" 1203727794 1091 d9163d29def82ee90370c8a63667742c "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty" 1203877327 339 592cf35cba3d400082b8a9a5d0199d70 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty" 1393459310 306 0796eafca5e159e6ec2167a6d22d81b1 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty" 1393459310 443 0b2e781830192df35c0fd357cf13e26e "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty" 1393459310 348 8927fde343487e003b01a4c2ca34073b "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty" 1203727794 274 4cad6e665cc93ac2ac979039a94fa1e1 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty" 1203877327 325 2bcd023400636339210573e2b3ee298b "" + "/usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty" 1405118212 5540 d5c60cf09c59da351aa4023ed084e4eb "" + "/usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty" 1417732693 4962 9c1069474ff71dbc47d5006555e352d3 "" + "/usr/share/texlive/texmf-dist/web2c/texmf.cnf" 1503343927 31343 93828589fb0cea665e553ee5a17ad2d4 "" + "/usr/share/texmf/web2c/texmf.cnf" 1503343927 31343 93828589fb0cea665e553ee5a17ad2d4 "" + "/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1528507587 718486 55e051f478d994e1e3e5b41625d996af "" + "inheritance.aux" 1528613815 8 a94a2480d3289e625eea47cd1b285758 "" + "inheritance.pdf" 1528613815 33726 755c6ba1b7e08ba25d4c4bf070e74723 "pdflatex" + "inheritance.png" 1528606148 15031 54872dfbf68cfa63dcdcc9264b34daae "" + "inheritance.tex" 1528613800 2092 8e81f1f5803589f754bd70c58b0c6450 "" + "tikz-uml.sty" 1526185697 302703 ed2f9b5dd419a64f254ecbf6a6653c7f "" + (generated) + "inheritance.pdf" + "inheritance.log" + "inheritance.aux" diff --git a/docs/assets/inheritance.fls b/docs/assets/inheritance.fls new file mode 100644 index 00000000..8e82d2d4 --- /dev/null +++ b/docs/assets/inheritance.fls @@ -0,0 +1,250 @@ +PWD /home/ljvm/Documents/Dev/pyswarms/docs/assets +INPUT /etc/texmf/web2c/texmf.cnf +INPUT /usr/share/texmf/web2c/texmf.cnf +INPUT /usr/share/texlive/texmf-dist/web2c/texmf.cnf +INPUT /var/lib/texmf/web2c/pdftex/pdflatex.fmt +INPUT inheritance.tex +OUTPUT inheritance.log +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cls +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cls +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkeyval.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkvutils.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/keyval.tex +INPUT /dev/null +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cfg +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /home/ljvm/texmf/tex/latex/preview/preview.sty +INPUT /home/ljvm/texmf/tex/latex/preview/preview.sty +INPUT /home/ljvm/texmf/tex/latex/preview/prtightpage.def +INPUT /home/ljvm/texmf/tex/latex/preview/prtightpage.def +INPUT inheritance.png +INPUT ./inheritance.png +INPUT ./inheritance.pdf +INPUT inheritance.aux +INPUT inheritance.aux +INPUT ./inheritance.pdf +INPUT inheritance.png +INPUT ./inheritance.png +INPUT /home/ljvm/texmf/tex/latex/xcolor/xcolor.sty +INPUT /home/ljvm/texmf/tex/latex/xcolor/xcolor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def +INPUT /home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/dvipsnam.def +INPUT /home/ljvm/texmf/tex/latex/graphics/dvipsnam.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common-lists.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphicx.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphicx.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphics.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphics.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/trig.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/trig.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeysfiltered.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex +INPUT tikz-uml.sty +INPUT tikz-uml.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.tex +INPUT /home/ljvm/texmf/tex/latex/tools/calc.sty +INPUT /home/ljvm/texmf/tex/latex/tools/calc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex +OUTPUT inheritance.pdf +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf-blur/tikzlibraryshadows.blur.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf-blur/tikzlibraryshadows.blur.code.tex +INPUT inheritance.aux +INPUT inheritance.aux +OUTPUT inheritance.aux +INPUT /usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss10.tfm +INPUT /usr/share/texlive/texmf-dist/tex/context/base/supp-pdf.mkii +INPUT /usr/share/texlive/texmf-dist/tex/context/base/supp-pdf.mkii +INPUT /home/ljvm/texmf/tex/generic/oberdiek/pdftexcmds.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/pdftexcmds.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/epstopdf-base.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/epstopdf-base.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/grfext.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/grfext.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvdefinekeys.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvdefinekeys.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/kvoptions.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/kvoptions.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvsetkeys.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvsetkeys.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/etexcmds.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/etexcmds.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr6.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmssbx10.tfm +INPUT /home/ljvm/.texmf-var/fonts/map/pdftex/updmap/pdftex.map +INPUT inheritance.aux +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss9.pfb +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmssbx10.pfb +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy9.pfb diff --git a/docs/assets/optimization_loop.aux b/docs/assets/optimization_loop.aux new file mode 100644 index 00000000..7c14129a --- /dev/null +++ b/docs/assets/optimization_loop.aux @@ -0,0 +1,2 @@ +\relax +\gdef \sa@multi@numpages {0} diff --git a/docs/assets/optimization_loop.fdb_latexmk b/docs/assets/optimization_loop.fdb_latexmk new file mode 100644 index 00000000..77e1eb7d --- /dev/null +++ b/docs/assets/optimization_loop.fdb_latexmk @@ -0,0 +1,156 @@ +# Fdb version 3 +["pdflatex"] 1528617882 "optimization_loop.tex" "optimization_loop.pdf" "optimization_loop" 1528617883 + "/dev/null" 1528595442 0 d41d8cd98f00b204e9800998ecf8427e "" + "/etc/texmf/web2c/texmf.cnf" 1515379388 475 c0e671620eb5563b2130f56340a5fde8 "" + "/home/ljvm/.texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1515762570 182396 f7dec8867b39ce26f1cba3e9650c3aa6 "" + "/home/ljvm/texmf/tex/generic/oberdiek/etexcmds.sty" 1335995445 7612 c47308d923ec19888707b0f1792b326a "" + "/home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty" 1303254447 7140 ece2cc23d9f20e1f53975ac167f42d3e "" + "/home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty" 1335995445 8253 3bdedc8409aa5d290a2339be6f09af03 "" + "/home/ljvm/texmf/tex/generic/oberdiek/kvdefinekeys.sty" 1335995445 5152 387d9200f396b498d5fd679ae44ed898 "" + "/home/ljvm/texmf/tex/generic/oberdiek/kvsetkeys.sty" 1335995445 14040 8de9f47fabc4ca3bd69b6d795e32751c "" + "/home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty" 1335995445 18425 775b341047ce304520cc7c11ca41392e "" + "/home/ljvm/texmf/tex/generic/oberdiek/pdftexcmds.sty" 1335995445 19987 01cb2f3c1d21e5f05711b7fd50b17f2a "" + "/home/ljvm/texmf/tex/generic/xstring/xstring.sty" 1269905706 144 0ca8d67b000b795a4d9ec000e0fd09c7 "" + "/home/ljvm/texmf/tex/generic/xstring/xstring.tex" 1381789620 54373 fd4487ae3e45d4074bc89aea1d2b6807 "" + "/home/ljvm/texmf/tex/latex/graphics/dvipsnam.def" 1454284088 4945 3b3a8face751255856839a3489e83341 "" + "/home/ljvm/texmf/tex/latex/graphics/graphics.sty" 1454284088 14337 b66dff1d80f6c21e70858a2b3c2d327d "" + "/home/ljvm/texmf/tex/latex/graphics/graphicx.sty" 1428932888 8125 557ab9f1bfa80d369fb45a914aa8a3b4 "" + "/home/ljvm/texmf/tex/latex/graphics/trig.sty" 1454284088 3980 0a268fbfda01e381fa95821ab13b6aee "" + "/home/ljvm/texmf/tex/latex/oberdiek/epstopdf-base.sty" 1303254447 12029 04d7fdf76e0464c23b5aa3a727952d7c "" + "/home/ljvm/texmf/tex/latex/oberdiek/grfext.sty" 1335995445 7075 bd0c34fbf1ae8fd1debd2a554e41b2d5 "" + "/home/ljvm/texmf/tex/latex/oberdiek/kvoptions.sty" 1335995445 22417 c74ff4af6a1aa2b65d1924020edbbe11 "" + "/home/ljvm/texmf/tex/latex/preview/preview.sty" 1447630789 13687 0cb5888b46d12f19ed3b85a16d43470e "" + "/home/ljvm/texmf/tex/latex/preview/prtightpage.def" 1266794019 4841 763a1efd128d3821c07232f7b2638b7b "" + "/home/ljvm/texmf/tex/latex/standalone/standalone.cfg" 1437172902 902 f9a15737aea33182ec1f3542ca20dbfe "" + "/home/ljvm/texmf/tex/latex/standalone/standalone.cls" 1437172902 27291 b798d344bcba4430e7f7dc7561c517d5 "" + "/home/ljvm/texmf/tex/latex/tools/calc.sty" 1454284088 10214 d03d065f799d54f6b7e9b175f8d84279 "" + "/home/ljvm/texmf/tex/latex/xcolor/xcolor.sty" 1169481954 55224 a43bab84e0ac5e6efcaf9a98bde73a94 "" + "/usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map" 1272929888 3287 e6b82fe08f5336d4d5ebc73fb1152e87 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1136768653 1512 f21f83efb36853c0b70002322c1ab3ad "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi9.tfm" 1136768653 1524 d89e2d087a9828407a196f428428ef4a "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1136768653 1300 b62933e007d01cfd073f79b963c01526 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr9.tfm" 1136768653 1292 6b21b9c2c7bebb38aa2273f7ca0fb3af "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss10.tfm" 1136768653 1316 b636689f1933f24d1294acdf6041daaa "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss9.tfm" 1136768653 1320 49357c421c0d469f88b867dd0c3d10e8 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmssbx10.tfm" 1136768653 1272 e2d13f0df30bf3ad990bb9d028e37f34 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1136768653 1116 933a60c408fc0a863a92debe84b2d294 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy9.tfm" 1136768653 1116 25a7bf822c58caf309a702ef79f4afbb "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm" 1136768653 768 1321e9409b4137d6fb428ac9dc956269 "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss10.pfb" 1248133631 24457 5cbb7bdf209d5d1ce9892a9b80a307cc "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss9.pfb" 1248133631 24373 a91d375736817a75026663adcb2190c1 "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmssbx10.pfb" 1248133631 28902 2f5c04fd2884d1878057baa5aad22765 "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb" 1248133631 31099 c85edf1dd5b9e826d67c9c7293b6786c "" + "/usr/share/texlive/texmf-dist/tex/context/base/supp-pdf.mkii" 1337017135 71627 94eb9990bed73c364d7f53f960cc8c5b "" + "/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty" 1284331290 1458 43ab4710dc82f3edeabecd0d099626b2 "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty" 1303254447 7324 11d14f318d865f420e692d4e6c9c18c3 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex" 1288312291 1006 b103be0bfc8c1682ff1fa9760697a329 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex" 1439074469 43226 167a99346bfe2676e3efcdde2d81fe45 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex" 1439074469 19302 4f089dc590e71f7331e6d5b5ea85273b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex" 1439074469 6068 edae1e768a7d8d8f0f00e953d2b0153e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex" 1393459310 7041 a891ad72049e17c4e366c40ca37b0ccb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex" 1393459310 4625 40c07e9f6f2f7c674704b3f2055560ce "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex" 1203877327 2631 7eefa6cdbefd8d4e2bad7262cf1094cd "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex" 1393459310 43477 81143b33d9ebafdeead07ede13372427 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex" 1393459310 17436 8d99d4113be311daf23deff86991ee7d "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex" 1439074469 20772 c57e34db4aa7b1da013169d04b743eac "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex" 1393459310 9641 711f0edc22c180a5caf168b6e8970057 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex" 1393459310 34516 658a71478d21df554bce9d9cd436203a "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex" 1288312291 3052 e5672c657232fd63b0a9853b0746297c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex" 1439074469 16669 4ec6e40088fc6de6334b443fe2dc59f0 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex" 1393459310 21541 4cd19f8ff7dd74d5aa7d803a6397af84 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex" 1439074469 19998 d77fef95c7369827753d17fd11be19c4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex" 1393459310 8943 2e2495b057f8f0035b5568394d489963 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex" 1203727794 437 cf40f841f40822be6cb995f8b47112fd "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex" 1393459310 4611 b858a4e5bd5442802c91a13027dc25bb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex" 1393459310 15934 b941bd3ae7b33179029513707d1f0ff6 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex" 1393459310 5484 4bb4a5cbbd05d6f17a261b59dbd014f1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex" 1203727794 782 2479083eef1ef47450770d40ad81f937 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex" 1288312291 1298 83d7449064b0f0f089f1898a244b6d16 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex" 1393459310 3725 36db4c06798413d051778705f3255eea "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex" 1288312291 4034 0a8cd33cf30d262ec971380666acb2d0 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex" 1203727794 3001 d54bab2f783098ed890fabbeb437b04f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex" 1203727794 527 a8d3e34fbab3dc317cf9b06aa5cdc2e4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex" 1203727794 1158 d6338189706f4587fbc6175c0fb41f17 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex" 1203727794 607 40dc15d3efcf10f095866a94bd544bc1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex" 1203727794 457 ffe9f8b9d108b5f729fd86c78c63589a "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex" 1203727794 447 e87a0add254801e837fa6c18f61f340f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex" 1203727794 1004 86af66805a9d0b62bd41ea0796a64d50 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex" 1203727794 590 7e11000a24bbee9ae2a4cd0e5d88e58c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex" 1288312291 11599 d694704a88e2f9007c996d3a6a4d629c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex" 1439074469 176652 1c2926908e2b356d454795c35385d580 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex" 1393459310 5181 c2b736d254ec36204f8fffd5a45bbd41 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex" 1393459310 31927 7acd27f90dd95ce67ad32166cd0b95ec "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex" 1203727794 2647 defb4a59c2a1d36127a1ac6eebb4a5c1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex" 1393459310 32969 dbcfd5a7de6a0f7255c333ef60287d59 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex" 1288312291 69900 cbd9fafb795a493fb2a3b73713994b78 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex" 1393459310 28333 0189c4cfb5044e700e6ba65a32295f01 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex" 1288312291 132566 291d42c3b23fdb5c47e51b36a5fea0c4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex" 1393459310 37737 ea6cb0b4e615f6048f20ee7153b3cc78 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex" 1288312291 49891 e74f8181c57d9359c941b6bee48fccc2 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex" 1393459310 90791 0f3e73cae9286c96d9fcb2161cc223bc "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex" 1393459310 454 9e9e7c99f4da4f41698be21eaef4938e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex" 1393459310 13416 940ea6971d7a65dc440d3479939c66ae "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex" 1439074469 94097 62ac62cda46eb715560dc27f9ed6e8b1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex" 1393459310 9375 5adc70f722abd29fc250d59e0694b548 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex" 1439074469 22069 7c21c42b15718ce922f36235be360490 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex" 1439074469 8210 a7be5b52ef3d2c087b7dc3d52898b67e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex" 1288312291 3534 c7f28fbac13616513e513efe93b8569b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex" 1393459310 3167 7c9394e79aac27db96a92f9b2792b858 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex" 1439074469 9289 261407875b9dbb0194691c3eb893610f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex" 1439074469 7078 946ddf4a7e57219b6afdbad98eb6731b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex" 1288312291 2688 139c6abc86761a6190c2f4bef5d752be "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex" 1439074469 92284 dcf023dbaa84e6c50e11c2f79fe8cfa6 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex" 1439074469 35430 046e15fbb65e74d8f0e7945f99741fdb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex" 1393459310 7099 f44d505bae6c7c2b933cdd63441db4b9 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex" 1393459310 71902 658cc1e13f73daec4225b8fc1c27600b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex" 1393459310 20934 2328bd2e04520e1ab077ac4ee13b8935 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex" 1439074469 16203 83cbe1220e389eeee283a6168f9a567b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex" 1439074469 42906 d54376d96df1a2ae2d33fb722236d8e9 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg" 1288312291 978 15af626ebd3d4d790aac19170dac04f2 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def" 1393459310 5437 d91f93ed61ecdc57e119849b2d784a0b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def" 1439074469 13507 809d848d9262638e1b1705a68a73c566 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex" 1439074469 35113 2ccc50c1c9573e4bac9230d030f9c67c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex" 1203877327 1983 b5994ebbcee17f1ba3d29bb1bd696fcf "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex" 1393459310 7881 d459d6057e13d10ce7a227ae44b7295e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex" 1393459310 22211 d696ef78c12269178882d218b2cf191d "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex" 1393459310 36194 e194ef4e0b396b531a3891feb4b1cc22 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeysfiltered.code.tex" 1393459310 33377 af391d6ad1bfcbe2278e191f48e43ba8 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex" 1440888734 2536 a3b0529d815a2759ba157b56610a6377 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common-lists.tex" 1393459310 6833 114eda2cf1d348e0e7e477a1a4dc1941 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex" 1439074469 16501 ab0135765e27b6b8dae047831fe84818 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def" 1439074469 5544 294baac9629ba59f675b1f2027ad7136 "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/keyval.tex" 1403829539 2725 fc34ef3ccb37ba15a640e8fca6190bca "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkeyval.tex" 1417732693 19231 26434a5656c684f5ffb1f26f98006baa "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkvutils.tex" 1403829539 7677 6f5ce7c1124cad7ec57d05b2562bd8fe "" + "/usr/share/texlive/texmf-dist/tex/latex/base/article.cls" 1454284088 20708 39fdf9e2fb65617012fa7382a351f485 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty" 1454284088 5159 a08c9bbd48fc492f15b22e458bef961f "" + "/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo" 1454284088 9179 4cd3c5f593e63512893b8ac0123f1bd7 "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg" 1254097189 802 7b8c8d72c24d795ed7720e4dfd29bff3 "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg" 1279039959 678 4792914a8f45be57bb98413425e4c7af "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg" 1278958963 3563 d35e897cae3b8c6848f6677b73370b54 "" + "/usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty" 1177890616 3878 6aa7c08ff2621006e0603349e40a30a8 "" + "/usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def" 1306616590 55368 3c8a0d99822330f2dfabc0dfb09ce897 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf-blur/tikzlibraryshadows.blur.code.tex" 1335333685 7525 063a37c856ae2c38332d93e3d457a299 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty" 1439074469 1197 8a80cdde14696a9198f1793a55dcf332 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty" 1288312291 410 5bf12ea7330e5f12c445332a4fe9a263 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty" 1203877327 21115 facf03b7dbe5ea2f5f1dce1ac84b5d05 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty" 1203727794 1091 d9163d29def82ee90370c8a63667742c "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty" 1203877327 339 592cf35cba3d400082b8a9a5d0199d70 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty" 1393459310 306 0796eafca5e159e6ec2167a6d22d81b1 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty" 1393459310 443 0b2e781830192df35c0fd357cf13e26e "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty" 1393459310 348 8927fde343487e003b01a4c2ca34073b "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty" 1203727794 274 4cad6e665cc93ac2ac979039a94fa1e1 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty" 1203877327 325 2bcd023400636339210573e2b3ee298b "" + "/usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty" 1405118212 5540 d5c60cf09c59da351aa4023ed084e4eb "" + "/usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty" 1417732693 4962 9c1069474ff71dbc47d5006555e352d3 "" + "/usr/share/texlive/texmf-dist/web2c/texmf.cnf" 1503343927 31343 93828589fb0cea665e553ee5a17ad2d4 "" + "/usr/share/texmf/web2c/texmf.cnf" 1503343927 31343 93828589fb0cea665e553ee5a17ad2d4 "" + "/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1528507587 718486 55e051f478d994e1e3e5b41625d996af "" + "optimization_loop.aux" 1528617882 8 a94a2480d3289e625eea47cd1b285758 "" + "optimization_loop.pdf" 1528617882 47217 3dc1b25bb3d586f2b98197f14580d0eb "pdflatex" + "optimization_loop.tex" 1528617876 3644 72cc6263136c7d13e98a24348e978a53 "" + "tikz-uml.sty" 1526185697 302703 ed2f9b5dd419a64f254ecbf6a6653c7f "" + (generated) + "optimization_loop.pdf" + "optimization_loop.log" + "optimization_loop.aux" diff --git a/docs/assets/optimization_loop.fls b/docs/assets/optimization_loop.fls new file mode 100644 index 00000000..6f8c18ec --- /dev/null +++ b/docs/assets/optimization_loop.fls @@ -0,0 +1,248 @@ +PWD /home/ljvm/Documents/Dev/pyswarms/docs/assets +INPUT /etc/texmf/web2c/texmf.cnf +INPUT /usr/share/texmf/web2c/texmf.cnf +INPUT /usr/share/texlive/texmf-dist/web2c/texmf.cnf +INPUT /var/lib/texmf/web2c/pdftex/pdflatex.fmt +INPUT optimization_loop.tex +OUTPUT optimization_loop.log +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cls +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cls +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkeyval.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkvutils.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/keyval.tex +INPUT /dev/null +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cfg +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /home/ljvm/texmf/tex/latex/preview/preview.sty +INPUT /home/ljvm/texmf/tex/latex/preview/preview.sty +INPUT /home/ljvm/texmf/tex/latex/preview/prtightpage.def +INPUT /home/ljvm/texmf/tex/latex/preview/prtightpage.def +INPUT ./optimization_loop.pdf +INPUT optimization_loop.aux +INPUT optimization_loop.aux +INPUT ./optimization_loop.pdf +INPUT /home/ljvm/texmf/tex/latex/xcolor/xcolor.sty +INPUT /home/ljvm/texmf/tex/latex/xcolor/xcolor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def +INPUT /home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/dvipsnam.def +INPUT /home/ljvm/texmf/tex/latex/graphics/dvipsnam.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common-lists.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphicx.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphicx.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphics.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphics.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/trig.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/trig.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeysfiltered.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex +INPUT tikz-uml.sty +INPUT tikz-uml.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.tex +INPUT /home/ljvm/texmf/tex/latex/tools/calc.sty +INPUT /home/ljvm/texmf/tex/latex/tools/calc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex +OUTPUT optimization_loop.pdf +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf-blur/tikzlibraryshadows.blur.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf-blur/tikzlibraryshadows.blur.code.tex +INPUT optimization_loop.aux +INPUT optimization_loop.aux +OUTPUT optimization_loop.aux +INPUT /usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss10.tfm +INPUT /usr/share/texlive/texmf-dist/tex/context/base/supp-pdf.mkii +INPUT /usr/share/texlive/texmf-dist/tex/context/base/supp-pdf.mkii +INPUT /home/ljvm/texmf/tex/generic/oberdiek/pdftexcmds.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/pdftexcmds.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/epstopdf-base.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/epstopdf-base.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/grfext.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/grfext.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvdefinekeys.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvdefinekeys.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/kvoptions.sty +INPUT /home/ljvm/texmf/tex/latex/oberdiek/kvoptions.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvsetkeys.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/kvsetkeys.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/etexcmds.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/etexcmds.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmss9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr6.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy9.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmssbx10.tfm +INPUT /home/ljvm/.texmf-var/fonts/map/pdftex/updmap/pdftex.map +INPUT optimization_loop.aux +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss10.pfb +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss9.pfb +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmssbx10.pfb +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb diff --git a/docs/assets/pyswarms_api.aux b/docs/assets/pyswarms_api.aux new file mode 100644 index 00000000..7c14129a --- /dev/null +++ b/docs/assets/pyswarms_api.aux @@ -0,0 +1,2 @@ +\relax +\gdef \sa@multi@numpages {0} diff --git a/docs/assets/pyswarms_api.fdb_latexmk b/docs/assets/pyswarms_api.fdb_latexmk new file mode 100644 index 00000000..447b804c --- /dev/null +++ b/docs/assets/pyswarms_api.fdb_latexmk @@ -0,0 +1,128 @@ +# Fdb version 3 +["pdflatex"] 1528611763 "pyswarms_api.tex" "pyswarms_api.pdf" "pyswarms_api" 1528611763 + "/dev/null" 1528595442 0 d41d8cd98f00b204e9800998ecf8427e "" + "/etc/texmf/web2c/texmf.cnf" 1515379388 475 c0e671620eb5563b2130f56340a5fde8 "" + "/home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty" 1303254447 7140 ece2cc23d9f20e1f53975ac167f42d3e "" + "/home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty" 1335995445 8253 3bdedc8409aa5d290a2339be6f09af03 "" + "/home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty" 1335995445 18425 775b341047ce304520cc7c11ca41392e "" + "/home/ljvm/texmf/tex/generic/xstring/xstring.sty" 1269905706 144 0ca8d67b000b795a4d9ec000e0fd09c7 "" + "/home/ljvm/texmf/tex/generic/xstring/xstring.tex" 1381789620 54373 fd4487ae3e45d4074bc89aea1d2b6807 "" + "/home/ljvm/texmf/tex/latex/graphics/dvipsnam.def" 1454284088 4945 3b3a8face751255856839a3489e83341 "" + "/home/ljvm/texmf/tex/latex/graphics/graphics.sty" 1454284088 14337 b66dff1d80f6c21e70858a2b3c2d327d "" + "/home/ljvm/texmf/tex/latex/graphics/graphicx.sty" 1428932888 8125 557ab9f1bfa80d369fb45a914aa8a3b4 "" + "/home/ljvm/texmf/tex/latex/graphics/trig.sty" 1454284088 3980 0a268fbfda01e381fa95821ab13b6aee "" + "/home/ljvm/texmf/tex/latex/preview/preview.sty" 1447630789 13687 0cb5888b46d12f19ed3b85a16d43470e "" + "/home/ljvm/texmf/tex/latex/preview/prtightpage.def" 1266794019 4841 763a1efd128d3821c07232f7b2638b7b "" + "/home/ljvm/texmf/tex/latex/standalone/standalone.cfg" 1437172902 902 f9a15737aea33182ec1f3542ca20dbfe "" + "/home/ljvm/texmf/tex/latex/standalone/standalone.cls" 1437172902 27291 b798d344bcba4430e7f7dc7561c517d5 "" + "/home/ljvm/texmf/tex/latex/tools/calc.sty" 1454284088 10214 d03d065f799d54f6b7e9b175f8d84279 "" + "/home/ljvm/texmf/tex/latex/xcolor/xcolor.sty" 1169481954 55224 a43bab84e0ac5e6efcaf9a98bde73a94 "" + "/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty" 1284331290 1458 43ab4710dc82f3edeabecd0d099626b2 "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty" 1303254447 7324 11d14f318d865f420e692d4e6c9c18c3 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex" 1288312291 1006 b103be0bfc8c1682ff1fa9760697a329 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex" 1439074469 43226 167a99346bfe2676e3efcdde2d81fe45 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex" 1439074469 19302 4f089dc590e71f7331e6d5b5ea85273b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex" 1439074469 6068 edae1e768a7d8d8f0f00e953d2b0153e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex" 1393459310 7041 a891ad72049e17c4e366c40ca37b0ccb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex" 1393459310 4625 40c07e9f6f2f7c674704b3f2055560ce "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex" 1203877327 2631 7eefa6cdbefd8d4e2bad7262cf1094cd "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex" 1393459310 43477 81143b33d9ebafdeead07ede13372427 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex" 1393459310 17436 8d99d4113be311daf23deff86991ee7d "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex" 1439074469 20772 c57e34db4aa7b1da013169d04b743eac "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex" 1393459310 9641 711f0edc22c180a5caf168b6e8970057 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex" 1393459310 34516 658a71478d21df554bce9d9cd436203a "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex" 1288312291 3052 e5672c657232fd63b0a9853b0746297c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex" 1439074469 16669 4ec6e40088fc6de6334b443fe2dc59f0 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex" 1393459310 21541 4cd19f8ff7dd74d5aa7d803a6397af84 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex" 1439074469 19998 d77fef95c7369827753d17fd11be19c4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex" 1393459310 8943 2e2495b057f8f0035b5568394d489963 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex" 1203727794 437 cf40f841f40822be6cb995f8b47112fd "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex" 1393459310 4611 b858a4e5bd5442802c91a13027dc25bb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex" 1393459310 5484 4bb4a5cbbd05d6f17a261b59dbd014f1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex" 1203727794 782 2479083eef1ef47450770d40ad81f937 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex" 1288312291 1298 83d7449064b0f0f089f1898a244b6d16 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex" 1393459310 3725 36db4c06798413d051778705f3255eea "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex" 1203727794 3001 d54bab2f783098ed890fabbeb437b04f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex" 1203727794 527 a8d3e34fbab3dc317cf9b06aa5cdc2e4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex" 1203727794 1158 d6338189706f4587fbc6175c0fb41f17 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex" 1203727794 607 40dc15d3efcf10f095866a94bd544bc1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex" 1203727794 457 ffe9f8b9d108b5f729fd86c78c63589a "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex" 1203727794 447 e87a0add254801e837fa6c18f61f340f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex" 1203727794 1004 86af66805a9d0b62bd41ea0796a64d50 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex" 1203727794 590 7e11000a24bbee9ae2a4cd0e5d88e58c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex" 1288312291 11599 d694704a88e2f9007c996d3a6a4d629c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex" 1439074469 176652 1c2926908e2b356d454795c35385d580 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex" 1393459310 5181 c2b736d254ec36204f8fffd5a45bbd41 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex" 1393459310 31927 7acd27f90dd95ce67ad32166cd0b95ec "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex" 1203727794 2647 defb4a59c2a1d36127a1ac6eebb4a5c1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex" 1393459310 32969 dbcfd5a7de6a0f7255c333ef60287d59 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex" 1288312291 69900 cbd9fafb795a493fb2a3b73713994b78 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex" 1393459310 28333 0189c4cfb5044e700e6ba65a32295f01 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex" 1288312291 132566 291d42c3b23fdb5c47e51b36a5fea0c4 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex" 1393459310 37737 ea6cb0b4e615f6048f20ee7153b3cc78 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex" 1288312291 49891 e74f8181c57d9359c941b6bee48fccc2 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex" 1393459310 90791 0f3e73cae9286c96d9fcb2161cc223bc "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex" 1393459310 454 9e9e7c99f4da4f41698be21eaef4938e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex" 1393459310 13416 940ea6971d7a65dc440d3479939c66ae "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex" 1439074469 94097 62ac62cda46eb715560dc27f9ed6e8b1 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex" 1393459310 9375 5adc70f722abd29fc250d59e0694b548 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex" 1439074469 22069 7c21c42b15718ce922f36235be360490 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex" 1439074469 8210 a7be5b52ef3d2c087b7dc3d52898b67e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex" 1288312291 3534 c7f28fbac13616513e513efe93b8569b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex" 1393459310 3167 7c9394e79aac27db96a92f9b2792b858 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex" 1439074469 9289 261407875b9dbb0194691c3eb893610f "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex" 1439074469 7078 946ddf4a7e57219b6afdbad98eb6731b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex" 1288312291 2688 139c6abc86761a6190c2f4bef5d752be "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex" 1439074469 92284 dcf023dbaa84e6c50e11c2f79fe8cfa6 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex" 1439074469 35430 046e15fbb65e74d8f0e7945f99741fdb "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex" 1393459310 7099 f44d505bae6c7c2b933cdd63441db4b9 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex" 1393459310 71902 658cc1e13f73daec4225b8fc1c27600b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex" 1393459310 20934 2328bd2e04520e1ab077ac4ee13b8935 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex" 1439074469 16203 83cbe1220e389eeee283a6168f9a567b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex" 1439074469 42906 d54376d96df1a2ae2d33fb722236d8e9 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg" 1288312291 978 15af626ebd3d4d790aac19170dac04f2 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def" 1393459310 5437 d91f93ed61ecdc57e119849b2d784a0b "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def" 1439074469 13507 809d848d9262638e1b1705a68a73c566 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex" 1439074469 35113 2ccc50c1c9573e4bac9230d030f9c67c "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex" 1203877327 1983 b5994ebbcee17f1ba3d29bb1bd696fcf "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex" 1393459310 7881 d459d6057e13d10ce7a227ae44b7295e "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex" 1393459310 22211 d696ef78c12269178882d218b2cf191d "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex" 1393459310 36194 e194ef4e0b396b531a3891feb4b1cc22 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeysfiltered.code.tex" 1393459310 33377 af391d6ad1bfcbe2278e191f48e43ba8 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex" 1440888734 2536 a3b0529d815a2759ba157b56610a6377 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common-lists.tex" 1393459310 6833 114eda2cf1d348e0e7e477a1a4dc1941 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex" 1439074469 16501 ab0135765e27b6b8dae047831fe84818 "" + "/usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def" 1439074469 5544 294baac9629ba59f675b1f2027ad7136 "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/keyval.tex" 1403829539 2725 fc34ef3ccb37ba15a640e8fca6190bca "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkeyval.tex" 1417732693 19231 26434a5656c684f5ffb1f26f98006baa "" + "/usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkvutils.tex" 1403829539 7677 6f5ce7c1124cad7ec57d05b2562bd8fe "" + "/usr/share/texlive/texmf-dist/tex/latex/base/article.cls" 1454284088 20708 39fdf9e2fb65617012fa7382a351f485 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty" 1454284088 5159 a08c9bbd48fc492f15b22e458bef961f "" + "/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo" 1454284088 9179 4cd3c5f593e63512893b8ac0123f1bd7 "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg" 1254097189 802 7b8c8d72c24d795ed7720e4dfd29bff3 "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg" 1278958963 3563 d35e897cae3b8c6848f6677b73370b54 "" + "/usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty" 1177890616 3878 6aa7c08ff2621006e0603349e40a30a8 "" + "/usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def" 1306616590 55368 3c8a0d99822330f2dfabc0dfb09ce897 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty" 1439074469 1197 8a80cdde14696a9198f1793a55dcf332 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty" 1288312291 410 5bf12ea7330e5f12c445332a4fe9a263 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty" 1203877327 21115 facf03b7dbe5ea2f5f1dce1ac84b5d05 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty" 1203727794 1091 d9163d29def82ee90370c8a63667742c "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty" 1203877327 339 592cf35cba3d400082b8a9a5d0199d70 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty" 1393459310 306 0796eafca5e159e6ec2167a6d22d81b1 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty" 1393459310 443 0b2e781830192df35c0fd357cf13e26e "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty" 1393459310 348 8927fde343487e003b01a4c2ca34073b "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty" 1203727794 274 4cad6e665cc93ac2ac979039a94fa1e1 "" + "/usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty" 1203877327 325 2bcd023400636339210573e2b3ee298b "" + "/usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty" 1405118212 5540 d5c60cf09c59da351aa4023ed084e4eb "" + "/usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty" 1417732693 4962 9c1069474ff71dbc47d5006555e352d3 "" + "/usr/share/texlive/texmf-dist/web2c/texmf.cnf" 1503343927 31343 93828589fb0cea665e553ee5a17ad2d4 "" + "/usr/share/texmf/web2c/texmf.cnf" 1503343927 31343 93828589fb0cea665e553ee5a17ad2d4 "" + "/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1528507587 718486 55e051f478d994e1e3e5b41625d996af "" + "pyswarms_api.aux" 1528611763 8 a94a2480d3289e625eea47cd1b285758 "" + "pyswarms_api.pdf" 1528611763 28449 bf8aa2f620d27c4141ae07f6a16f679b "pdflatex" + "pyswarms_api.tex" 1528611725 3224 d99518edfd37f8be7c602d2ec9643998 "" + "tikz-uml.sty" 1526185697 302703 ed2f9b5dd419a64f254ecbf6a6653c7f "" + (generated) + "pyswarms_api.log" + "pyswarms_api.pdf" + "pyswarms_api.aux" diff --git a/docs/assets/pyswarms_api.fls b/docs/assets/pyswarms_api.fls new file mode 100644 index 00000000..8b96c082 --- /dev/null +++ b/docs/assets/pyswarms_api.fls @@ -0,0 +1,204 @@ +PWD /home/ljvm/Documents/Dev/pyswarms/docs/assets +INPUT /etc/texmf/web2c/texmf.cnf +INPUT /usr/share/texmf/web2c/texmf.cnf +INPUT /usr/share/texlive/texmf-dist/web2c/texmf.cnf +INPUT /var/lib/texmf/web2c/pdftex/pdflatex.fmt +INPUT pyswarms_api.tex +OUTPUT pyswarms_api.log +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cls +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cls +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifluatex.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ifpdf.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/xkeyval/xkeyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkeyval.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/xkvutils.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/xkeyval/keyval.tex +INPUT /dev/null +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cfg +INPUT /home/ljvm/texmf/tex/latex/standalone/standalone.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /home/ljvm/texmf/tex/latex/preview/preview.sty +INPUT /home/ljvm/texmf/tex/latex/preview/preview.sty +INPUT /home/ljvm/texmf/tex/latex/preview/prtightpage.def +INPUT /home/ljvm/texmf/tex/latex/preview/prtightpage.def +INPUT ./pyswarms_api.pdf +INPUT pyswarms_api.aux +INPUT pyswarms_api.aux +INPUT ./pyswarms_api.pdf +INPUT /home/ljvm/texmf/tex/latex/xcolor/xcolor.sty +INPUT /home/ljvm/texmf/tex/latex/xcolor/xcolor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def +INPUT /home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/infwarerr.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty +INPUT /home/ljvm/texmf/tex/generic/oberdiek/ltxcmds.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/dvipsnam.def +INPUT /home/ljvm/texmf/tex/latex/graphics/dvipsnam.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-common-lists.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/ms/everyshi.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphicx.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphicx.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphics.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/graphics.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/trig.sty +INPUT /home/ljvm/texmf/tex/latex/graphics/trig.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeysfiltered.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgffor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgf/math/pgfmath.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex +INPUT tikz-uml.sty +INPUT tikz-uml.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/ifthen.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.sty +INPUT /home/ljvm/texmf/tex/generic/xstring/xstring.tex +INPUT /home/ljvm/texmf/tex/latex/tools/calc.sty +INPUT /home/ljvm/texmf/tex/latex/tools/calc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/pgfopts/pgfopts.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.misc.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.symbols.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.arrows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.callouts.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.multipart.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfit.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshadows.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/pgflibraryfadings.code.tex +OUTPUT pyswarms_api.pdf +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex +INPUT /usr/share/texlive/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.markings.code.tex diff --git a/pyswarms/base/base_discrete.py b/pyswarms/base/base_discrete.py index 31697d02..fe0853f0 100644 --- a/pyswarms/base/base_discrete.py +++ b/pyswarms/base/base_discrete.py @@ -113,7 +113,7 @@ def __init__(self, n_particles, dimensions, binary, options, initial positions. When passed with a :code:`False` value, random integers from 0 to :code:`dimensions` are generated. options : dict with keys :code:`{'c1', 'c2', 'w'}` - a dictionary containing the parameters for the specific + a dictionary containing the parameters for the specific optimization technique * c1 : float cognitive parameter @@ -168,31 +168,6 @@ def _populate_history(self, hist): self.pos_history.append(hist.position) self.velocity_history.append(hist.velocity) - @property - def get_cost_history(self): - """Get cost history""" - return np.array(self.cost_history) - - @property - def get_mean_pbest_history(self): - """Get mean personal best history""" - return np.array(self.mean_pbest_history) - - @property - def get_mean_neighbor_history(self): - """Get mean neighborhood cost history""" - return np.array(self.mean_neighbor_history) - - @property - def get_pos_history(self): - """Get position history""" - return np.array(self.pos_history) - - @property - def get_velocity_history(self): - """Get velocity history""" - return np.array(self.velocity_history) - def optimize(self, objective_func, iters, print_step=1, verbose=1): """Optimizes the swarm for a number of iterations. @@ -267,25 +242,10 @@ def reset(self): self.pos_history = [] self.velocity_history = [] - # Generate initial position - self.pos = np.random.random_sample(size=self.swarm_size).\ - argsort(axis=1) - if self.binary: - self.pos = np.random.randint(2, size=self.swarm_size) - - # Initialize velocity vectors - if self.velocity_clamp is not None: - min_velocity, max_velocity = self.velocity_clamp[0],\ - self.velocity_clamp[1] - self.velocity = ((max_velocity - min_velocity) - * np.random.random_sample(size=self.swarm_size) - + min_velocity) - else: - self.velocity = np.random.random_sample(size=self.swarm_size) - - # Initialize the best cost of the swarm - self.best_cost = np.inf - self.best_pos = None - - # Initialize the personal best of each particle - self.personal_best_pos = self.pos + # Initialize the swarm + self.swarm = create_swarm(n_particles=self.n_particles, + dimensions=self.dimensions, + discrete=True, + init_pos=self.init_pos, + binary=self.binary, + clamp=self.velocity_clamp, options=self.options) diff --git a/pyswarms/base/base_single.py b/pyswarms/base/base_single.py index 16def2e1..c2535457 100644 --- a/pyswarms/base/base_single.py +++ b/pyswarms/base/base_single.py @@ -193,31 +193,6 @@ def _populate_history(self, hist): self.pos_history.append(hist.position) self.velocity_history.append(hist.velocity) - @property - def get_cost_history(self): - """Get cost history""" - return np.array(self.cost_history) - - @property - def get_mean_pbest_history(self): - """Get mean personal best history""" - return np.array(self.mean_pbest_history) - - @property - def get_mean_neighbor_history(self): - """Get mean neighborhood cost history""" - return np.array(self.mean_neighbor_history) - - @property - def get_pos_history(self): - """Get position history""" - return np.array(self.pos_history) - - @property - def get_velocity_history(self): - """Get velocity history""" - return np.array(self.velocity_history) - def optimize(self, objective_func, iters, print_step=1, verbose=1): """Optimizes the swarm for a number of iterations. From 5343a1c38166cc60a5b2925b702e37822b3d71c0 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Tue, 12 Jun 2018 22:27:14 +0900 Subject: [PATCH 04/38] Update test on optimizers In this commit, we updated the tests on the optimizers to reflect the changes on using @property. It's much better now because our tests are parameterized than having multiple asserts. Signed-off-by: Lester James V. Miranda --- tests/optimizers/test_binary.py | 16 +++++++++------- tests/optimizers/test_global_best.py | 17 ++++++++++------- tests/optimizers/test_local_best.py | 17 ++++++++++------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/tests/optimizers/test_binary.py b/tests/optimizers/test_binary.py index 618a3b42..243986e7 100644 --- a/tests/optimizers/test_binary.py +++ b/tests/optimizers/test_binary.py @@ -7,7 +7,6 @@ # Import from package from pyswarms.discrete import BinaryPSO -from pyswarms.utils.functions.single_obj import sphere_func @pytest.mark.parametrize('options', [ {'c2':0.7, 'w':0.5, 'k': 2, 'p': 2}, @@ -59,10 +58,13 @@ def test_reset_default_values(binary_reset): assert binary_reset.best_cost == np.inf assert binary_reset.best_pos == None -def test_training_history_shape(binary_history): +@pytest.mark.parametrize('history, expected_shape', [ + ('cost_history', (1000,)), + ('mean_pbest_history', (1000,)), + ('mean_neighbor_history',(1000,)), + ('pos_history',(1000, 10, 2)), + ('velocity_history',(1000, 10, 2))]) +def test_training_history_shape(binary_history, history, expected_shape): """Test if training histories are of expected shape""" - assert binary_history.get_cost_history.shape == (1000,) - assert binary_history.get_mean_pbest_history.shape == (1000,) - assert binary_history.get_mean_neighbor_history.shape == (1000,) - assert binary_history.get_pos_history.shape == (1000, 10, 2) - assert binary_history.get_velocity_history.shape == (1000, 10, 2) + pso = vars(binary_history) + assert np.array(pso[history]).shape == expected_shape diff --git a/tests/optimizers/test_global_best.py b/tests/optimizers/test_global_best.py index 42611ea4..aa6cea44 100644 --- a/tests/optimizers/test_global_best.py +++ b/tests/optimizers/test_global_best.py @@ -74,16 +74,19 @@ def test_reset_default_values(gbest_reset): assert gbest_reset.swarm.best_cost == np.inf assert set(gbest_reset.swarm.best_pos) == set(np.array([])) -def test_training_history_shape(gbest_history): +@pytest.mark.parametrize('history, expected_shape', [ + ('cost_history', (1000,)), + ('mean_pbest_history', (1000,)), + ('mean_neighbor_history',(1000,)), + ('pos_history',(1000, 10, 2)), + ('velocity_history',(1000, 10, 2))]) +def test_training_history_shape(gbest_history, history, expected_shape): """Test if training histories are of expected shape""" - assert gbest_history.get_cost_history.shape == (1000,) - assert gbest_history.get_mean_pbest_history.shape == (1000,) - assert gbest_history.get_mean_neighbor_history.shape == (1000,) - assert gbest_history.get_pos_history.shape == (1000, 10, 2) - assert gbest_history.get_velocity_history.shape == (1000, 10, 2) + pso = vars(gbest_history) + assert np.array(pso[history]).shape == expected_shape def test_ftol_effect(options): """Test if setting the ftol breaks the optimization process accodingly""" pso = GlobalBestPSO(10, 2, options=options, ftol=1e-1) pso.optimize(sphere_func, 2000, verbose=0) - assert pso.get_cost_history.shape != (2000,) \ No newline at end of file + 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 ab745924..14544ec7 100644 --- a/tests/optimizers/test_local_best.py +++ b/tests/optimizers/test_local_best.py @@ -87,16 +87,19 @@ def test_reset_default_values(lbest_reset): assert lbest_reset.swarm.best_cost == np.inf assert set(lbest_reset.swarm.best_pos) == set(np.array([])) -def test_training_history_shape(lbest_history): +@pytest.mark.parametrize('history, expected_shape', [ + ('cost_history', (1000,)), + ('mean_pbest_history', (1000,)), + ('mean_neighbor_history',(1000,)), + ('pos_history',(1000, 10, 2)), + ('velocity_history',(1000, 10, 2))]) +def test_training_history_shape(lbest_history, history, expected_shape): """Test if training histories are of expected shape""" - assert lbest_history.get_cost_history.shape == (1000,) - assert lbest_history.get_mean_pbest_history.shape == (1000,) - assert lbest_history.get_mean_neighbor_history.shape == (1000,) - assert lbest_history.get_pos_history.shape == (1000, 10, 2) - assert lbest_history.get_velocity_history.shape == (1000, 10, 2) + pso = vars(lbest_history) + assert np.array(pso[history]).shape == expected_shape def test_ftol_effect(options): """Test if setting the ftol breaks the optimization process accodingly""" pso = LocalBestPSO(10, 2, options=options, ftol=1e-1) pso.optimize(sphere_func, 2000, verbose=0) - assert pso.get_cost_history.shape != (2000,) + assert np.array(pso.cost_history).shape != (2000,) From b128fa2f3ca689e457e6035a72effb1d4a7f3289 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Tue, 12 Jun 2018 22:28:16 +0900 Subject: [PATCH 05/38] Mark environment tests as expected failures This coincides with the decision to deprecate the PlotEnvironment. The whole environments module is now unsupported, and users will now be required to use the plotters module instead. Signed-off-by: Lester James V. Miranda --- tests/utils/environments/conftest.py | 9 ++++----- tests/utils/environments/test_plot_environment.py | 12 +++++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/utils/environments/conftest.py b/tests/utils/environments/conftest.py index 55eb42f4..022d5674 100644 --- a/tests/utils/environments/conftest.py +++ b/tests/utils/environments/conftest.py @@ -6,7 +6,6 @@ # Import modules import os import pytest -import numpy as np from mock import Mock import matplotlib as mpl @@ -23,9 +22,9 @@ def mock_pso(): """Returns a function that mocks a PSO class with missing attributes""" def _mock_pso(index): class_methods = [ - 'get_cost_history', - 'get_pos_history', - 'get_velocity_history', + 'cost_history', + 'pos_history', + 'velocity_history', 'optimize', 'reset' ] @@ -37,4 +36,4 @@ def _mock_pso(index): def plot_environment(): """Returns a PlotEnvironment instance""" optimizer = GlobalBestPSO(10, 3, options={'c1': 0.5, 'c2': 0.3, 'w': 0.9}) - return PlotEnvironment(optimizer, sphere_func, 1000) \ No newline at end of file + return PlotEnvironment(optimizer, sphere_func, 1000) diff --git a/tests/utils/environments/test_plot_environment.py b/tests/utils/environments/test_plot_environment.py index 1318ab92..aeaaec1e 100644 --- a/tests/utils/environments/test_plot_environment.py +++ b/tests/utils/environments/test_plot_environment.py @@ -4,7 +4,6 @@ # Import modules import os import pytest -import numpy as np import matplotlib as mpl if os.environ.get('DISPLAY','') == '': @@ -18,9 +17,9 @@ from pyswarms.utils.functions.single_obj import sphere_func class_methods = [ - 'get_cost_history', - 'get_pos_history', - 'get_velocity_history', + 'cost_history', + 'pos_history', + 'velocity_history', 'optimize', 'reset' ] @@ -33,14 +32,17 @@ def test_getters_pso(mock_pso, attributes): m = mock_pso(idx) PlotEnvironment(m, sphere_func, 100) +@pytest.mark.xfail def test_plot_cost_return_type(plot_environment): """Tests if plot_cost() returns a SubplotBase instance""" assert isinstance(plot_environment.plot_cost(),SubplotBase) +@pytest.mark.xfail def test_plot2D_return_type(plot_environment): """Test if plot_particles2D() returns a FuncAnimation instance""" assert isinstance(plot_environment.plot_particles2D(), FuncAnimation) +@pytest.mark.xfail def test_plot3D_return_type(plot_environment): """Test if plot_particles3D() returns a FuncAnimation instance""" - assert isinstance(plot_environment.plot_particles3D(), FuncAnimation) \ No newline at end of file + assert isinstance(plot_environment.plot_particles3D(), FuncAnimation) From 78aaa124ed25a8600683c9e2f0bde8a610513674 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Tue, 12 Jun 2018 21:59:14 +0900 Subject: [PATCH 06/38] Add plotters.py module Reference: #130 This commit adds a plotters.py module to replace the environments module. We hope that we can actually decouple the optimization and visualization part, without having the environment to do another rollout of your PSO. Signed-off-by: Lester James V. Miranda --- pyswarms/utils/plotters/__init__.py | 8 + pyswarms/utils/plotters/plotters.py | 373 ++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 pyswarms/utils/plotters/__init__.py create mode 100644 pyswarms/utils/plotters/plotters.py diff --git a/pyswarms/utils/plotters/__init__.py b/pyswarms/utils/plotters/__init__.py new file mode 100644 index 00000000..2ad916e9 --- /dev/null +++ b/pyswarms/utils/plotters/__init__.py @@ -0,0 +1,8 @@ +""" +The mod:`pyswarms.utils.plotters` module implements various +visualization capabilities to interact with your swarm. Here, +ou can plot cost history and animate your swarm in both 2D or +3D spaces. +""" + +from .plotters import * \ No newline at end of file diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py new file mode 100644 index 00000000..184c085b --- /dev/null +++ b/pyswarms/utils/plotters/plotters.py @@ -0,0 +1,373 @@ +# -*- coding: utf-8 -*- + +r""" +Plotting tool for Optimizer Analysis + +This module is built on top of :code:`matplotlib` to render quick and easy +plots for your optimizer. It can plot the best cost for each iteration, and +show animations of the particles in 2-D and 3-D space. Furthermore, because +it has :code:`matplotlib` running under the hood, the plots are easily +customizable. + +For example, if we want to plot the cost, simply run the optimizer, get the +cost history from the optimizer instance, and pass it to the +:code:`plot_cost_history()` method + +.. code-block:: python + + import pyswarms as ps + from pyswarms.utils.functions.single_obj import sphere_func + from pyswarms.utils.plotters import plot_cost_history + + # Set up optimizer + options = {'c1':0.5, 'c2':0.3, 'w':0.9} + optimizer = ps.single.GlobalBestPSO(n_particles=10, dimensions=2, + options=options) + + # Obtain cost history from optimizer instance + cost_history = optimizer.cost_history + + # Plot! + plot_cost_history(cost_history) + plt.show() + +In case you want to plot the particle movement, it is important that either +one of the :code:`matplotlib` animation :code:`Writers` is installed. These +doesn't come out of the box for :code:`pyswarms`, and must be installed +separately. For example, in a Linux or Windows distribution, you can install +:code:`ffmpeg` as + + >>> conda install -c conda-forge ffmpeg + +Now, if you want to plot your particles in a 2-D environment, simply pass +the position history of your swarm (obtainable from swarm instance): + + +.. code-block:: python + + import pyswarms as ps + from pyswarms.utils.functions.single_obj import sphere_func + from pyswarms.utils.plotters import plot_cost_history + + # Set up optimizer + options = {'c1':0.5, 'c2':0.3, 'w':0.9} + optimizer = ps.single.GlobalBestPSO(n_particles=10, dimensions=2, + options=options) + + # Obtain pos history from optimizer instance + pos_history = optimizer.pos_history + + # Plot! + plot_trajectory2D(pos_history) + +You can also supply various arguments in this method: the indices of the +specific dimensions to be used, the limits of the axes, and the interval/ +speed of animation. +""" + +# Import modules +import logging +from collections import namedtuple + +import matplotlib.pyplot as plt +import numpy as np +from matplotlib import (animation, cm) +from mpl_toolkits.mplot3d import Axes3D + +# Import from package +from .formatters import (Designer, Animator, Mesher) + +# Initialize logger +logger = logging.getLogger(__name__) + +def plot_cost_history(cost_history, ax=None, title='Cost History', + designer=None, **kwargs): + """Creates a simple line plot with the cost in the y-axis and + the iteration at the x-axis + + Parameters + ---------- + cost_history : list or numpy.ndarray + Cost history of shape :code:`(iters, )` or length :code:`iters` where + each element contains the cost for the given iteration. + ax : :class:`matplotlib.axes.Axes` (default is :code:`None`) + The axes where the plot is to be drawn. If :code:`None` is + passed, then the plot will be drawn to a new set of axes. + title : str (default is :code:`'Cost History'`) + The title of the plotted graph. + designer : pyswarms.utils.formatters.Designer (default is :code:`None`) + Designer class for custom attributes + **kwargs : dict + Keyword arguments that are passed as a keyword argument to + :class:`matplotlib.axes.Axes` + + Returns + ------- + :class:`matplotlib.axes._subplots.AxesSubplot` + The axes on which the plot was drawn. + """ + try: + # Infer number of iterations based on the length + # of the passed array + iters = len(cost_history) + + # If no Designer class supplied, use defaults + if designer is None: + designer = Designer() + + # If no ax supplied, create new instance + if ax is None: + _, ax = plt.subplots(1,1, figsize=designer.figsize) + + # Plot with iters in x-axis and the cost in y-axis + ax.plot(np.arange(iters), cost_history, 'k', lw=2, label=designer.label) + + # Customize plot depending on parameters + ax.set_title(title, fontsize=designer.title_fontsize) + ax.legend(fontsize=designer.text_fontsize) + ax.set_xlabel('Iterations', fontsize=designer.text_fontsize) + ax.set_ylabel('Cost', fontsize=designer.text_fontsize) + ax.tick_params(labelsize=designer.text_fontsize) + except TypeError: + raise + else: + return ax + +def plot_contour(pos_history, canvas=None, title='Trajectory', mark=None, + designer=None, mesher=None, animator=None, **kwargs): + """Draws a 2D contour map for particle trajectories + + Here, the space is represented as flat plane. The contours indicate the + elevation with respect to the objective function. This works best with + 2-dimensional swarms with their fitness in z-space. + + Parameters + ---------- + pos_history : numpy.ndarray or list + Position history of the swarm with shape + :code:`(iteration, n_particles, dimensions)` + canvas : tuple of :class:`matplotlib.figure.Figure` and :class:`matplotlib.axes.Axes` (default is :code:`None`) + The (figure, axis) where all the events will be draw. If :code:`None` is + supplied, then plot will be drawn to a fresh set of canvas. + title : str (default is :code:`'Trajectory'`) + The title of the plotted graph. + mark : tuple (default is :code:`None`) + Marks a particular point with a red crossmark. Useful for marking + the optima. + designer : pyswarms.utils.formatters.Designer (default is :code:`None`) + Designer class for custom attributes + mesher : pyswarms.utils.formatters.Mesher (default is :code:`None`) + Mesher class for mesh plots + animator : pyswarms.utils.formatters.Animator (default is :code:`None`) + Animator class for custom animation + **kwargs : dict + Keyword arguments that are passed as a keyword argument to + :class:`matplotlib.axes.Axes` plotting function + + Returns + ------- + :class:`matplotlib.animation.FuncAnimation` + The drawn animation that can be saved to mp4 or other + third-party tools + """ + + try: + # If no Designer class supplied, use defaults + if designer is None: + designer = Designer(limits=[(-1,1), (-1,1)], label=['x-axis', 'y-axis']) + + # If no Animator class supplied, use defaults + if animator is None: + animator = Animator() + + # If ax is default, then create new plot. Set-up the figure, the + # axis, and the plot element that we want to animate + if canvas is None: + fig, ax = plt.subplots(1, 1, figsize=designer.figsize) + else: + fig, ax = canvas + + # Get number of iterations + n_iters = len(pos_history) + + # Customize plot + ax.set_title(title, fontsize=designer.title_fontsize) + ax.set_xlabel(designer.label[0], fontsize=designer.text_fontsize) + ax.set_ylabel(designer.label[1], fontsize=designer.text_fontsize) + ax.set_xlim(designer.limits[0]) + ax.set_ylim(designer.limits[1]) + + # Make a contour map if possible + if mesher is not None: + xx, yy, zz, = _mesh(mesher) + ax.contour(xx, yy, zz, levels=mesher.levels) + + # Mark global best if possible + if mark is not None: + ax.scatter(mark[0], mark[1], color='red', marker='x') + + # Put scatter skeleton + plot = ax.scatter(x=[], y=[], c='black', alpha=0.6, **kwargs) + + # Do animation + anim = animation.FuncAnimation(fig=fig, + func=_animate, + frames=range(n_iters), + fargs=(pos_history, plot), + interval=animator.interval, + repeat=animator.repeat, + repeat_delay=animator.repeat_delay) + except TypeError: + raise + else: + return anim + +def plot_surface(pos_history, canvas=None, title='Trajectory', + designer=None, mesher=None, animator=None, mark=None, **kwargs): + """Plots a swarm's trajectory in 3D + + This is useful for plotting the swarm's 2-dimensional position with + respect to the objective function. The value in the z-axis is the fitness + of the 2D particle when passed to the objective function. When preparing the + position history, make sure that the: + + * first column is the position in the x-axis, + * second column is the position in the y-axis; and + * third column is the fitness of the 2D particle + + The :class:`pyswarms.utils.plotters.formatters.Mesher` class provides a + method that prepares this history given a 2D pos history from any + optimizer. + + .. code-block:: python + + import pyswarms as ps + from pyswarms.utils.functions.single_obj import sphere_func + from pyswarms.utils.plotters import plot_surface + from pyswarms.utils.plotters.formatters import Mesher + + # Run optimizer + options = {'c1':0.5, 'c2':0.3, 'w':0.9} + optimizer = ps.single.GlobalBestPSO(n_particles=10, dimensions=2, options) + + # Prepare position history + m = Mesher(func=sphere_func) + pos_history_3d = m.compute_history_3d(optimizer.pos_history) + + # Plot! + plot_surface(pos_history_3d) + + Parameters + ---------- + pos_history : numpy.ndarray + Position history of the swarm with shape + :code:`(iteration, n_particles, 3)` + objective_func : callable + The objective function that takes a swarm of shape + :code:`(n_particles, 2)` and returns a fitness array + of :code:`(n_particles, )` + canvas : tuple of :class:`matplotlib.figure.Figure` and + :class:`matplotlib.axes.Axes` (default is :code:`None`) + The (figure, axis) where all the events will be draw. If :code:`None` + is supplied, then plot will be drawn to a fresh set of canvas. + title : str (default is :code:`'Trajectory'`) + The title of the plotted graph. + mark : tuple (default is :code:`None`) + Marks a particular point with a red crossmark. Useful for marking the + optima. + designer : pyswarms.utils.formatters.Designer (default is :code:`None`) + Designer class for custom attributes + mesher : pyswarms.utils.formatters.Mesher (default is :code:`None`) + Mesher class for mesh plots + animator : pyswarms.utils.formatters.Animator (default is :code:`None`) + Animator class for custom animation + **kwargs : dict + Keyword arguments that are passed as a keyword argument to + :class:`matplotlib.axes.Axes` plotting function + + Returns + ------- + :class:`matplotlib.animation.FuncAnimation` + The drawn animation that can be saved to mp4 or other + third-party tools + """ + try: + # If no Designer class supplied, use defaults + if designer is None: + designer = Designer(limits=[(-1,1), (-1,1), (-1,1)], + label=['x-axis', 'y-axis', 'z-axis']) + + # If no Animator class supplied, use defaults + if animator is None: + animator = Animator() + + # If ax is default, then create new plot. Set-up the figure, the + # axis, and the plot element that we want to animate + if canvas is None: + fig, ax = plt.subplots(1, 1, figsize=designer.figsize) + else: + fig, ax = canvas + + # Initialize 3D-axis + ax = Axes3D(fig) + + # Get number of iterations + n_iters = len(pos_history) + + # Customize plot + ax.set_title(title, fontsize=designer.title_fontsize) + ax.set_xlabel(designer.label[0], fontsize=designer.text_fontsize) + ax.set_ylabel(designer.label[1], fontsize=designer.text_fontsize) + ax.set_zlabel(designer.label[2], fontsize=designer.text_fontsize) + ax.set_xlim(designer.limits[0]) + ax.set_ylim(designer.limits[1]) + ax.set_zlim(designer.limits[2]) + + # Make a contour map if possible + if mesher is not None: + xx, yy, zz, = _mesh(mesher) + ax.plot_surface(xx, yy, zz, cmap=cm.viridis, alpha=mesher.alpha) + + # Mark global best if possible + if mark is not None: + ax.scatter(mark[0], mark[1], mark[2], color='red', marker='x') + + # Put scatter skeleton + plot = ax.scatter(xs=[], ys=[], zs=[], c='black', alpha=0.6, **kwargs) + + # Do animation + anim = animation.FuncAnimation(fig=fig, + func=_animate, + frames=range(n_iters), + fargs=(pos_history, plot), + interval=animator.interval, + repeat=animator.repeat, + repeat_delay=animator.repeat_delay) + except TypeError: + raise + else: + return anim + +def _animate(i, data, plot): + """Helper animation function that is called sequentially + :class:`matplotlib.animation.FuncAnimation` + """ + current_pos = data[i] + if np.array(current_pos).shape[1] == 2: + plot.set_offsets(current_pos) + else: + plot._offsets3d = current_pos.T + return plot, + +def _mesh(mesher): + """Helper function to make a mesh""" + xlim = mesher.limits[0] + ylim = mesher.limits[1] + x = np.arange(xlim[0], xlim[1], mesher.delta) + y = np.arange(ylim[0], ylim[1], mesher.delta) + xx, yy = np.meshgrid(x, y) + xypairs = np.vstack([xx.reshape(-1), yy.reshape(-1)]).T + # Get z-value + z = mesher.func(xypairs) + zz = z.reshape(xx.shape) + return (xx, yy, zz) \ No newline at end of file From b8c86c2525167723768230a0d56876737e959ef5 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Wed, 13 Jun 2018 12:19:59 +0900 Subject: [PATCH 07/38] Add formatters module Reference: #130 The problem before is that we tend to have many parameters in our plotting functions. The formatters module changes that. We have three types of formatters: Designer, Animator, and Mesher. There are defaults present, but the user can change and pass them to the ploting functions whenever needed. Signed-off-by: Lester James V. Miranda --- pyswarms/utils/plotters/formatters.py | 65 +++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 pyswarms/utils/plotters/formatters.py diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py new file mode 100644 index 00000000..c6f6e10d --- /dev/null +++ b/pyswarms/utils/plotters/formatters.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +""" +Plot Formatters + +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.validators import instance_of + +@attrs +class Designer(object): + """Designer class for specifying a plot's formatting and design""" + # Overall plot design + figsize = attrib(type=tuple, validator=instance_of(tuple), default=(10,8)) + title_fontsize = attrib(validator=instance_of((str, int, float)), + default='large') + text_fontsize = attrib(validator=instance_of((str, int, float)), + default='medium') + label = attrib(validator=instance_of((str, list, tuple)), default='Cost') + limits = attrib(validator=instance_of((list, tuple)), + default=[(-1,1),(-1,1)]) + +@attrs +class Animator(object): + """Animator class for specifying animation behavior""" + interval = attrib(type=int, validator=instance_of(int), default=80) + repeat_delay = attrib(default=None) + repeat = attrib(type=bool, validator=instance_of(bool), default=True) + +@attrs +class Mesher(object): + """Mesher class for plotting contours of objective functions""" + func = attrib() + # For mesh creation + delta = attrib(type=float, default=0.001) + limits = attrib(validator=instance_of((list, tuple)), + default=[(-1,1),(-1,1)]) + levels = attrib(type=list, default=np.arange(-2.0,2.0,0.070)) + # Surface transparency + alpha = attrib(type=float, validator=instance_of(float), default=0.3) + + def compute_history_3d(self, pos_history): + """Computes a 3D position matrix + + The first two columns are the 2D position in the x and y axes + respectively, while the third column is the fitness on that given + position. + + Parameters + ---------- + pos_history : numpy.ndarray + Two-dimensional position matrix history of shape + :code:`(iterations, n_particles, 2)` + + Returns + ------- + numpy.ndarray + 3D position matrix of shape :code:`(iterations, n_particles, 3)` + """ + fitness = np.array(list(map(self.func, pos_history))) + return np.dstack((pos_history, fitness)) \ No newline at end of file From 435db1c38ea37781662e15860fcb5cf53a9d34bb Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Tue, 12 Jun 2018 21:59:33 +0900 Subject: [PATCH 08/38] Add tests for plotters module --- tests/utils/plotters/__init__.py | 0 tests/utils/plotters/conftest.py | 37 ++++++++++++++++++ tests/utils/plotters/test_plotters.py | 55 +++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 tests/utils/plotters/__init__.py create mode 100644 tests/utils/plotters/conftest.py create mode 100644 tests/utils/plotters/test_plotters.py diff --git a/tests/utils/plotters/__init__.py b/tests/utils/plotters/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/utils/plotters/conftest.py b/tests/utils/plotters/conftest.py new file mode 100644 index 00000000..d109b893 --- /dev/null +++ b/tests/utils/plotters/conftest.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Fixtures for tests""" + +# Import modules +import os +import pytest +import numpy as np +from mock import Mock +import matplotlib as mpl + +if os.environ.get('DISPLAY','') == '': + mpl.use('Agg') + +# Import from package +from pyswarms.single import GlobalBestPSO +from pyswarms.utils.functions.single_obj import sphere_func +from pyswarms.utils.plotters.formatters import Mesher + +@pytest.fixture +def trained_optimizer(): + """Returns a trained optimizer instance with 100 iterations""" + options = {'c1':0.5, 'c2':0.3, 'w':0.9} + optimizer = GlobalBestPSO(n_particles=10, dimensions=2, options=options) + optimizer.optimize(sphere_func, iters=100) + return optimizer + +@pytest.fixture +def pos_history(): + """Returns a list containing a swarms' position history""" + return np.random.uniform(size=(10, 5, 2)) + +@pytest.fixture +def mesher(): + """A Mesher instance with sphere function and delta=0.1""" + return Mesher(func=sphere_func, delta=0.1) \ No newline at end of file diff --git a/tests/utils/plotters/test_plotters.py b/tests/utils/plotters/test_plotters.py new file mode 100644 index 00000000..3db3cb84 --- /dev/null +++ b/tests/utils/plotters/test_plotters.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import modules +import os +import pytest +import matplotlib as mpl + +if os.environ.get('DISPLAY','') == '': + mpl.use('Agg') + +from matplotlib.axes._subplots import SubplotBase +from matplotlib.animation import FuncAnimation + +# Import from package +from pyswarms.utils.plotters import (plot_cost_history, + plot_contour, + plot_surface) + +from pyswarms.utils.plotters.plotters import (_mesh, _animate) +from pyswarms.utils.plotters.formatters import Mesher + +@pytest.mark.parametrize('history', ['cost_history', 'mean_neighbor_history', + 'mean_pbest_history']) +def test_plot_cost_history_return_type(trained_optimizer, history): + """Tests if plot_cost_history() returns a SubplotBase instance""" + opt_params = vars(trained_optimizer) + plot = plot_cost_history(opt_params[history]) + assert isinstance(plot, SubplotBase) + +@pytest.mark.parametrize('bad_values', [2, 43.14]) +def test_plot_cost_history_error(bad_values): + """Tests if plot_cost_history() raises an error given bad values""" + with pytest.raises(TypeError): + plot_cost_history(bad_values) + +def test_plot_contour_return_type(pos_history): + """Tests if the animation function returns the expected type""" + assert isinstance(plot_contour(pos_history), FuncAnimation) + +def test_plot_surface_return_type(pos_history): + """Tests if the animation function returns the expected type""" + assert isinstance(plot_surface(pos_history), FuncAnimation) + +def test_mesh_hidden_function_shape(mesher): + """Tests if the hidden _mesh() function returns the expected shape""" + xx, yy, zz = _mesh(mesher) + assert (xx.shape == yy.shape == zz.shape == (20,20)) + +def test_animate_hidden_function_type(pos_history): + """Tests if the hidden _animate() function returns the expected type""" + fig, ax = mpl.pyplot.subplots(1,1) + ax = mpl.pyplot.scatter(x=[], y=[]) + return_plot = _animate(i=1, data=pos_history, plot=ax) + assert isinstance(return_plot, tuple) \ No newline at end of file From 054570e9ef3ffb753824629123afa3f4d963d55e Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Wed, 13 Jun 2018 21:46:46 +0900 Subject: [PATCH 09/38] Fix bad behavior in discrete swarm generation Sometimes your swarm can generate a pure 0 or pure 1 vector, we should account that. Signed-off-by: Lester James V. Miranda --- tests/backend/test_generators.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/backend/test_generators.py b/tests/backend/test_generators.py index be43bfe6..0d110f40 100644 --- a/tests/backend/test_generators.py +++ b/tests/backend/test_generators.py @@ -23,6 +23,34 @@ def test_generate_swarm_return_values(bounds, init_pos): upper_bound = init_pos * np.array(max_bounds) assert (pos <= upper_bound).all() and (pos >= lower_bound).all() +def test_generate_swarm_out_of_bounds(): + """Tests if generate_swarm() raises ValueError when initialized with the wrong value""" + bounds = ([1,1,1], [5,5,5]) + init_pos = np.array([[-2,3,3], [6,8,1]]) + with pytest.raises(ValueError): + pos = P.generate_swarm(n_particles=2, dimensions=3, bounds=bounds, + init_pos=init_pos) + +@pytest.mark.parametrize('binary', [False, True]) +def test_generate_discrete_binary_swarm(binary): + """Tests if generate_discrete_swarm(binary=True) returns expected values""" + dims = 3 + pos = P.generate_discrete_swarm(n_particles=2, dimensions=dims, binary=binary) + if binary: + assert len(np.unique(pos)) <= 2 # Might generate pure 0 or 1 + else: + assert (np.max(pos, axis=1) == dims - 1).all() + +@pytest.mark.parametrize('init_pos', [None, np.array([[4,2,1], [1,4,6]])]) +def test_generate_discrete_swarm(init_pos): + """Tests if init_pos actually sets the position properly""" + dims = 3 + pos = P.generate_discrete_swarm(n_particles=2, dimensions=dims, init_pos=init_pos) + if init_pos is None: + assert (np.max(pos, axis=1) == dims - 1).all() + else: + assert np.equal(pos, init_pos).all() + @pytest.mark.parametrize('clamp', [None, (0,1), (2,5), (1,6)]) def test_generate_velocity_return_values(clamp): """Tests if generate_velocity() returns expected values""" From a56c50e3ebb9bc2c9338149343d678932b1d5127 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Wed, 13 Jun 2018 22:47:43 +0900 Subject: [PATCH 10/38] Add plotters to docs index (#130) This commit adds the plotters module into ReadTheDocs index and adds a deprecated information on plot_environment.py Signed-off-by: Lester James V. Miranda --- docs/api/_pyswarms.utils.rst | 8 +++++--- docs/api/pyswarms.utils.environments.rst | 17 ++++++++++++++++ docs/api/pyswarms.utils.plotters.rst | 20 +++++++++++++++++++ .../utils/environments/plot_environment.py | 4 ++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 docs/api/pyswarms.utils.environments.rst create mode 100644 docs/api/pyswarms.utils.plotters.rst diff --git a/docs/api/_pyswarms.utils.rst b/docs/api/_pyswarms.utils.rst index 0bd75a0c..a7e81225 100644 --- a/docs/api/_pyswarms.utils.rst +++ b/docs/api/_pyswarms.utils.rst @@ -1,11 +1,13 @@ Utilities ========= -This includes various utilities to help in optimization. In the future, -parameter search and plotting techniques will be incoroporated in this -module. +This includes various utilities to help in optimization. Some utilities +include benchmark objective functions, hyperparameter search, and plotting +functionalities. .. toctree:: pyswarms.utils.functions pyswarms.utils.search + pyswarms.utils.plotters + pyswarms.utils.environments diff --git a/docs/api/pyswarms.utils.environments.rst b/docs/api/pyswarms.utils.environments.rst new file mode 100644 index 00000000..907e20b4 --- /dev/null +++ b/docs/api/pyswarms.utils.environments.rst @@ -0,0 +1,17 @@ +pyswarms.utils.environments package +==================================== + +.. automodule:: pyswarms.utils.environments + +.. deprecated:: 0.2.1 + This module will be deprecated in the next release. Please use + :mod:`pyswarms.utils.plotters` instead. + +pyswarms.utils.environments.plot_environment module +---------------------------------------------------- + +.. automodule:: pyswarms.utils.environments.plot_environment + :members: + :undoc-members: + :show-inheritance: + :special-members: __init__ \ No newline at end of file diff --git a/docs/api/pyswarms.utils.plotters.rst b/docs/api/pyswarms.utils.plotters.rst new file mode 100644 index 00000000..a95795d3 --- /dev/null +++ b/docs/api/pyswarms.utils.plotters.rst @@ -0,0 +1,20 @@ +pyswarms.utils.plotters package +================================ + +.. automodule:: pyswarms.utils.plotters + +pyswarms.utils.plotters.plotters module +---------------------------------------- + +.. automodule:: pyswarms.utils.plotters.plotters + :members: + :undoc-members: + :show-inheritance: + +pyswarms.utils.plotters.formatters module +------------------------------------------ + +.. automodule:: pyswarms.utils.plotters.formatters + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/pyswarms/utils/environments/plot_environment.py b/pyswarms/utils/environments/plot_environment.py index 9606d91d..23024530 100644 --- a/pyswarms/utils/environments/plot_environment.py +++ b/pyswarms/utils/environments/plot_environment.py @@ -3,6 +3,10 @@ r""" Plot environment for Optimizer Analysis +.. deprecated:: 0.2.1 + This module will be deprecated in the next release. Please use + :mod:`pyswarms.utils.plotters` instead. + The class PlotEnvironment is built on top of :code:`matplotlib` in order to render quick and easy plots for your optimizer. It can plot the best cost for each iteration, and show animations of the particles in 2-D and From d007009bf1784ad01a030f43f35ab1e2f2c3f034 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Thu, 14 Jun 2018 13:05:33 +0900 Subject: [PATCH 11/38] Update docstrings in plotters.py (#130) This commit updates the docstrings in formatters.py and adds a legend attribute in plotters.py Signed-off-by: Lester James V. Miranda --- pyswarms/utils/plotters/formatters.py | 95 +++++++++++++++++++++++++-- pyswarms/utils/plotters/plotters.py | 8 +-- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index c6f6e10d..fbffc31a 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -13,27 +13,114 @@ @attrs class Designer(object): - """Designer class for specifying a plot's formatting and design""" + """Designer class for specifying a plot's formatting and design + + You can use this class for specifying design-related customizations to + your plot. This can be passed in various functions found in the + :mod:`pyswarms.utils.plotters` module. + + .. code-block :: python + + from pyswarms.utils.plotters import plot_cost_history + from pyswarms.utils.plotters.formatters import Designer + + # Set title_fontsize into 20 + my_designer = Designer(title_fontsize=20) + + # Assuming we already had an optimizer ready + plot_cost_history(cost_history, designer=my_designer) + + Attributes + ---------- + figsize : tuple (default is :code:`(10,8)`) + Overall figure size. + title_fontsize : str, int, or float (default is :code:`large`) + Size of the plot's title. + text_fontsize : str, int, or float (default is :code:`medium`) + Size of the plot's labels and legend. + legend : str (default is :code:`Cost`) + Label to show in the legend. For cost histories, it states + the label of the line plot. + label : str, list, or tuple (default is :code:`['x-axis', 'y-axis']`) + Label to show in the x, y, or z-axis. For a 3D plot, please pass + an iterable with three elements. + """ # Overall plot design figsize = attrib(type=tuple, validator=instance_of(tuple), default=(10,8)) title_fontsize = attrib(validator=instance_of((str, int, float)), default='large') text_fontsize = attrib(validator=instance_of((str, int, float)), default='medium') - label = attrib(validator=instance_of((str, list, tuple)), default='Cost') + legend = attrib(validator=instance_of(str), default='Cost') + label = attrib(validator=instance_of((str, list, tuple)), default=['x-axis', 'y-axis']) limits = attrib(validator=instance_of((list, tuple)), default=[(-1,1),(-1,1)]) @attrs class Animator(object): - """Animator class for specifying animation behavior""" + """Animator class for specifying animation behavior + + You can use this class to modify options on how the animation will be run + in the :func:`pyswarms.utils.plotters.plot_contour` and + :func:`pyswarms.utils.plotters.plot_surface` methods. + + .. code-block :: python + + from pyswarms.utils.plotters import plot_contour + from pyswarms.utils.plotters.formatters import Animator + + # Do not repeat animation + my_animator = Animator(repeat=False) + + # Assuming we already had an optimizer ready + plot_contour(pos_history, animator=my_animator) + + Attributes + ---------- + interval : int (default is :code:`80`) + Sets the interval or speed into which the animation is played. + repeat_delay : int, float (default is :code:`None`) + Sets the delay before repeating the animation again. + repeat : bool (default is :code:`True`) + Pass :code:`False` if you don't want to repeat the animation. + """ interval = attrib(type=int, validator=instance_of(int), default=80) repeat_delay = attrib(default=None) repeat = attrib(type=bool, validator=instance_of(bool), default=True) @attrs class Mesher(object): - """Mesher class for plotting contours of objective functions""" + """Mesher class for plotting contours of objective functions + + This class enables drawing a surface plot of a given objective function. + You can customize how this plot is drawn with this class. Pass an instance + of this class to enable meshing. + + .. code-block :: python + + from pyswarms.utils.plotters import plot_surface + from pyswarms.utils.plotters.formatters import Mesher + from pyswarms.utils.functions import single_obj as fx + + # Use sphere function + my_mesher = Mesher(func=fx.sphere_func) + + # Assuming we already had an optimizer ready + plot_surface(pos_history, mesher=my_mesher) + + Attributes + ---------- + func : callable + Objective function to plot a surface of. + delta : float (default is :code:`0.001`) + Number of steps when generating the surface plot + limits : list, tuple (default is :code:`[(-1,1), (-1,1)]`) + The range, in each axis, where the mesh will be drawn. + levels : list (default is :code:`np.arange(-2.0, 2.0, 0.070)`) + Levels on which the contours are shown. + alpha : float (default is :code:`0.3`) + Transparency of the surface plot + """ func = attrib() # For mesh creation delta = attrib(type=float, default=0.001) diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py index 184c085b..25db5506 100644 --- a/pyswarms/utils/plotters/plotters.py +++ b/pyswarms/utils/plotters/plotters.py @@ -113,20 +113,20 @@ def plot_cost_history(cost_history, ax=None, title='Cost History', # If no Designer class supplied, use defaults if designer is None: - designer = Designer() + designer = Designer(legend='Cost', label=['Iterations', 'Cost']) # If no ax supplied, create new instance if ax is None: _, ax = plt.subplots(1,1, figsize=designer.figsize) # Plot with iters in x-axis and the cost in y-axis - ax.plot(np.arange(iters), cost_history, 'k', lw=2, label=designer.label) + ax.plot(np.arange(iters), cost_history, 'k', lw=2, label=designer.legend) # Customize plot depending on parameters ax.set_title(title, fontsize=designer.title_fontsize) ax.legend(fontsize=designer.text_fontsize) - ax.set_xlabel('Iterations', fontsize=designer.text_fontsize) - ax.set_ylabel('Cost', fontsize=designer.text_fontsize) + ax.set_xlabel(designer.label[0], fontsize=designer.text_fontsize) + ax.set_ylabel(designer.label[1], fontsize=designer.text_fontsize) ax.tick_params(labelsize=designer.text_fontsize) except TypeError: raise From 9a4aa45701f516ed238b02a1e28cea36033ec7ff Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Thu, 14 Jun 2018 13:06:08 +0900 Subject: [PATCH 12/38] Add demo and documentation for plotters --- docs/examples/output_8_0.png | Bin 0 -> 12558 bytes docs/examples/output_9_0.png | Bin 15164 -> 0 bytes docs/examples/visualization.rst | 155 +- examples/visualization.ipynb | 9184 +++++++++++++++++++------------ 4 files changed, 5628 insertions(+), 3711 deletions(-) create mode 100644 docs/examples/output_8_0.png delete mode 100644 docs/examples/output_9_0.png diff --git a/docs/examples/output_8_0.png b/docs/examples/output_8_0.png new file mode 100644 index 0000000000000000000000000000000000000000..8f902cd3cba0b00d94653f13abf19a1fde7e891f GIT binary patch literal 12558 zcmd6O2~<;8w|_zqoPb)32q@GVq*g%`nWxqXBLXU1ky&L(nTG&jCe&7HWvCYg8Kc#T zUJz6S0zpWtf(RiBB0~~L1wp0|h$KK5-np^1_Wk?b_kHhuZ>_&;-Nntl=bp3A9)D-= z?YSd%)(R`tR$?$11;W97$1oTfG6sVcESCdM9%U1#;Eyb655aLc_!qz2`+M+vMd(3i z5(cyCWAwtdaTDjkLz9U8Cn6j|d?U!G!hJA7ry@cFLm~qGPJbHZ6Hf9A3I24K?oM63 z?Vo-f5fN&-W5=HZbVI_4JAMx8yM)1fiXrU#$}u{BoSSsfJ2GKtX(%8zcD>`i4RV{U zT2jAJn>RRz^IWU!J}E!PH2v|h-o6Wg2M>z2^?i%ql1!^9)!uot`*RaD(vcI-Z}MK1 z?Y#H#nyLNn*H+6WVZO5dR*#}LW%JRGg*jqzMorrOqYzg=!ghK*(Xw?|AFARnye&Fb z^rW42n|%q{1LFy+9>QVvUxFJ_BP|n8aQQOTi~wI>_UP|_s9nYHI2_KT4Q=w9)Z^oI zmJ<>;H#OkJ_-d1;CA+C_tc?dF2_B{clPcQX9HPWZdp3xI%!NZ1LF?*AZxa@OyU^kU zGiaIjG5FcdJPQUpSsP<7v#5B&s5NH%5Fsl)P7xlI@3%Wvtku0XT3v7!nRA2|t|K>) z5fU`5&9tOz#n{7nxl}Q}`*uv=1b*rAKr6mVdo~=_B3SOx{@ALsL~e1|u61HlaU5ZB zds7;LZx?P0Kg`^Koi_|8fkwtw0^j!DPXW}S zE#0tpIB`(-R>8byEZoD-PJYcON*NPw;l(i2u25JqOUlvuf<}h#NuTlwAq7GD;o6U8 z#*vy(-7UETehdO*6BOwHLyUb;f>jec8CN_K>7Ek5f%b8iO0=oqF~0M7M|N&wC$AVI9S*KIno0A`c>~-x05-ny^)9b)$>ZOhXD8n@34H z6dnXSL!H_SG=FWKLk$Jjj~63! zN)=|pCfpmg*2+0N^%Or57$q?lcxILyzDF)!vI08RftTO1Lf~auWLIU-$dxgVDIEkN zB6zg9wS-xPda@>5E_oA#wZWK0C4mzmOFPCeR7UVzHIbQ;+$ZZ{E8L)^qYKu0S&Yaa$D<8>=GUpu$NOOk;mZa4d6dHQJMlUn7iL|$zU;H6?g+b` z)W9yn`Rr|E)S$or{ZE)S0lrV!J~ENTjhlE7(1Aexg=g`Ewp3mE*gZ|C<7tk_^+i~f zf5#Vw!5m}0-$5A9{Jd#ef8w>DGwK2!f9L|duT!gS!ev8E^|0}G5WCTscq3EK@C7%X zA@4F$quaESXU_YGw-<^$R=%Xc$flp|(ws>_Oih|XWF};S12y8xJRfW(wEeovz~J#9 zGp~ASQPOB}>4I}zyoqF@-YPRQ(=2lS$t&F^S%D?bhvCa>!>c1BNGaD2!@r5(GE3xp zu^Vt4rCQZ!Um@;Gp-N|G#6o&A#Xd5P%#T=p2)_OuQ8>sPyW6Ny;!^PgzQ3?Fzxaz_ z`naKCNN>wXM={wy^=M(1#V1{k5U@WS!-P>&nD7!yG^%3Z6zySTjsPWmXE!i?Y2ee7 zni@4QVBWerfHbP|w&UBM+`%3E^#|o`8qc2&eBK{y#XDOn$;%5$5Z^*>Gu#ex?DM8`1j8-eG@s@y=fy7w@NUoTFYe@t4pRF*?=S6ZiLHMU zWhhyFPh%#!bAg2|Mn<|L>Wn30iSkf~DpXO4yexGy>F>i{#1mpKYc2{>N<8Zei_Bkd z-4HMMrFH}=dqwYC;47w0imK`p@FaB~PLN!U6H6o?tajfX{+Ut87}rcNs`bfnyekYLPeDB0U z?-};X66c4jJOna}1NMvG5-o)}=A+K6R~!;+8E-HzWbn>IMK5Nqfw?e;HPGz2xT`2X ze5II6)(m)A@>@k&!gzjbh__So@s5{xZLG?(S%=SwP{$=guUudWqQJ;uSi^xom7lIk zoSVUSVl$RyX_cl`?rZBO%qP_LXw&ux-d`c4ZI;SWd0r zyBz}$CK7fyxrIH>XC+-v8Y)FvGt%`{3;{MU#P#5=W~N&)>SWHQjvbAmEg~@MH$1@5m+s z>~ae^Fr}YKH1Ah+i|Y1KVq3oHdZ=iYYNK2I?ShD%(VuRXu9P(MFk_}#$%{jP@#B>2 z+5xTkw?$U3ahfxXGi-DTQBS~*RtvFPWe~d|OUIQq0NWV9eYUrHac;DmNKKe2=$qYW zXZ)M2ZvA{XW{b9VPHt|la5nAj{SqL^J(=IyC(X6z$4@ooNA>t^_pHl@ijPkgifdEi z_|+*x=0hg<-wGz;wXMV?t41&%lioY63ucLF@zN0To^sD=7d|PcE(9hHXD6P^0Nf=R z)pxt+*XZ4Lr&qrBG}oPL=0=KVzw?~=F3jF*V9tVt&xSgBa?>2Y*|)FdiaYm`F-$ zZL|P%ca!@PhT`1_@QcI-?(%||!JPcWxnYtezn!`mQCiny)eme{qm`z9m1r^QT;f_4 z+}gmdiQGnAnjfm3Y)~HxJPaJ+o%Q6+5x=g`4h2_BceJ=SZU;(ScTMDIe%e;TF}7pJ zlSRs8yx?G2iopKr{U>cd7pybl;|b?Grd~H!k5{ra!Z1uH=y6aLw*fpM$F6qbyPz;h zCkpjoc}5|=%~?AAg-@TLJX;X=B^6~D@m;)|U@iPrq^mB$%qEK__=4c#2AJjiAabcME!(ubSvvtiZF&vv z&Fg`y_gmjz>Vv|y-Kk-nyV}Ey3=9kga}L7SXP5 zDEtt$k!%wlES%}gEb*@_I~X1Y$HBE#Lzk0+8)|==?Iw1eSd}J~fvPh{D+kjnU;DA~ zdSk<;Uo7Ya7|u~hUS>kB_jzEwCcuFW*oXDNSMRo}YSnxB&1orN2dcx#pWnHKl-@?< zDI`jyg`@=68XQcO-%yB$rZbVbl?6}oZr6E@&%YhgmP%d!DUGc}yE?i!=|3!`@-{XIvRD;; zN=V|Qr$P|Xv7-lZp@+YIs+!m_|4w@jsFL%NaUe!1adv-A#oHuXB(qZ#Tp%OErCx>j z)Yjp?0=42asXDcM-p@bAyMiyy+>U#RQ;!vOV>a}0EY)Z?5xG*1mUFK6#GnW-C}xnO zd`l{KNlCG>HVB1+Y{Nee#p+@6A;@V+!oVR8PeA(8fQ0}s{^a-1D2aP z@Jk0hh+UZ$V+ic64jA)azjzeWE7o*+yE`;?mPUskmJT_Ul@fFDw${~O9Sr_u4&`06c9H}FMoW8$mu zLxvei7EOq+99@*;2(z?jtME=+Om2_f?K^x=t4W#33dxwNPRo?;{GTyp z%e_mHkj2KC{k&O%NB4ABHB@n9c9yrnvLHqZhR>vizvdq~=>WOALQ0q+%xJ90Sw5f3 zu~afPCm zy;Ts#3n?Dy@f8kHAZR*}w}K}O z1g-*669xkB9uFMB5jaJ*5Zv$XFa6ovU>gCR0~Y)g<;()GV1tch2L}hQ!gF{+u022p z;jhu?4*bX8DBO_E<0*j6$XybV1>=+ z=^*(bp*YFN;~KY^(1=xQDQ3vXmpm@REO%Ut3z6&XBEAvhv}NQCtgLsl?Xh)^kUZAY zpTFaTbnC>6+sG+0dB{i)5{ogGKhasvGUbg8ieebQ5spDtw~(@D=W6A(Vm=deA#*N} zvfvc&iKSNow<6)IcU%Qwdx4@}!w(6roaPoGl`>@PHsAFmA!R4^2xMgB#a%sC-mV|= z4y*xa@Vnu|3!0W+hM$Jh2!_Gk_k64s3!J1fVSyv5e(0B-3k%74a^soaTkmLxN>+RE ztxmSM=C)V17lnk38+{2C9EAdk{l}kP+D4pp9MqKR0GOQqiSg%6Blu>@G~}hcrQ??9vshz=nSJnDN|L;X zJ~CveiDKDX)C-R{0veU_`^v|4+^Z&tUw#X9AXv7M3~Q`>!H_4g*5e*WBl$6_#J&6f zXIlCHm%Zdb9Ie?Wft7xp@3vL$ULAKpTmW>FG?ZwXtMg7kyZLZ_?m#fFn~=zv=O}Pi z#z$qh97&K`CG~2U6O;r&ahM4NX*9HobL{v5F^yDV9!+@1)9%8og+_da+`*|Dd{XAX zkhSp+=E>8iW!^k;hki#SmxDMDG-*anB#PCtQh7`Bd(M6bY_hhWYJyvvs{nA>$vOb? zLMr8H^xGZI8ZPqr9#9c3E;ZOCSHYeM(k>q04Lvh|S1C1@fUh}PCLcxbfffj`C(Z*SJEK;xYWwa3 z?*M8tz<0A6rwG%v=S*L?_?yNTU zzqq!&Jj6;*qRj1t`)63Mxj%8S>FH zew^WjKT(+3^rOL5Qjw^=hg9Wll^R{o%2{%}nL*KW#(oIIu4!h+{o>>^l#qyx;FTaXyPDXPG^he=ymsawY#VWW?4?no zN6x@n2E;hdQ-CTIK5e@8#w}Swx2T^gAiR8Lk`U)kZB*Itju>YS*0E!jF>Ar@ZHts7 z@(gh43pTtRlEC`x*8M6E*coxxg+c1lu~X__xU@*Q0FDa(wZ8o;L-Nw;w{G3q$=Zi{ zmIn9p)#0d00)+C;n81_w=Yly^pxe@O!GnL>>i?@f{AaUz`5@d0@+wRNo=XW(=z7n4 z1pxz+J(A1B%^;V7>o0wcq8KYO*r6$aRl=WE0u;rh1(=}BjRa@5R+P6~@JdRT5%@rA z%m3FysxCCR!<%8ZOm7F+Va~kH6(;31))#t1onA$T>@vdC52fRsTC-8`HaKvsXK@Da zd3fr6!?e8ITs7fg6s79jquK1j)+$A7h3bU{!e`>5%&@d;h@xz-LhUsZ z(aDn*g4Mj`youb&8w)9jivtuuuPl&>zlA8u)V<(%3Z&BI{&y)8rig@$6hYItlMed5 zdR(spg$*Y1tW&*GhE@IDu}Xh~E5k~wdVAA3cZvB`BsjRCMzxEWUf5V`XkxNL8lJp{ zmp9r`gAAGz&Tf>ZHr+-xkX^F6jev60|({Hqm)Xd z$0+TlV<+u-qEn5>JKok;$xsK0MFAm%sYkvg+A>w5JjV~f7J04-C^?F}4gUmNZ`?k; zdzDC4U`#T{uO**HklgXnykb8moIJMg;M5y@90nh=TM*7OOK33Fm>K)Nu(MX09|_)U zrsMkGpRnpoL{ITlOsPzzOvp_nh){@$uOTs)*(7H`9V)q=F=~Qycx}v*Vy&+uE67p` zJ*ti-4yGffn#T1+wrfprAB(aa6ue{??-AJ*43gYfl6vUO=KSzcDdY0`jyw;S?#Gu? zk&gz08eq9ZGz$Xe95?29Y4P4M}6${~&dGaeELLd zf|%Pl>bW#$vQa!9l4Mp?7(0@mzLfwUXr{Sh?5q|d3V3SpK1nZ{ z5r?A``h&62+)-b#}G&#>h{s=ALH^N|WjV(O-S1;XCm_EXS*42dhgR zLi#TLTfJ?^OE94Tv|R_urMG%$oPOcaP@v&k_CO@V6R|^xptd6QnmudK$6!BW6`dZA z%H-adAzBgeoKcjpqPiPa#olQt#X2=biM(_`fYQW)4W*)=5v^uXy`ql`=_gP_}kAj<&r?h29WPh8Cu8 z7Pg;$g{s~7?x&aNdy{RUNRWZWD0_=X29cT# zR}j8jshmjtH`0mws8-)90N_G`HHzT&wLIKA_RIU^uVJHMQE1Ydnb^+3OgMg9lQTgS zJ79lm67@#cqX3%tnN^wl+H!A(?bLc+CZh?O?)Mc+l8Z0_l)U7n=j3C5Rv&+3om?aI zNBQV?F|10}_{!+wBE$BKh;Z+duC=s;uDgUS!{2hPi$@&V%^W*)po-f_B~J4S$z|pg zGLnk8xIzI=rEas!3wwVxOmKOL<`{c?E{2h}h8}@C2wBgozdZe6iU=}bcN#ghm&ySL z|2$q};xKOnnakrR!|y@OysFM;QkAgVOn0e7Dz})dpkXd!S`lPhX01Zk2}u3Tw>`ht z_}+W7pGe=0sU!ZbunMLT+h-TPUs$|Y3#xFvIo=8d?Ny!FdNX9Gay`&TAd-u%d%9oU zj~egkp@sE>GRqEbypTCYpQsxvXIXN>n?aQ(9Tavv;AX^s{IFzJL3yNDa)0dfq9z8$ zSEK>W1F5$LjkvL2KnaaDT1sz_nxRcYf!q`tzk3>K?O!gMeDeF9bT5|ZOV-tc@C)gk z#7B$Z56;-Q7?N5A+0_2Mt>{1 z;-FQ(oqn#R&M0a=LTXr%DvJM(#jPjjUXZSu0v^Rk1qt?jU~wzTXY_k1CDJKZeE~M5 z1g4q#1{DYTQp6$IgIX(1!l%dZlI7pg^evc4Ym$U+1>8EL!OkCa7nQ%-uM8fY0goo7 z^8AI?UP42OFaty=OIaozCN=Oo5uCM0%np}v65j09eD&+99UFv!gGHdKk5-KZKdC(U z9xc%VH%sO?z<8ZK(sVVZ2)C9YUp#WZu#t?jTy3sGQ!u`Plm=*+DH~(E860@Vte9iNr45F)97MNSnfnJw+eBk!P-B_Nai|+vHR~ z2kBN1d|WqmsVtObAs!Q~2T|%bP{{9pb1h@==C$5{cbU zT5<#(V8x{gx?hWCT|i%gQt~CnR<6WuqH}}j&d%g^iVlMXb-e8Dv-m!&=qRYiZUc4p zW$H%3`5jJ`76Jy&E#mopx9H!#0}&5f5F+PX9j-+f4DuHbf zX)kPWD4hBvYlo*LvEEA;RD14$YFWy>S@sh787KGh<&&TmTX&%Cd!`}(K#WV&yEcyG zrGRIawimg}O1`dKvSHno7*t8ps%2>9NTsOTv*&e|uKUxTiU4PNRD+mzrgx^nD14}Z zJXOs~NViX%%8dVQl_&3|Ye<2xI;gic2UP04TN^KBZFipHWn3+KJg8h2y^OjrMr^cD zIC(#;bE1GtKWzLp^((f-oog1jlw$vd$r|G|Jc51fv3mFGcVjsH8jV2s(hCjU?qse( zQGXw(fd}^7KNnD1eRd*IOm`zO4d!|BRwqYa-qzQwJ@2Rt62X8$lMI}a`Apeh5rTFv zI4ZNNLWmO4cz+?hdtE!xeeeuG%YUHQE+UEK5(QwaYWjH9f;FQb-X3&(Vs1efWZYNt zR=>&5;!YN6jjI92X$CGyeg-{u+TTNTFPk|_bK!vM4p>93aNW;ZHiInBY|w!MiUu#- z9>;FJqcM{v?HR#&;?m`G5#-Q4?92(c{wnFK$6qmw-jqDE2y7X=d_Heyt6xLDX23Q0 z{&M`O)2F{VckW!6{eG9X2iB39#5Z;_x>tlmn-2m5ML#(M-@T9cI5>ecc8S?h=7e=k zHgfDRq$GM7)=#l0K$VvX%9vfC2jl$HC#6|IxU#HEctX%lcCpV8YsG2MGhuNWMXzl# zD4@pceERSGaSI7!oeG2lv89@mW!>sC&%aMfiApbH&JT?4N}BCyv9Hq=cDmIYT6sd- zh&_Hs8@Z)FdV4{)H;T>QhSnJhGs~LiUkB)0&DOMd_QwXHz8W-fp}-q7c7++m5>w-3 z)Sh(uCQe1>)W-)6YI|nS25c`&qoM_KmoHHliS}^P*Bp9Z?S4udDnFrR@@dm!LaAN2 z4{Y`I2U+njR^;77yDkiPX3l$&k|NJQ*lRIGM&Yr8YZ=0;LT?;_Ujx>Ld2JZLx4{PTA1sy+CD9`jG zbIU*jrFw4Xt50|~P~=B~p9kw2`jsffbbdW9nk?+$VDt5|oOObWaGp1jA{^_gaBikh zwHz%}Y00kL9g_;1%gx|J!=Z&}j&Kcr7UI1`YD(fr zRQH65F&CR;Pnlj%sDAmpto<*otC&MoFR{hR3CY&&Cfv?uam##p^CH_9aOl0^r}^mu z>_ad;C4LUtYs+V|hP5UK`&=6h>i3V$G5~V69=f(_$JKIJOdGtocBWS~g~=F1=3+@p z=B9Ut;(X^lR?c5RDjhpM5p3l-2`u!ft7EBS_jL962Zlh~z~IUoJ`t9Jno}(#my5Zq z(1I0i?od+u^fPJ3Ymz}EPjlAM5z`7pRE9tiy_-Q*F(CPT^0ho}d{7kVcia@Z7cMf$ zQHR*X>7Nr^!14I*^Io#otC%@a*m$K2 z(v;88>Q$EH`ngGQTXl`dgJortmiLdG#2}V?nRmTB*rFpuaHuZW>t=`Q(0P1)0{a8dtMV>+7ItW3!X~K zyrtWS0h2X?$2*Rj7Jo;TO_!~^ zvzN86?I+L+;WEaT6TPWP!U{a$!!r07f7n`V5^T>)M{10k+LMPOEes73;xlY0Ub(F4 z+p>zdx}9I2CTQTsoS`9{cDu>X+X#DSbIb_mxmA?g%;63tY*D+3R%{IE=)-lU#l8>f zF!LU82)6T?iGkVpYMv`Ww+!iw3R>DNXl58C%PihU61yIqvWmk{ZSUo`B7K^Cj>E4~53=Ial@V{@wR;|DWgI>*Wptd^lDb!m)Rs+IXn_yAx?fIpxB=gEK}m-B)W~ zn$NlsPV{q_W>@H{XJl6dD9>%~Iv!OT8dBPv^kBAWg3@>~sQAc{=NFG17;I~_=DGec zL1x0GmS%8KVM1=go*Whu5^~yiSBbE)nwr{lu!@_?}qFh4I0-xF37WoQkC8Iy|A@v& zl8BjcCd^K+aO3eN@#kk4Me%c(=)LFW_`aV{4p8STN>x6wU?ed!z>vVWM0e1GYem9| zcfqm#Y>I;|={Xgaat9;HSTRUa$`4m*&F6O#7yUqziZ?O-XP9ge-}kn@gzRVd;#D>M z2HPa+Ad8PB#)7cknA+QjilH(_3)BnLhX>lI7KNkD-6+r2vMr97XJ-j_dX!VSS9sr) zm@(b8@t*Xma@~DcX%{Vz@~(EC$vx8}A`AUmKidy=wXi4!O#(&o@Wjoj-%x4P4Kan+gb^`*z;tx&@Daf5JlxIsx#tFpa#i+l=MD@qDm|B@lF zch}FJ##HmF4~GQ0)HwyPZM0ixkA9-?X}qiWg;RK!*4aAMJT0`J1ID5Cq+r>W0ZN?9 z0n-?Vqw1s4iHHdvq$sr4)Y?hllQ8aQZJ(MS(sin@t%@^InXHgRB~#Z);0?rQ7&gk% z$ad8`$rqC@M zda6;}yZxBZ##Hn3>Rn#ipRs*)y{b6^_45JT^+vCRg_%ro% z$`feXzK5D2+I5dK(`_&!d#WQBis)SA=X-b{*$ z8aE@jKXE(2$H&KnX?k{4ZlGpvq{rSlb+Fvtm_x+;;LIs-ea^?Y?#V$$+09?Wz?%8^ z?x>{x`Xg>Y!i_cJ@^JxyYhT|#*~`K4zH0A7>zte%*Xgc@85tQNgGIXgSaSlOa7jJG z;m-9In(fl|;lETjkeDckExq&gY?a##M~9^AnweS#EjM-%Wk(XadsV*sxp&xI< z(k8!n(cq;}a27oJCSPY~=jAF_f$fz>rIk5H{}u(x09%Lq-s$fbUs-wKPh}uy{%HQtBJQoRh3`9?WZxiy%j+HcME$pX zUOEnG2Qzc8%vfyGE4#Z(3SB81_TQTDkCnp;o0yoaJJeNF#I}gkCG?tmKRI)cHsbkn z)hkyxb>zR(VBW*fm&@(g4{L;7Z;g|<7=M_zU%3|U5S)js_-1aeWoJ@gT?!N86 zJl$R9IFtlarE0JCblJnkW>VpF$k@z{9o5L|FK<@mdJX2v$SoTg%E0HL?M=*Fx}lDtx$m+ekKf8QGE6jRusr0n9+@Xf z!vPtFTh6`Mcinv;(VJWu{Nh4Psq56Goch+!^$oc+hrGbGG3}oR@~B>Wc|3)fQ$e3>y*;g$8SVy*D{J#41`6 z5)!;fg&~@!3$B!hmX@CCFT43xW+LJ3Skv>SmKL-n_`$6&FQ2r=OFd6Y`eR?9Dcs@mrl{R2JZ2JP0wM^fIu`|5BcltHN?P!dAvfvb=C&* zLo{phDl(a0<9CSEuJ8pa2Sgm!D0Ln`AH>W}uYC3_t$vpFjk*5vfjf=dM~)nM8X4Ju zZ=)@8){Yeo)Js3xk*XTJkRlZ@Hg?_IdrsnZ@uetxGo4McWjc3oE+4dH4*R6FI*^;UxvXZ<`Ag(yz(spCC;UUwI zu7+sdoIvswhk<-S(Y(+==QgUv0pBZ=t9HY#*r7`#Njm0(lgl#6Ya6SJGIM30_AB~G zC;R+3SL0x;Hrmo0CAd1m;=h09)Ywj^>>&y9tD|Hz3uW+fl3(&JQ-?GB=)PcvC0osl zj7j;rIzWEns%-sUIB+`2KD~kA3S197g2E=#5Ra|6y)37x>vVT0v&#uXc+seOmkNng zBu$)Q-r&2kUX+ucf3mP*ls-)1=jY2YfS#B5wX9N1PrJ9Ca~uRC|dhI|&8cJ11=G}&>uXB@s)n+=)Iar|5y_%&Q9NiRK@kX{g?`Bl{VJtOh4 zVV=>us>s+_DMy}kjc`TVRR)pj`c|nS(}}sdg*Obh2CFMSe#vQu4L?}!a8Jc7eF$O( zPSSx>X{{qXR?^{0{skFL`3{jN7Eh&TYY$X=pgxc>z;fQRAf{X z7zXx!7S@+sIT2vl=J~n!nyJ(49ZS>QGfzL&Q2Q)(Xe4-yK13jMrjQVpmd1ywa-VAk z5N->gLIW7+tS~kJw!LppRh9YPz$P;usc(0BswaqvOL}wZ(^N+~-S%qo_5pK6x9P5h zy(LQPeQn1D1=I8MAKMpJ1_6%45WGCzvtpS#$sb4GJ*xcCEI0)*WXFyj>kj^4x(RMQ zCQCED;Z$hah2>t0uVloQN9GI;&VyGWMgusVg`FQfdAT@%FkdU@V_Xkd_@lUyEy7mo z;_iaRbNq>8%Qt6w3Yb^)G`J0mzOYBElv=$D-AqCoDkcGbEO@mkP)y5bdfs>C=x^JP9!bT@%prrNDK@Nn;Flwf4I%`tPWJVC@3h9 zdJC>jkjbzMl$oMB#p)Ub@1GuL&IDqoyPAxP%;(5th~0~gJl;eSiQqals-w7?K_Do> z-fn$L-s&Qe@OXSpMn(gPq_i@Ou3H&}Is1|aD*d)rCwL7gkplYFKqG3c8=2eM+9+(= z(y#XkT^SQJ%3tV6*YLh`r-VS*1}viNv+(KICt^Npljvr&vPru1oo5b-KFJ9ET%^1xORN@^6tNVC;m-PH6Q-cRcHDzU8 zF3!&IiwPdn{jzTTb)7w#TyIr$=4@_OVNzAdX;y$v>)FpG%EegoJM>!!YQUuTva^3D z6Xs9(tT-Xepu*{z$0lMTuN;Rs=Mba5g(W+vEGQ&jzN)1yiVi{X`-eERFR-^vP@ODQA}Ehmyk;*N)4xw!(8N z#-p!#%-2wC2dh2HzkF61=)1^N5e#>Fo~TR&5lx4(z`FKn&Pi{I=#g2aGhI!QjCeY?6AM&9C-{1yE- zXDlke#o4cni}*LeL6|vyDGK$Pp3Zrs$tLQ%=KB5#SL3T8zm~P-nZDv{Xi}A%_3OJk zP^NyX;}>zK9_(P?xL?=mGM$t4V{N7gU2OG^9yZ1(-M8lZd#=csm_=}X{B4pf5-O1h zjBiuqBBKgS@IerCc$NlLwJ5+Yb$W^{+WO1wsXSK4W00VdTX0BDPEKCpCxEVQdKKa# z?sNU>XUee0<#0x#_6L{q}GUB|t;IwI#O=zrME$cgw6zrdN&xaaC-s&Ak1h zbN+m&9sduA4q2I*r?6yGh?-Vrp&^=4nOgDh&9J2Mq0$S&mG^;Ki-FOf4LjIWap&uP zaSUTqQxj^y^^?|PUZo`S8m~JW-y2=t?BZ`vQ)fUG)@`jV1XQ58U5H|gJo2##2?vj9 z#i`lZo#?gjSAfH_0rp)X=KyP>GX;AQlC!Pq1xv}ihS^t@k??>mKLfv3ag=kcW>Ujxv@F#&o3k-RJ|Ev;fG7!oVD3#^xvR6ckWy@93tWr z$Q4-@b$+o2wl{~iwFvHW9^c>X!%5%agNv?TzrN6UL4@kHxRmeu&l|9W5t->3eS<9X zCqZjTUWRHJ$Kpo2@^0L?5g8fzG%l_g7TXFi^bRQO+f@JKNPvR~`whl$s6YRrwCZAw zR|5rdlu<)JKgGjmAE#tw&|oLP^m8QJUrgy|qY)0PuC6W*M>b+V%g3i>hQWNhRu|)! z5@%i){eCKh_c+}G7zxBR9bHq?gEN;P;o{5G)i^w?u69K6co%3PA(}qA+3AqXM69If zf~>-)rD2`mwH&FhEi)?%$a|wPjGIo#e>jlN17hjnK?*p)6p4Usn0K6nL+ydKVWL!PMpkbM*O#%5Lia-iaBhA)l{- z%JmRS8L-&8+0vGc@fZsThl;*yE7hKh1EftL?Te6f%@wx@hD{BDEpOh3$eQhoX-k?@ z4Q5^jOsXQRe@v9iBVl0tn&RSinA5!1_9kV$;UQbzhFp7B9v`1ZKv105@*4(`BZm$J zf|GxT6GS697m}Pk z2|WQCd-n1E*KpiM4TuYT;!bD9*;&(rtJ?M2Tp(NK?k znt$@zVtdUP!ArUM>bL%zz5d-Enbn$LnF(Nd8ChA2z_%Kz_OeE(+I`3|W*LVdcJ0Sl z6Odl2VnE3S*GzV8g=K@SA2D^h=H?&aB-`Lj?8@dKxWvDCqvB2;Xo3&+pSIP7QP@qIY-ieX2qWD9^f@X7AHeZWdE2#{?WW+C1HsT(A&PD8#FB=b6vb-JL{c-7qJyZY_w?`MB3 zE>1xPsGoT;$9)xeTLrmlMpPW0;Q7HJj8h^MvfNWn!^+Q|KbL_c@K_lMW~poirde1u zeWDbPzk|IExNzm=t){w-mO7`Y7coB|94!KTiQW2K%*p#{#CFy3 zt-}j?9Uf`=NMFh{FUYH|mOpjs)aObU{(E#R-yHqdxe&_Hzcm^G-=>GoNNLTX!$LIO zti+b5Cp*&nK+Jjf?%e>$CG<SaC%pFowPTkK9~q-RjiJx zsoD$XjeLCFQ(ZZ15R>B#zvl&#pj}jO8!jY}>4#KGARv|qYk*wNH=UDSRQB)}f*Yq5<%NW3>ccv)k!*j# zobUUY)Zc02Jr!=I-(M7!Q2}NPkhQN|f!vH0WV=`uZcQDXaJOBMKDuRon*XiAF%JNW zF>*=%&H!N}B0{E?X1~+jAln#1=x+Xx7nBPm4w@H0@guEH> zDR3y>#JS8a7X(pAeb1cMsAHk+salssiF>o}N9Sjkg`sgA7rPlP_vv!Ji({SwZ%YM+ zfBnV{iKRlQB}{9bR;@W)oZ7kgr2Z+oXVt>;Sn+`mG{RWQ546;E3|qi1RO3OHi#7Y! z>d`D+>ymH_=aqcA=0x<#9kQs+z-%Z^|TxN0Ig69gQq@1yUSYV2X7z1Yg_r^)7a7%B8Tn#!`2AR>k5 zQZ3S<=Akhc#oq{^!d9+_*>)S;i?c3Au9tl@T{A@Tl@whMxbghNX7z#n2U=An)3j$=_<(Pj!S+M!5($>4QDKr9PeFRkv;e%iXd#CH;9c!CFLHGz1;g4xm-w`BD%^>F|3y0xOJ#N?58#p%ygTzv@P8gp(qoQ2eY11Kcn>(@*nd-* zQ+4Tt>o%ML@;!kd{GEleo*pZp9OzUa7b8Y1(A8nw5AO1R;5+}}BIM(A2wes0^T2O& z?cKk(2STRVSVe>EGk8AyCk>D05H|e)>9$FopYLBM@&Lj3fYj}S2qpUa;gkQd$iMFR z?@1t<`9l_Zx1Y!dn)(>60<0|Hq(!yIotoSD;^L=(k(VxA(!AKE^%IWlju`$ZS#iN> zM&NZoS6A2n>tqgx%fBhU_BK|+-stZfF5@vw5HhXwF@)_{PKf9+c(bak@5?3IJ zFUPoJ*=G`NV=lDbs>J#3Y4yteF?^4uGtXGW+;cqY@~k;5&RoqQB>}f1AS)0b_@tgs zJbsESc%tCb9uM066A5b6TH1FJrzN#f@yvSWW*}#xgM(1qC}S zid-ucx@>UnkNLOi&hNaHz2swUYjUHJs(^Sa2`40m&r83o6ZLE9ELNKe%WA_%hA;lZ zv_imbCKHNss-NMsP{&XQAwgL-|5c9!@bR9G5BM0BgryhCyLEV>mN4%|;jZ^d84Bcb zrc980QwS`Og*nvh%k{1US}hL{AwumHYa%6nf%ZMMILqY;hhYs&v@K?;ez)a~z4p1$ zUa<3>k?M0x1VT()Uu*rkGAeeK+|oMxzJ2ETfbR=j4V}-fNVLBz#s6$xX6}GgAzaMlK{#{b7V4MB&~*n@5w?3s;(-szgYrusTq%-d zKOJ=Uez)IWKa?A2JRt`*3dYqRjg-m|e=3mYVG@x5S7{JQAr*0quryzvw@|<<*Q#h2@q&zF}GH5WOq)v9kra zw37BcClO5nhz#fNyv!(Fmu1Ilav%prlJmGLB_{2P3HrR|PRV;`5p&Wv`Xtx9xqpSR zf^kJ`VVgMHY0J^`^h9l^k-0e!$SsiA8Go{$rcmD-@Z}1?mG4lb$<)Xz4$7F%L5wkb zN@Dqbs4`2R$s>xS*DoCZo&vL?5NCZYLDZ(1E#pO@I6IMEfbsg-$;hq+q;3W=w>@4kJ{BO}$Eot1KOUObR zedOr7)*4qewXvOU6|Zj#<#`GfC^7&`iKvn>%@&cwb1|FRVtUaR()2IA^#)5nAbD#) z=vlVE!S3^jcHOq%LNv~-sdhK-&Lil3>iFWIGDx5Rur-U!YyQNETqZZ{Hjnf>;#pLU z>Z9#xGOtEWDQ!jvaGyOac1sFw*klIc6K5f@^!PTg0Qr!3!kg{~w-pbASt_T7K^gRS z;*3Ba0-p6OehcWr>L2H|6<5dpH7xDPJ-tCJydObgvi@yI2#x$zEu{m-+gI%=j>F-g znkn$xOw<(IEJ4-=@%j;D7vg`IUusxw=P`IJFuA|KkdO2K{^Wm5B#m<#BiW3!99$76 z5aO0;aV9x9Gfu$TKBW|7uAOD-bKcY7W=XUkX(g#25pQJ@iliI7!ci`1hAktAzUT9! zi602myWC$9@6oqb8(OqI$(K6-S401p%IIM6pisRVF7Um`53i-PH*2-66 ztXTTsD*(ez-84@vU)J`w#41+C#gclR6Gg*h(g{CF{JsDa^5Ae&?(f4**r*0#Z7K$VmN@**>nkN-rcoP?-vjTR$Bc-B3`h66V)@Y|PkByC?bd8KSeq&gcF5Q)KyB)IxJQ8K}Gj!>oW{F1n zf_~i%MOgGOsOF(}j>2X}z<7W!0kjZ`G(TnP()AQvrK?9O!ln?LG{^Q%D?^Tjf{gUx z-|G=I3I$o%<9|QF&xbja{~&1AuPhL8wU4z_&QNX_D+Cih;tb@B#ABoYMD1oX3_4Wn zOQm`7;@=_A2M!!Sfrx+P@1meaOC{>Jv9jM7rnyJvfo!qgrWeYn;0pT}6&P_^Ih7ev zYy;Q@jT$Oo(<43zboF61DxrM(C@n2*zcJv*gk_Y}S zY~5=*&!}Lu=c?nAfA2!uB;29hT}`(E`M`m%c1UPq>xM|Khk4mg?1}3m1$%6s7hmrr zM&tUjFg+r1%LXsfvY0uM77#6CC~p#}>p#_3DvJ!$i_f^#+YOTkUVUt{V8wbCxmIpzX*)n$FF)&i6#pH)U-i{o z+n~Rt^4uT#!=&Rq`Y%Ap4E8rdR8alyDpMd$%~0e zH&}ueS90=~B0=wF2}vvp8c3*%tsBEGM?3!yqH>=TiV5aDKRjyNS%5HwD7{1G3Wf}q3&8aVc6W3B#&wgds9a>`D_`ZS@ahB{X z=95=nG6o<$1Al#P@7}$rr-_NJAW%a2`O0HKu>2f_U-_#N3LxnNsK`ypPxA>=y!r0(OH_P=Ol2g#syz z8=7t0P9o5mBLPnXE!J{~dmM$7vo1&dS$}(_i_Jg#r$HJJenoQU0tY$!I1jmlo1_=! ziyj9TKOkYZH~s9B&^zjI^5t1cvg@}M)Af*$9SQ0pWW=-qP>R>PeEGxrd>u@ul57Atf4-saIbgONQeYm;QoMgr+`_ZAiUob|0sPAf$iuUfa+byqQH{T zDWJTeW(O*tLmQMcQLM3{^dy+Z6+Inpq@4o=JuP%8;D4xF@E1k`xieZN@g7~X;nKjX zTYY=*abU!D7g9gIUd%CG>Z;rLalzLZ2yYxCfHjoFEQgM-0AxM`!>#AtS_qEqhKO^C z3)Yyd!~GAmlR)8}E&%DZ!nh%W2c50M&vSYi85w!>iTHhIK$>_UiP)1@iabXho`W7o zA^kU>ORTL8pCJbaYMLbm>0x;HZwSa)lPt0DTRjTBQDe}#;(4<9*SRG6(NPE1ABus! z{_&cQ_r9tHoS~S$4mS`CG)jZpt;??kp};7ZaL@3+YgJ{5yyr`~J5=Q+_%?{?Fh76R zYkM|0H-QJThQVIW&ZnScE`~yMdGN%W3lN~y?9TUg9r6+UJon|5OViO96jG%^nyjEo zQ#tnRg2zn3E2M|Y$|Mu!Uk*_Op#O~$Qd}QN*1vQqR^Ic}*T(QHC?PkF{$(bs41{R}M_HA6qd+vM2->Ndtj4q^xHR73R~ zYIUbQd|`y(J)p?}aqdCcpt%Co>)FY*X`QkYRIgAIRH^~|12z3OJJQ^=YD!^B?Jpg! z0(Mpcj!8om7y1pE;M42Pkpk5lKUzlC8>Acu6BgT)lH+ezJ%q}~o;`a!zCMvU4)w|1 zJCO!7;6UpC>_*ItL+Hv=Xl{g31^9$D%yAY(ZBT?jr$2s4U;mt@W(r6|&^ZS+^0QDZ zc+&>a`J9#2sGn3bQVE7OpM!2)da(z9D1ox459|_yIfn44lo(NH4v(3ba%z)4G-!=8zdCB%WOOD0iT076Sb_Y_#ibIu|^s;VRX2f&*vhbABcj_qNC3OT}P%99bIbE z#)C}fo+zvq*-GeZQC@J?hLjq}A4GCxiJ%k*V+(1O?ODnMu)19!V#Kq5P zYctoqO4EXf`lc~5KK=qkIbhbzy!y}=343xK`sgAfX^`3r%=>p!1&?Cpz(S;%X@w&V1k(dvdWYgvQ~?G8KJxt8vp1lcze&D~m{QFHr~$(H zL&r{;eQy{T8VD~x35piqAF|$+de`*f144Ul^_7mN1bVZdP-KFj5e;7JKKog3yu{F_ z^fm-cFmy)$D+*nbq(n$sA|OqG=j07w-enD^hslN_gSxonnHHtdMMOlrh7ocT4it?B zDTCL+4jyzB7^`=C{`@&os)QmLGo?AV9|O(J=Pq8%f0;;peE&X8X=y1?2@yDb!$P{( zJQtzj1(8uB>Z{O+9tWi08DmP?LWUTumg(#30}aX8^CDRrTo*JVz`(qIhqwdK*2<@( zq%?52F!y6<(i)jWVDY{Pk)MU)llR3RPyiCM5xiu#gSdi1VnaiN-IrZ=9T0qlZt%Pf z-@>PWeUKr7EwXbzR3#6o+9;jyYU+k*9)Z?2pPtMrhs?~(Jv(XMC=BJsnye6Y4E-tW za6Fpl&Rqj6HssNN0R9T>EpJ2W(jhRdxVZC>yhm-H6$}#q1XKP;cYI!dd|6*#|E-O5 zF2JQG*crG&utFx_7u?9=El`%0W!9muG_WQ3(ThhLkP~2Iyd@;S@U%|wVfV@=GrF>fTw+wsc==9+;<^ zKCDAqO7d+9U6qbnL=jvZAS2UeW znU;}$k=YgImL-`lSALC@!!)De`^tJB*3r8nD$X>!S4#ZqEE#QWrlFtZ{_hpq4wsdz zy2Jh!q#!`UYXi(}ja32CssH_|&M=J)c#!}WlZvP&nhDZr|5=x8tV(*{Oq7l9GZnvj z4Flr)%l02Vn4f_-U-mV;;R|T!x9jc3xNFbmI+_ws!s|1!efjM!<=2E_O)iMX-^Sz- zZ@s`V8FKX`;Yd7duQqhV<1v^VyT)l;-*xdX7|eElS$>ZL2E&i*D{Z&eiNkfBs zjyFMha7yo+++lhRQ)6}2Fx`-TBc4E^)~5AiwaX&}h7Z)xE$B_kr1rk2=0~Lm*r?12 zIdK)2cEY3Q*I(dt!INH*iw}Y6^=A^jLc>Ym$ki>P2P{2riGHRgaGl`NS`B;tbDF*XUS+@8< zxpnz~uVV2`z`JrgadCT=(^$&(;&8yrm=iTw<_DWn|5XQ1SxuS*V`e^#dOPsa@4ROT~a<@%@Vbc zPe|T5d(^cGf46^OL8aHXareg4E-G6L#@NtYWA3u|lNYUFV}G-H+`+rJqpXZeN`&?J z)OSO{+Wf3*E>~?U`+r(yQSXcG^ST@3^-&a`lg(sreYm)Tv~4lD>U#H3kz{@%KEQFn z7wS1EN9wE;_s41uUvBA7PM~9Y;}6UH&HdR9c8Q83N08cq^QShdTHiMCx_L}gacR+G zc{@yDBZO%DCU(KI$?bK4ggIdI4tCR=0pgZsCp&(G)ZlKmPK0~P}$>U-;JN(!?| z6a#o!#(1VG5#suEraiyZ$quh(Em!)o=5Rfz(%hJ)?>fTli=-Zh*4^fLyF+1Y7iPT;$s4m*T3w~)hp;LH~)>#`A#=Q!8D-TCA-yVc( z6~!M-m3hT~KYBB4m}q{Xb+#?_2D{CUlVx-MLEl1at2TL zd6vDts&Rywo2*|Ma@q`w-X@dNsZ>T1=E(PmCwHvG<63tW$e||ZUf<-SDs-WkVWMeI zp2F8vtdlVAcmhSKp;q{q$E#O<%0`n4i>gJ5noj0Zt@T3z`mMK)I2BiK4a@;$2GE)Yv=_>1Lb{-N z" + "" ] }, "metadata": {}, @@ -121,7 +119,7 @@ } ], "source": [ - "plt_env.plot_cost(figsize=(8,6));\n", + "plot_cost_history(cost_history=optimizer.cost_history)\n", "plt.show()" ] }, @@ -130,7 +128,7 @@ "metadata": {}, "source": [ "## Animating swarms\n", - "The `PlotEnvironment()` offers two methods to perform animation, `plot_particles2D()` and `plot_particles3D()`. As its name suggests, these methods plot the particles in a 2-D or 3-D space. You can choose which dimensions will be plotted using the `index` argument, but the default takes the first 2 (or first three in 3D) indices of your swarm dimension. \n", + "The `plotters` module offers two methods to perform animation, `plot_contour()` and `plot_surface()`. As its name suggests, these methods plot the particles in a 2-D or 3-D space.\n", "\n", "Each animation method returns a `matplotlib.animation.Animation` class that still needs to be animated by a `Writer` class (thus necessitating the installation of a writer module). For the proceeding examples, we will convert the animations into an HTML5 video. In such case, we need to invoke some extra methods to do just that." ] @@ -138,9 +136,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# equivalent to rcParams['animation.html'] = 'html5'\n", @@ -152,1255 +148,3561 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Plotting in 2-D space\n" + "Lastly, it would be nice to add meshes in our swarm to plot the sphere function. This enables us to visually recognize where the particles are with respect to our objective function. We can accomplish that using the `Mesher` class." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from pyswarms.utils.plotters.formatters import Mesher" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, + "outputs": [], + "source": [ + "# Initialize mesher with sphere function\n", + "m = Mesher(func=fx.sphere_func)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are different formatters available in the `pyswarms.utils.plotters.formatters` module to customize your plots and visualizations. Aside from `Mesher`, there is a `Designer` class for customizing font sizes, figure sizes, etc. and an `Animator` class to set delays and repeats during animation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Plotting in 2-D space\n", + "\n", + "We can obtain the swarm's position history using the `pos_history` attribute from the `optimizer` instance. To plot a 2D-contour, simply pass this together with the `Mesher` to the `plot_contour()` function. In addition, we can also mark the global minima of the sphere function, `(0,0)`, to visualize the swarm's \"target\"." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, "outputs": [ { "data": { "text/html": [ - "" @@ -1535,2148 +3735,1933 @@ "" ] }, - "execution_count": 7, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "HTML(plt_env.plot_particles2D(limits=((-1.2,1.2),(-1.2,1.2))).to_html5_video())" + "# Make animation\n", + "animation = plot_contour(pos_history=optimizer.pos_history,\n", + " mesher=m,\n", + " mark=(0,0))\n", + "\n", + "# Enables us to view it in a Jupyter notebook\n", + "HTML(animation.to_html5_video())" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# Save as GIF\n", + "animation.save('plot_contour.gif', writer='imagemagick', dpi=96)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Plotting in 3-D space" + "### Plotting in 3-D space\n", + "\n", + "To plot in 3D space, we need a position-fitness matrix with shape `(iterations, n_particles, 3)`. The first two columns indicate the x-y position of the particles, while the third column is the fitness of that given position. You need to set this up on your own, but we have provided a helper function to compute this automatically" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# Obtain a position-fitness matrix using the Mesher.compute_history_3d()\n", + "# method. It requires a cost history obtainable from the optimizer class\n", + "pos_history_3d = m.compute_history_3d(optimizer.pos_history)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Make a designer and set the x,y,z limits to (-1,1), (-1,1) and (-0.1,1) respectively\n", + "from pyswarms.utils.plotters.formatters import Designer\n", + "d = Designer(limits=[(-1,1), (-1,1), (-0.1,1)], label=['x-axis', 'y-axis', 'z-axis'])" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "" @@ -3849,23 +5694,30 @@ "" ] }, - "execution_count": 14, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "HTML(plt_env.plot_particles3D(limits=((-1.2,1.2),(-1.2,1.2),(-1.2,1.2))).to_html5_video())" + "# Make animation\n", + "animation3d = plot_surface(pos_history=pos_history_3d, # Use the cost_history we computed\n", + " mesher=m, designer=d, # Customizations\n", + " mark=(0,0,0)) # Mark minima\n", + "\n", + "# Enables us to view it in a Jupyter notebook\n", + "HTML(animation3d.to_html5_video())" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "execution_count": 16, + "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# Save as GIF\n", + "animation3d.save('plot_surface.gif', writer='imagemagick', dpi=96)" + ] } ], "metadata": { From 8249ce284c92122d82b207a3e4b6240a7de5dd0a Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Thu, 14 Jun 2018 16:16:23 +0900 Subject: [PATCH 13/38] [ci skip] Update README --- README.md | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 328919f3..e75e796e 100644 --- a/README.md +++ b/README.md @@ -186,27 +186,48 @@ fresh run to plot the cost and create animation. ```python import pyswarms as ps from pyswarms.utils.functions import single_obj as fx -from pyswarms.utils.environments import PlotEnvironment +from pyswarms.utils.plotters import plot_cost_history # Set-up optimizer options = {'c1':0.5, 'c2':0.3, 'w':0.9} -optimizer = ps.single.GlobalBestPSO(n_particles=10, dimensions=3, options=options) -# Initialize plot environment -plt_env = PlotEnvironment(optimizer, fx.sphere_func, 1000) +optimizer = ps.single.GlobalBestPSO(n_particles=50, dimensions=2, options=options) +optimizer.optimize(fx.sphere_func, iters=100) # Plot the cost -plt_env.plot_cost(figsize=(8,6)); +plot_cost_history(optimizer.cost_history) plt.show() ``` - + -We can also plot the animation, +We can also plot the animation... ```python -plt_env.plot_particles2D(limits=((-1.2,1.2),(-1.2,1.2)) +from pyswarms.utils.plotters.formatters import Mesher +from pyswarms.utils.plotters.formatters import Designer +# Plot the sphere function's mesh for better plots +m = Mesher(func=fx.sphere_func) +# Adjust figure limits +d = Designer(limits=[(-1,1), (-1,1), (-0.1,1)], + label=['x-axis', 'y-axis', 'z-axis']) ``` - +In 2D, +```python +plot_contour(pos_history=optimizer.pos_history, mesher=m, mark=(0,0)) +``` + +![Contour](https://i.imgur.com/H3YofJ6.gif) + +Or in 3D! + +```python +pos_history_3d = m.compute_history_3d(optimizer.pos_history) # preprocessing +animation3d = plot_surface(pos_history=pos_history_3d, + mesher=m, designer=d, + mark=(0,0,0)) +``` + +![Surface](https://i.imgur.com/kRb61Hx.gif) ## Contributing From a863fb802e199f6d88f49b4ce23387a213be8f89 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Sun, 17 Jun 2018 00:13:43 +0900 Subject: [PATCH 14/38] Format code using black (#139) This commit formats the majority of pyswarms code using the black formatter. This will be our new formatter from this point forward. Signed-off-by: Lester James V. Miranda --- pyswarms/__init__.py | 10 +- pyswarms/backend/__init__.py | 2 +- pyswarms/backend/generators.py | 76 ++++++--- pyswarms/backend/operators.py | 57 +++++-- pyswarms/backend/swarms.py | 28 ++- pyswarms/backend/topology/__init__.py | 5 +- pyswarms/backend/topology/base.py | 4 +- pyswarms/backend/topology/ring.py | 16 +- pyswarms/backend/topology/star.py | 8 +- pyswarms/base/__init__.py | 5 +- pyswarms/base/base_discrete.py | 72 +++++--- pyswarms/base/base_single.py | 105 ++++++++---- pyswarms/discrete/__init__.py | 4 +- pyswarms/discrete/binary.py | 98 +++++++---- pyswarms/single/__init__.py | 5 +- pyswarms/single/global_best.py | 79 ++++++--- pyswarms/single/local_best.py | 99 +++++++---- pyswarms/utils/console_utils.py | 12 +- pyswarms/utils/environments/__init__.py | 4 +- .../utils/environments/plot_environment.py | 160 ++++++++++++------ pyswarms/utils/functions/single_obj.py | 144 ++++++++++------ pyswarms/utils/plotters/__init__.py | 2 +- pyswarms/utils/plotters/formatters.py | 40 +++-- pyswarms/utils/plotters/plotters.py | 100 +++++++---- pyswarms/utils/search/__init__.py | 5 +- pyswarms/utils/search/base_search.py | 32 ++-- pyswarms/utils/search/grid_search.py | 33 +++- pyswarms/utils/search/random_search.py | 67 +++++--- tests/backend/conftest.py | 17 +- tests/backend/test_generators.py | 44 +++-- tests/backend/test_operators.py | 14 +- tests/backend/topology/conftest.py | 18 +- tests/backend/topology/test_ring.py | 16 +- tests/backend/topology/test_star.py | 14 +- tests/optimizers/conftest.py | 37 ++-- tests/optimizers/test_binary.py | 59 ++++--- tests/optimizers/test_global_best.py | 79 +++++---- tests/optimizers/test_local_best.py | 101 +++++++---- tests/utils/environments/conftest.py | 24 ++- .../environments/test_plot_environment.py | 26 +-- tests/utils/functions/conftest.py | 17 +- .../utils/functions/test_singleobj_bounds.py | 61 ++++--- tests/utils/functions/test_singleobj_dims.py | 9 +- .../utils/functions/test_singleobj_return.py | 33 +++- .../functions/test_singleobj_returndims.py | 12 +- tests/utils/plotters/conftest.py | 11 +- tests/utils/plotters/test_plotters.py | 33 ++-- tests/utils/search/conftest.py | 92 +++++++--- tests/utils/search/test_gridsearch.py | 12 +- tests/utils/search/test_randomsearch.py | 9 +- 50 files changed, 1314 insertions(+), 696 deletions(-) diff --git a/pyswarms/__init__.py b/pyswarms/__init__.py index d79e93e4..ab501398 100644 --- a/pyswarms/__init__.py +++ b/pyswarms/__init__.py @@ -11,14 +11,10 @@ """ __author__ = """Lester James V. Miranda""" -__email__ = 'ljvmiranda@gmail.com' -__version__ = '0.2.0' +__email__ = "ljvmiranda@gmail.com" +__version__ = "0.2.0" from .single import global_best, local_best from .discrete import binary -__all__ = [ - 'global_best', - 'local_best', - 'binary' - ] +__all__ = ["global_best", "local_best", "binary"] diff --git a/pyswarms/backend/__init__.py b/pyswarms/backend/__init__.py index 2173eaaf..ff1d88c1 100644 --- a/pyswarms/backend/__init__.py +++ b/pyswarms/backend/__init__.py @@ -6,4 +6,4 @@ from .generators import * from .operators import * -from .swarms import * \ No newline at end of file +from .swarms import * diff --git a/pyswarms/backend/generators.py b/pyswarms/backend/generators.py index 7b31488f..10f28b90 100644 --- a/pyswarms/backend/generators.py +++ b/pyswarms/backend/generators.py @@ -16,7 +16,9 @@ from .swarms import Swarm -def generate_swarm(n_particles, dimensions, bounds=None, center=1.00, init_pos=None): +def generate_swarm( + n_particles, dimensions, bounds=None, center=1.00, init_pos=None +): """Generates a swarm Parameters @@ -44,23 +46,35 @@ def generate_swarm(n_particles, dimensions, bounds=None, center=1.00, init_pos=N if (init_pos is not None) and (bounds is None): pos = init_pos elif (init_pos is not None) and (bounds is not None): - if not (np.all(bounds[0] <= init_pos) and np.all(init_pos <= bounds[1])): - raise ValueError('User-defined init_pos is out of bounds.') + if not ( + np.all(bounds[0] <= init_pos) and np.all(init_pos <= bounds[1]) + ): + raise ValueError("User-defined init_pos is out of bounds.") pos = init_pos elif (init_pos is None) and (bounds is None): - pos = center * np.random.uniform(low=0.0, high=1.0, size=(n_particles, dimensions)) + pos = center * np.random.uniform( + low=0.0, high=1.0, size=(n_particles, dimensions) + ) else: lb, ub = bounds - min_bounds = np.repeat(np.array(lb)[np.newaxis, :], n_particles, axis=0) - max_bounds = np.repeat(np.array(ub)[np.newaxis, :], n_particles, axis=0) - pos = center * np.random.uniform(low=min_bounds, high=max_bounds, - size=(n_particles, dimensions)) + min_bounds = np.repeat( + np.array(lb)[np.newaxis, :], n_particles, axis=0 + ) + max_bounds = np.repeat( + np.array(ub)[np.newaxis, :], n_particles, axis=0 + ) + pos = center * np.random.uniform( + low=min_bounds, high=max_bounds, size=(n_particles, dimensions) + ) except ValueError: raise else: return pos -def generate_discrete_swarm(n_particles, dimensions, binary=False, init_pos=None): + +def generate_discrete_swarm( + n_particles, dimensions, binary=False, init_pos=None +): """Generates a discrete swarm Parameters @@ -78,19 +92,22 @@ def generate_discrete_swarm(n_particles, dimensions, binary=False, init_pos=None try: if (init_pos is not None) and binary: if not len(np.unique(init_pos)) == 2: - raise ValueError('User-defined init_pos is not binary!') + raise ValueError("User-defined init_pos is not binary!") pos = init_pos elif (init_pos is not None) and not binary: pos = init_pos elif (init_pos is None) and binary: pos = np.random.randint(2, size=(n_particles, dimensions)) else: - pos = np.random.random_sample(size=(n_particles, dimensions)).argsort(axis=1) + pos = np.random.random_sample( + size=(n_particles, dimensions) + ).argsort(axis=1) except ValueError: raise else: return pos + def generate_velocity(n_particles, dimensions, clamp=None): """Initializes a velocity vector @@ -111,17 +128,27 @@ def generate_velocity(n_particles, dimensions, clamp=None): velocity matrix of shape (n_particles, dimensions) """ try: - min_velocity, max_velocity = (0,1) if clamp==None else clamp - velocity = ((max_velocity - min_velocity) - * np.random.random_sample(size=(n_particles, dimensions)) - + min_velocity) + min_velocity, max_velocity = (0, 1) if clamp == None else clamp + velocity = (max_velocity - min_velocity) * np.random.random_sample( + size=(n_particles, dimensions) + ) + min_velocity except (ValueError, TypeError): raise else: return velocity -def create_swarm(n_particles, dimensions, discrete=False, binary=False, - options={}, bounds=None, center=1.0, init_pos=None, clamp=None): + +def create_swarm( + n_particles, + dimensions, + discrete=False, + binary=False, + options={}, + bounds=None, + center=1.0, + init_pos=None, + clamp=None, +): """Abstracts the generate_swarm() and generate_velocity() methods Parameters @@ -156,10 +183,17 @@ def create_swarm(n_particles, dimensions, discrete=False, binary=False, a Swarm class """ if discrete: - position = generate_discrete_swarm(n_particles, dimensions, binary=binary) + position = generate_discrete_swarm( + n_particles, dimensions, binary=binary + ) else: - position = generate_swarm(n_particles, dimensions, bounds=bounds, - center=center, init_pos=init_pos) + position = generate_swarm( + n_particles, + dimensions, + bounds=bounds, + center=center, + init_pos=init_pos, + ) velocity = generate_velocity(n_particles, dimensions, clamp=clamp) - return Swarm(position, velocity, options=options) \ No newline at end of file + return Swarm(position, velocity, options=options) diff --git a/pyswarms/backend/operators.py b/pyswarms/backend/operators.py index e2f01ed0..bf8419cb 100644 --- a/pyswarms/backend/operators.py +++ b/pyswarms/backend/operators.py @@ -17,6 +17,7 @@ # Create a logger logger = logging.getLogger(__name__) + def compute_pbest(swarm): """Takes a swarm instance and updates the personal best scores @@ -58,18 +59,23 @@ def compute_pbest(swarm): # Infer dimensions from positions dimensions = swarm.dimensions # Create a 1-D and 2-D mask based from comparisons - mask_cost = (swarm.current_cost < swarm.pbest_cost) - mask_pos = np.repeat(mask_cost[:, np.newaxis], swarm.dimensions, axis=1) + mask_cost = swarm.current_cost < swarm.pbest_cost + mask_pos = np.repeat( + mask_cost[:, np.newaxis], swarm.dimensions, axis=1 + ) # Apply masks new_pbest_pos = np.where(~mask_pos, swarm.pbest_pos, swarm.position) - new_pbest_cost = np.where(~mask_cost, swarm.pbest_cost, swarm.current_cost) + new_pbest_cost = np.where( + ~mask_cost, swarm.pbest_cost, swarm.current_cost + ) except AttributeError: - msg = 'Please pass a Swarm class. You passed {}'.format(type(swarm)) + msg = "Please pass a Swarm class. You passed {}".format(type(swarm)) logger.error(msg) raise else: return (new_pbest_pos, new_pbest_cost) + def compute_velocity(swarm, clamp): """Updates the velocity matrix @@ -107,12 +113,20 @@ def compute_velocity(swarm, clamp): try: # Prepare parameters swarm_size = swarm.position.shape - c1 = swarm.options['c1'] - c2 = swarm.options['c2'] - w = swarm.options['w'] + c1 = swarm.options["c1"] + c2 = swarm.options["c2"] + w = swarm.options["w"] # Compute for cognitive and social terms - cognitive = (c1 * np.random.uniform(0,1, swarm_size) * (swarm.pbest_pos - swarm.position)) - social = (c2 * np.random.uniform(0, 1, swarm_size) * (swarm.best_pos - swarm.position)) + cognitive = ( + c1 + * np.random.uniform(0, 1, swarm_size) + * (swarm.pbest_pos - swarm.position) + ) + social = ( + c2 + * np.random.uniform(0, 1, swarm_size) + * (swarm.best_pos - swarm.position) + ) # Compute temp velocity (subject to clamping if possible) temp_velocity = (w * swarm.velocity) + cognitive + social @@ -120,20 +134,22 @@ def compute_velocity(swarm, clamp): updated_velocity = temp_velocity else: min_velocity, max_velocity = clamp - mask = np.logical_and(temp_velocity >= min_velocity, - temp_velocity <= max_velocity) + mask = np.logical_and( + temp_velocity >= min_velocity, temp_velocity <= max_velocity + ) updated_velocity = np.where(~mask, swarm.velocity, temp_velocity) except AttributeError: - msg = 'Please pass a Swarm class. You passed {}'.format(type(swarm)) + msg = "Please pass a Swarm class. You passed {}".format(type(swarm)) logger.error(msg) raise except KeyError: - msg = 'Missing keyword in swarm.options' + msg = "Missing keyword in swarm.options" logger.error(msg) raise else: return updated_velocity + def compute_position(swarm, bounds): """Updates the position matrix @@ -160,15 +176,20 @@ def compute_position(swarm, bounds): if bounds is not None: lb, ub = bounds - min_bounds = np.repeat(np.array(lb)[np.newaxis, :], swarm.n_particles, axis=0) - max_bounds = np.repeat(np.array(ub)[np.newaxis, :], swarm.n_particles, axis=0) - mask = (np.all(min_bounds <= temp_position, axis=1) - * np.all(temp_position <= max_bounds, axis=1)) + min_bounds = np.repeat( + np.array(lb)[np.newaxis, :], swarm.n_particles, axis=0 + ) + max_bounds = np.repeat( + np.array(ub)[np.newaxis, :], swarm.n_particles, axis=0 + ) + mask = np.all(min_bounds <= temp_position, axis=1) * np.all( + temp_position <= max_bounds, axis=1 + ) mask = np.repeat(mask[:, np.newaxis], swarm.dimensions, axis=1) 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)) + msg = "Please pass a Swarm class. You passed {}".format(type(swarm)) logger.error(msg) raise else: diff --git a/pyswarms/backend/swarms.py b/pyswarms/backend/swarms.py index 6f1c8f27..8a63bd7c 100644 --- a/pyswarms/backend/swarms.py +++ b/pyswarms/backend/swarms.py @@ -10,9 +10,10 @@ # Import modules import numpy as np -from attr import (attrs, attrib) +from attr import attrs, attrib from attr.validators import instance_of + @attrs class Swarm(object): """A Swarm Class @@ -76,6 +77,7 @@ class Swarm(object): current_cost : numpy.ndarray (default is empty array) the current cost found by the swarm of shape :code:`(n_particles, dimensions)` """ + # Required attributes position = attrib(type=np.ndarray, validator=instance_of(np.ndarray)) velocity = attrib(type=np.ndarray, validator=instance_of(np.ndarray)) @@ -84,10 +86,24 @@ class Swarm(object): dimensions = attrib(type=int, validator=instance_of(int)) options = attrib(type=dict, default={}, validator=instance_of(dict)) pbest_pos = attrib(type=np.ndarray, validator=instance_of(np.ndarray)) - best_pos = attrib(type=np.ndarray, default=np.array([]), validator=instance_of(np.ndarray)) - pbest_cost = attrib(type=np.ndarray, default=np.array([]), validator=instance_of(np.ndarray)) - best_cost = attrib(type=float, default=np.inf, validator=instance_of((int, float))) - current_cost = attrib(type=np.ndarray, default=np.array([]), validator=instance_of(np.ndarray)) + best_pos = attrib( + type=np.ndarray, + default=np.array([]), + validator=instance_of(np.ndarray), + ) + pbest_cost = attrib( + type=np.ndarray, + default=np.array([]), + validator=instance_of(np.ndarray), + ) + best_cost = attrib( + type=float, default=np.inf, validator=instance_of((int, float)) + ) + current_cost = attrib( + type=np.ndarray, + default=np.array([]), + validator=instance_of(np.ndarray), + ) @n_particles.default def n_particles_default(self): @@ -99,4 +115,4 @@ def dimensions_default(self): @pbest_pos.default def pbest_pos_default(self): - return self.position \ No newline at end of file + return self.position diff --git a/pyswarms/backend/topology/__init__.py b/pyswarms/backend/topology/__init__.py index eef1b585..95673091 100644 --- a/pyswarms/backend/topology/__init__.py +++ b/pyswarms/backend/topology/__init__.py @@ -10,7 +10,4 @@ from .ring import Ring -__all__ = [ - "Star", - "Ring" -] \ No newline at end of file +__all__ = ["Star", "Ring"] diff --git a/pyswarms/backend/topology/base.py b/pyswarms/backend/topology/base.py index a9d7af44..aedfb9fc 100644 --- a/pyswarms/backend/topology/base.py +++ b/pyswarms/backend/topology/base.py @@ -12,8 +12,8 @@ :mod:`pyswarms.backend.swarms.Swarm` module. """ -class Topology(object): +class Topology(object): def __init__(self, **kwargs): """Initializes the class""" pass @@ -29,4 +29,4 @@ def compute_position(self, swarm): def compute_velocity(self, swarm): """Updates the swarm's velocity-matrix""" - raise NotImplementedError("Topology::compute_velocity()") \ No newline at end of file + raise NotImplementedError("Topology::compute_velocity()") diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index 2e7034d7..cfebb69a 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -22,8 +22,8 @@ # Create a logger logger = logging.getLogger(__name__) -class Ring(Topology): +class Ring(Topology): def __init__(self): super(Ring, self).__init__() @@ -62,15 +62,21 @@ 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[idx][:, np.newaxis].argmin(axis=1) + best_neighbor = swarm.pbest_cost[idx][:, np.newaxis].argmin( + axis=1 + ) else: idx_min = swarm.pbest_cost[idx].argmin(axis=1) best_neighbor = idx[np.arange(len(idx)), idx_min] # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) - best_pos = swarm.pbest_pos[np.argmin(swarm.pbest_cost[best_neighbor])] + best_pos = swarm.pbest_pos[ + np.argmin(swarm.pbest_cost[best_neighbor]) + ] except AttributeError: - msg = 'Please pass a Swarm class. You passed {}'.format(type(swarm)) + msg = "Please pass a Swarm class. You passed {}".format( + type(swarm) + ) logger.error(msg) raise else: @@ -134,4 +140,4 @@ def compute_position(self, swarm, bounds=None): numpy.ndarray New position-matrix """ - return ops.compute_position(swarm, bounds) \ No newline at end of file + return ops.compute_position(swarm, bounds) diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index f4203340..e655217d 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -21,8 +21,8 @@ # Create a logger logger = logging.getLogger(__name__) -class Star(Topology): +class Star(Topology): def __init__(self): super(Star, self).__init__() @@ -63,7 +63,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)) + msg = "Please pass a Swarm class. You passed {}".format( + type(swarm) + ) logger.error(msg) raise else: @@ -127,4 +129,4 @@ def compute_position(self, swarm, bounds=None): numpy.ndarray New position-matrix """ - return ops.compute_position(swarm, bounds) \ No newline at end of file + return ops.compute_position(swarm, bounds) diff --git a/pyswarms/base/__init__.py b/pyswarms/base/__init__.py index a225d367..6c0d4b79 100644 --- a/pyswarms/base/__init__.py +++ b/pyswarms/base/__init__.py @@ -6,7 +6,4 @@ from .base_single import SwarmOptimizer from .base_discrete import DiscreteSwarmOptimizer -__all__ = [ - "SwarmOptimizer" - "DiscreteSwarmOptimizer", - ] +__all__ = ["SwarmOptimizer" "DiscreteSwarmOptimizer"] diff --git a/pyswarms/base/base_discrete.py b/pyswarms/base/base_discrete.py index ae20fc15..52781888 100644 --- a/pyswarms/base/base_discrete.py +++ b/pyswarms/base/base_discrete.py @@ -38,8 +38,8 @@ # Import from package from ..backend import create_swarm -class DiscreteSwarmOptimizer(object): +class DiscreteSwarmOptimizer(object): def assertions(self): """Assertion method to check various inputs. @@ -59,20 +59,27 @@ def assertions(self): # Check clamp settings if self.velocity_clamp is not None: if not isinstance(self.velocity_clamp, tuple): - raise TypeError('Parameter `velocity_clamp` must be a tuple') + raise TypeError("Parameter `velocity_clamp` must be a tuple") if not len(self.velocity_clamp) == 2: - raise IndexError('Parameter `velocity_clamp` must be of ' - 'size 2') + raise IndexError( + "Parameter `velocity_clamp` must be of " "size 2" + ) if not self.velocity_clamp[0] < self.velocity_clamp[1]: - raise ValueError('Make sure that velocity_clamp is in the ' - 'form (v_min, v_max)') + raise ValueError( + "Make sure that velocity_clamp is in the " + "form (v_min, v_max)" + ) # Required keys in options argument - if not all(key in self.options for key in ('c1', 'c2', 'w')): - raise KeyError('Missing either c1, c2, or w in options') - - def setup_logging(self, default_path='./config/logging.yaml', - default_level=logging.INFO, env_key='LOG_CFG'): + 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 @@ -89,14 +96,22 @@ def setup_logging(self, default_path='./config/logging.yaml', if value: path = value if os.path.exists(path): - with open(path, 'rt') as f: + 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, dimensions, binary, options, - velocity_clamp=None, init_pos=None, ftol=-np.inf): + def __init__( + self, + n_particles, + dimensions, + binary, + options, + velocity_clamp=None, + init_pos=None, + ftol=-np.inf, + ): """Initializes the swarm. Creates a :code:`numpy.ndarray` of positions depending on the @@ -142,10 +157,16 @@ def __init__(self, n_particles, dimensions, binary, options, self.init_pos = init_pos self.ftol = ftol # Initialize named tuple for populating the history list - self.ToHistory = namedtuple('ToHistory', - ['best_cost', 'mean_pbest_cost', - 'mean_neighbor_cost', 'position', - 'velocity']) + self.ToHistory = namedtuple( + "ToHistory", + [ + "best_cost", + "mean_pbest_cost", + "mean_neighbor_cost", + "position", + "velocity", + ], + ) # Invoke assertions self.assertions() @@ -228,9 +249,12 @@ def reset(self): self.velocity_history = [] # Initialize the swarm - self.swarm = create_swarm(n_particles=self.n_particles, - dimensions=self.dimensions, - discrete=True, - init_pos=self.init_pos, - binary=self.binary, - clamp=self.velocity_clamp, options=self.options) + self.swarm = create_swarm( + n_particles=self.n_particles, + dimensions=self.dimensions, + discrete=True, + init_pos=self.init_pos, + binary=self.binary, + clamp=self.velocity_clamp, + options=self.options, + ) diff --git a/pyswarms/base/base_single.py b/pyswarms/base/base_single.py index 190dd7ed..339559e2 100644 --- a/pyswarms/base/base_single.py +++ b/pyswarms/base/base_single.py @@ -39,8 +39,8 @@ # Import from package from ..backend import create_swarm -class SwarmOptimizer(object): +class SwarmOptimizer(object): def assertions(self): """Assertion method to check various inputs. @@ -60,43 +60,58 @@ def assertions(self): # Check setting of bounds if self.bounds is not None: if not isinstance(self.bounds, tuple): - raise TypeError('Parameter `bound` must be a tuple.') + raise TypeError("Parameter `bound` must be a tuple.") if not len(self.bounds) == 2: - raise IndexError('Parameter `bound` must be of size 2.') + raise IndexError("Parameter `bound` must be of size 2.") if not self.bounds[0].shape == self.bounds[1].shape: - raise IndexError('Arrays in `bound` must be of equal shapes') - if not self.bounds[0].shape[0] == self.bounds[1].shape[0] == \ - self.dimensions: - raise IndexError('Parameter `bound` must be the same shape ' - 'as dimensions.') + raise IndexError("Arrays in `bound` must be of equal shapes") + if ( + not self.bounds[0].shape[0] + == self.bounds[1].shape[0] + == self.dimensions + ): + raise IndexError( + "Parameter `bound` must be the same shape " + "as dimensions." + ) if not (self.bounds[1] > self.bounds[0]).all(): - raise ValueError('Values of `bounds[1]` must be greater than ' - '`bounds[0]`.') + raise ValueError( + "Values of `bounds[1]` must be greater than " + "`bounds[0]`." + ) # Check clamp settings - if hasattr(self.velocity_clamp, '__iter__'): + if hasattr(self.velocity_clamp, "__iter__"): if not len(self.velocity_clamp) == 2: - raise IndexError('Parameter `velocity_clamp` must be of ' - 'size 2') + raise IndexError( + "Parameter `velocity_clamp` must be of " "size 2" + ) if not self.velocity_clamp[0] < self.velocity_clamp[1]: - raise ValueError('Make sure that velocity_clamp is in the ' - 'form (min, max)') + raise ValueError( + "Make sure that velocity_clamp is in the " + "form (min, max)" + ) # Check setting of center if isinstance(self.center, (list, np.ndarray)): if not len(self.center) == self.dimensions: - raise IndexError('Parameter `center` must be the same shape ' - 'as dimensions.') + raise IndexError( + "Parameter `center` must be the same shape " + "as dimensions." + ) if isinstance(self.center, np.ndarray) and self.center.ndim != 1: - raise ValueError('Parameter `center` must have a 1d array') - + raise ValueError("Parameter `center` must have a 1d array") # Required keys in options argument - if not all(key in self.options for key in ('c1', 'c2', 'w')): - raise KeyError('Missing either c1, c2, or w in options') - - def setup_logging(self, default_path='./config/logging.yaml', - default_level=logging.INFO, env_key='LOG_CFG'): + 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 @@ -113,14 +128,23 @@ def setup_logging(self, default_path='./config/logging.yaml', if value: path = value if os.path.exists(path): - with open(path, 'rt') as f: + 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, dimensions, options, bounds=None, - velocity_clamp=None, center=1.0, ftol=-np.inf, init_pos=None): + def __init__( + self, + n_particles, + dimensions, + options, + bounds=None, + velocity_clamp=None, + center=1.0, + ftol=-np.inf, + init_pos=None, + ): """Initializes the swarm. Creates a Swarm class depending on the values initialized @@ -165,10 +189,16 @@ def __init__(self, n_particles, dimensions, options, bounds=None, self.ftol = ftol self.init_pos = init_pos # Initialize named tuple for populating the history list - self.ToHistory = namedtuple('ToHistory', - ['best_cost', 'mean_pbest_cost', - 'mean_neighbor_cost', 'position', - 'velocity']) + self.ToHistory = namedtuple( + "ToHistory", + [ + "best_cost", + "mean_pbest_cost", + "mean_neighbor_cost", + "position", + "velocity", + ], + ) # Invoke assertions self.assertions() # Initialize resettable attributes @@ -250,7 +280,12 @@ def reset(self): self.velocity_history = [] # Initialize the swarm - self.swarm = create_swarm(n_particles=self.n_particles, - dimensions=self.dimensions, - bounds=self.bounds, center=self.center, init_pos=self.init_pos, - clamp=self.velocity_clamp, options=self.options) + self.swarm = create_swarm( + n_particles=self.n_particles, + dimensions=self.dimensions, + bounds=self.bounds, + center=self.center, + init_pos=self.init_pos, + clamp=self.velocity_clamp, + options=self.options, + ) diff --git a/pyswarms/discrete/__init__.py b/pyswarms/discrete/__init__.py index 8610851b..a7120805 100644 --- a/pyswarms/discrete/__init__.py +++ b/pyswarms/discrete/__init__.py @@ -6,6 +6,4 @@ from .binary import BinaryPSO -__all__ = [ - "BinaryPSO" - ] +__all__ = ["BinaryPSO"] diff --git a/pyswarms/discrete/binary.py b/pyswarms/discrete/binary.py index 8811d392..a9e41d2c 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -63,8 +63,8 @@ from ..backend.topology import Ring from ..utils.console_utils import cli_print, end_report -class BinaryPSO(DiscreteSwarmOptimizer): +class BinaryPSO(DiscreteSwarmOptimizer): def assertions(self): """Assertion method to check various inputs. @@ -78,17 +78,27 @@ def assertions(self): """ super(BinaryPSO, self).assertions() - if not all(key in self.options for key in ('k', 'p')): - raise KeyError('Missing either k or p in options') + if not all(key in self.options for key in ("k", "p")): + raise KeyError("Missing either k or p in options") if not 0 <= self.k <= self.n_particles: - raise ValueError('No. of neighbors must be between 0 and no. of' - 'particles.') + raise ValueError( + "No. of neighbors must be between 0 and no. of" "particles." + ) if self.p not in [1, 2]: - raise ValueError('p-value should either be 1 (for L1/Minkowski)' - 'or 2 (for L2/Euclidean).') - - def __init__(self, n_particles, dimensions, options, init_pos=None, - velocity_clamp=None, ftol=-np.inf): + raise ValueError( + "p-value should either be 1 (for L1/Minkowski)" + "or 2 (for L2/Euclidean)." + ) + + def __init__( + self, + n_particles, + dimensions, + options, + init_pos=None, + velocity_clamp=None, + ftol=-np.inf, + ): """Initializes the swarm. Attributes @@ -121,11 +131,17 @@ def __init__(self, n_particles, dimensions, options, init_pos=None, # Initialize logger self.logger = logging.getLogger(__name__) # Assign k-neighbors and p-value as attributes - self.k, self.p = options['k'], options['p'] + self.k, self.p = options["k"], options["p"] # Initialize parent class - super(BinaryPSO, self).__init__(n_particles=n_particles, dimensions=dimensions, - binary=True, options=options, init_pos=init_pos, - velocity_clamp=velocity_clamp, ftol=ftol) + super(BinaryPSO, self).__init__( + n_particles=n_particles, + dimensions=dimensions, + binary=True, + options=options, + init_pos=init_pos, + velocity_clamp=velocity_clamp, + ftol=ftol, + ) # Invoke assertions self.assertions() # Initialize the resettable attributes @@ -160,35 +176,50 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): # Compute cost for current position and personal best self.swarm.current_cost = objective_func(self.swarm.position) self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos) - self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(self.swarm) + self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest( + self.swarm + ) best_cost_yet_found = np.min(self.swarm.best_cost) # Update gbest from neighborhood - self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest(self.swarm, - self.p, - self.k) + 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 %s/%s, cost: %s' % - (i+1, iters, np.min(self.swarm.best_cost)), verbose, 2, - logger=self.logger) + cli_print( + "Iteration %s/%s, cost: %s" + % (i + 1, iters, np.min(self.swarm.best_cost)), + verbose, + 2, + logger=self.logger, + ) # Save to history - hist = self.ToHistory(best_cost=self.swarm.best_cost, - mean_pbest_cost=np.mean(self.swarm.pbest_cost), - mean_neighbor_cost=np.mean(self.swarm.best_cost), - position=self.swarm.position, - velocity=self.swarm.velocity) + hist = self.ToHistory( + best_cost=self.swarm.best_cost, + mean_pbest_cost=np.mean(self.swarm.pbest_cost), + mean_neighbor_cost=np.mean(self.swarm.best_cost), + position=self.swarm.position, + velocity=self.swarm.velocity, + ) self._populate_history(hist) # Verify stop criteria based on the relative acceptable cost ftol - relative_measure = self.ftol*(1 + np.abs(best_cost_yet_found)) - if np.abs(self.swarm.best_cost - best_cost_yet_found) < relative_measure: + relative_measure = self.ftol * (1 + np.abs(best_cost_yet_found)) + if ( + np.abs(self.swarm.best_cost - best_cost_yet_found) + < relative_measure + ): break # Perform position velocity update - self.swarm.velocity = self.top.compute_velocity(self.swarm, self.velocity_clamp) + self.swarm.velocity = self.top.compute_velocity( + self.swarm, self.velocity_clamp + ) self.swarm.position = self._compute_position(self.swarm) # 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) + end_report( + final_best_cost, final_best_pos, verbose, logger=self.logger + ) return (final_best_cost, final_best_pos) def _compute_position(self, swarm): @@ -203,8 +234,10 @@ def _compute_position(self, swarm): swarm: pyswarms.backend.swarms.Swarm a Swarm class """ - return (np.random.random_sample(size=swarm.dimensions) < self._sigmoid - (swarm.velocity)) * 1 + return ( + np.random.random_sample(size=swarm.dimensions) + < self._sigmoid(swarm.velocity) + ) * 1 def _sigmoid(self, x): """Helper method for the sigmoid function. @@ -220,4 +253,3 @@ def _sigmoid(self, x): Output sigmoid computation """ return 1 / (1 + np.exp(x)) - diff --git a/pyswarms/single/__init__.py b/pyswarms/single/__init__.py index 2e7a96a1..26cbc982 100644 --- a/pyswarms/single/__init__.py +++ b/pyswarms/single/__init__.py @@ -7,7 +7,4 @@ from .global_best import GlobalBestPSO from .local_best import LocalBestPSO -__all__ = [ - "GlobalBestPSO", - "LocalBestPSO" - ] +__all__ = ["GlobalBestPSO", "LocalBestPSO"] diff --git a/pyswarms/single/global_best.py b/pyswarms/single/global_best.py index c21e35b7..9d31401e 100644 --- a/pyswarms/single/global_best.py +++ b/pyswarms/single/global_best.py @@ -69,10 +69,17 @@ class GlobalBestPSO(SwarmOptimizer): - - def __init__(self, n_particles, dimensions, options, - bounds=None, velocity_clamp=None, center=1.00, ftol=-np.inf, - init_pos=None): + def __init__( + self, + n_particles, + dimensions, + options, + bounds=None, + velocity_clamp=None, + center=1.00, + ftol=-np.inf, + init_pos=None, + ): """Initializes the swarm. Attributes @@ -104,10 +111,16 @@ def __init__(self, n_particles, dimensions, options, relative error in objective_func(best_pos) acceptable for convergence """ - super(GlobalBestPSO, self).__init__(n_particles=n_particles, dimensions=dimensions, - options=options, bounds=bounds, - velocity_clamp=velocity_clamp, - center=center, ftol=ftol, init_pos=init_pos) + super(GlobalBestPSO, self).__init__( + n_particles=n_particles, + dimensions=dimensions, + options=options, + bounds=bounds, + velocity_clamp=velocity_clamp, + center=center, + ftol=ftol, + init_pos=init_pos, + ) # Initialize logger self.logger = logging.getLogger(__name__) @@ -144,34 +157,52 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): # Compute cost for current position and personal best self.swarm.current_cost = objective_func(self.swarm.position) self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos) - self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(self.swarm) + self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest( + self.swarm + ) best_cost_yet_found = self.swarm.best_cost # Get minima of pbest and check if it's less than gbest if np.min(self.swarm.pbest_cost) < self.swarm.best_cost: - self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest(self.swarm) + self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( + self.swarm + ) # Print to console if i % print_step == 0: - cli_print('Iteration %s/%s, cost: %s' % - (i+1, iters, self.swarm.best_cost), verbose, 2, - logger=self.logger) + cli_print( + "Iteration %s/%s, cost: %s" + % (i + 1, iters, self.swarm.best_cost), + verbose, + 2, + logger=self.logger, + ) # Save to history - hist = self.ToHistory(best_cost=self.swarm.best_cost, - mean_pbest_cost=np.mean(self.swarm.pbest_cost), - mean_neighbor_cost=self.swarm.best_cost, - position=self.swarm.position, - velocity=self.swarm.velocity) + hist = self.ToHistory( + best_cost=self.swarm.best_cost, + mean_pbest_cost=np.mean(self.swarm.pbest_cost), + mean_neighbor_cost=self.swarm.best_cost, + position=self.swarm.position, + velocity=self.swarm.velocity, + ) self._populate_history(hist) # Verify stop criteria based on the relative acceptable cost ftol - relative_measure = self.ftol*(1 + np.abs(best_cost_yet_found)) - if np.abs(self.swarm.best_cost - best_cost_yet_found) < relative_measure: + relative_measure = self.ftol * (1 + np.abs(best_cost_yet_found)) + if ( + np.abs(self.swarm.best_cost - best_cost_yet_found) + < relative_measure + ): break # Perform velocity and position updates - self.swarm.velocity = self.top.compute_velocity(self.swarm, self.velocity_clamp) - self.swarm.position = self.top.compute_position(self.swarm, self.bounds) + self.swarm.velocity = self.top.compute_velocity( + self.swarm, self.velocity_clamp + ) + self.swarm.position = self.top.compute_position( + self.swarm, self.bounds + ) # 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() # Write report in log and return final cost and position - end_report(final_best_cost, final_best_pos, verbose, logger=self.logger) + end_report( + final_best_cost, final_best_pos, verbose, logger=self.logger + ) return (final_best_cost, final_best_pos) - diff --git a/pyswarms/single/local_best.py b/pyswarms/single/local_best.py index e4bb7897..6d368ca3 100644 --- a/pyswarms/single/local_best.py +++ b/pyswarms/single/local_best.py @@ -78,7 +78,6 @@ class LocalBestPSO(SwarmOptimizer): - def assertions(self): """Assertion method to check various inputs. @@ -92,17 +91,29 @@ def assertions(self): """ super(LocalBestPSO, self).assertions() - if not all(key in self.options for key in ('k', 'p')): - raise KeyError('Missing either k or p in options') + if not all(key in self.options for key in ("k", "p")): + raise KeyError("Missing either k or p in options") if not 0 <= self.k <= self.n_particles: - raise ValueError('No. of neighbors must be between 0 and no. ' - 'of particles.') + raise ValueError( + "No. of neighbors must be between 0 and no. " "of particles." + ) if self.p not in [1, 2]: - raise ValueError('p-value should either be 1 (for L1/Minkowski) ' - 'or 2 (for L2/Euclidean).') - - def __init__(self, n_particles, dimensions, options, bounds=None, - velocity_clamp=None, center=1.00, ftol=-np.inf, init_pos=None): + raise ValueError( + "p-value should either be 1 (for L1/Minkowski) " + "or 2 (for L2/Euclidean)." + ) + + def __init__( + self, + n_particles, + dimensions, + options, + bounds=None, + velocity_clamp=None, + center=1.00, + ftol=-np.inf, + init_pos=None, + ): """Initializes the swarm. Attributes @@ -144,13 +155,18 @@ def __init__(self, n_particles, dimensions, options, bounds=None, # Initialize logger self.logger = logging.getLogger(__name__) # Assign k-neighbors and p-value as attributes - self.k, self.p = options['k'], options['p'] + self.k, self.p = options["k"], options["p"] # Initialize parent class - super(LocalBestPSO, self).__init__(n_particles=n_particles, dimensions=dimensions, - options=options, bounds=bounds, - velocity_clamp=velocity_clamp, - center=center, ftol=ftol, - init_pos=init_pos) + super(LocalBestPSO, self).__init__( + n_particles=n_particles, + dimensions=dimensions, + options=options, + bounds=bounds, + velocity_clamp=velocity_clamp, + center=center, + ftol=ftol, + init_pos=init_pos, + ) # Invoke assertions self.assertions() # Initialize the resettable attributes @@ -185,33 +201,50 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): # Compute cost for current position and personal best self.swarm.current_cost = objective_func(self.swarm.position) self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos) - self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(self.swarm) + self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest( + self.swarm + ) best_cost_yet_found = np.min(self.swarm.best_cost) # Update gbest from neighborhood - self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest(self.swarm, - self.p, - self.k) + 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 %s/%s, cost: %s' % - (i+1, iters, np.min(self.swarm.best_cost)), verbose, 2, - logger=self.logger) + cli_print( + "Iteration %s/%s, cost: %s" + % (i + 1, iters, np.min(self.swarm.best_cost)), + verbose, + 2, + logger=self.logger, + ) # Save to history - hist = self.ToHistory(best_cost=self.swarm.best_cost, - mean_pbest_cost=np.mean(self.swarm.pbest_cost), - mean_neighbor_cost=np.mean(self.swarm.best_cost), - position=self.swarm.position, - velocity=self.swarm.velocity) + hist = self.ToHistory( + best_cost=self.swarm.best_cost, + mean_pbest_cost=np.mean(self.swarm.pbest_cost), + mean_neighbor_cost=np.mean(self.swarm.best_cost), + position=self.swarm.position, + velocity=self.swarm.velocity, + ) self._populate_history(hist) # Verify stop criteria based on the relative acceptable cost ftol - relative_measure = self.ftol*(1 + np.abs(best_cost_yet_found)) - if np.abs(self.swarm.best_cost - best_cost_yet_found) < relative_measure: + relative_measure = self.ftol * (1 + np.abs(best_cost_yet_found)) + if ( + np.abs(self.swarm.best_cost - best_cost_yet_found) + < relative_measure + ): break # Perform position velocity update - self.swarm.velocity = self.top.compute_velocity(self.swarm, self.velocity_clamp) - self.swarm.position = self.top.compute_position(self.swarm, self.bounds) + self.swarm.velocity = self.top.compute_velocity( + self.swarm, self.velocity_clamp + ) + self.swarm.position = self.top.compute_position( + self.swarm, self.bounds + ) # 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) + end_report( + final_best_cost, final_best_pos, verbose, logger=self.logger + ) return (final_best_cost, final_best_pos) diff --git a/pyswarms/utils/console_utils.py b/pyswarms/utils/console_utils.py index c282b3e9..22ae118d 100644 --- a/pyswarms/utils/console_utils.py +++ b/pyswarms/utils/console_utils.py @@ -49,13 +49,15 @@ def end_report(cost, pos, verbosity, logger): # Cuts the length of the best position if it's too long if len(list(pos)) > 3: - out = ('[ ' + 3 * '{:3f} ' + '...]').format(*list(pos)) + 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) + 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/pyswarms/utils/environments/__init__.py b/pyswarms/utils/environments/__init__.py index bd5d791e..2207de52 100644 --- a/pyswarms/utils/environments/__init__.py +++ b/pyswarms/utils/environments/__init__.py @@ -6,6 +6,4 @@ from .plot_environment import PlotEnvironment -__all__ = [ - "PlotEnvironment" - ] +__all__ = ["PlotEnvironment"] diff --git a/pyswarms/utils/environments/plot_environment.py b/pyswarms/utils/environments/plot_environment.py index b1ca0f0f..fc77d60e 100644 --- a/pyswarms/utils/environments/plot_environment.py +++ b/pyswarms/utils/environments/plot_environment.py @@ -70,29 +70,39 @@ from collections import namedtuple from mpl_toolkits.mplot3d import Axes3D -warnings.simplefilter('default') -warnings.warn("The pyswarms.environments module is deprecated and will be removed in v.0.2.5. For visualization, please use pyswarms.plotters", DeprecationWarning, stacklevel=2) +warnings.simplefilter("default") +warnings.warn( + "The pyswarms.environments module is deprecated and will be removed in v.0.2.5. For visualization, please use pyswarms.plotters", + DeprecationWarning, + stacklevel=2, +) -class PlotEnvironment(object): +class PlotEnvironment(object): def assertions(self): """Assertion check""" # Check if the objective_func is a callable if not callable(self.objective_func): - raise TypeError('Must pass a callable') + raise TypeError("Must pass a callable") # Check if getters exist in the optimizer - if not (hasattr(self.optimizer, 'get_cost_history') - & hasattr(self.optimizer, 'get_pos_history') - & hasattr(self.optimizer, 'get_velocity_history')): - raise AttributeError('Missing getters in optimizer, check ' - 'pyswarms.base module') + if not ( + hasattr(self.optimizer, "get_cost_history") + & hasattr(self.optimizer, "get_pos_history") + & hasattr(self.optimizer, "get_velocity_history") + ): + raise AttributeError( + "Missing getters in optimizer, check " "pyswarms.base module" + ) # Check if important methods exist in the optimizer - if not (hasattr(self.optimizer, 'optimize') - & hasattr(self.optimizer, 'reset')): - raise AttributeError('Missing methods in optimizer, check ' - 'pyswarms.base module') + if not ( + hasattr(self.optimizer, "optimize") + & hasattr(self.optimizer, "reset") + ): + raise AttributeError( + "Missing methods in optimizer, check " "pyswarms.base module" + ) def __init__(self, optimizer, objective_func, iters): """Runs the optimizer against an objective function for a number @@ -127,12 +137,19 @@ def __init__(self, optimizer, objective_func, iters): self.optimizer.reset() self.status = self.optimizer.optimize(objective_func, iters, 1, 0) # Initialize tuples for particle plotting - self.Index = namedtuple('Index', ['x', 'y', 'z']) - self.Limit = namedtuple('Limit', ['x', 'y', 'z']) - self.Label = namedtuple('Label', ['x', 'y', 'z']) - - def plot_cost(self, title='Cost History', ax=None, figsize=None, - title_fontsize="large", text_fontsize="medium", **kwargs): + self.Index = namedtuple("Index", ["x", "y", "z"]) + self.Limit = namedtuple("Limit", ["x", "y", "z"]) + self.Label = namedtuple("Label", ["x", "y", "z"]) + + def plot_cost( + self, + title="Cost History", + ax=None, + figsize=None, + title_fontsize="large", + text_fontsize="medium", + **kwargs + ): """Creates a simple line plot with the cost in the y-axis and the iteration at the x-axis @@ -174,27 +191,45 @@ def plot_cost(self, title='Cost History', ax=None, figsize=None, # Plot with self.iters as x-axis and cost_history as # y-axis. - ax.plot(np.arange(self.iters), cost_history, 'k', lw=2, - label='Best cost') - ax.plot(np.arange(self.iters), mean_pbest_history, 'k--', lw=2, - label='Avg. personal best cost') - ax.plot(np.arange(self.iters), mean_neighbor_history, 'k:', lw=2, - label='Avg. neighborhood cost') + ax.plot( + np.arange(self.iters), cost_history, "k", lw=2, label="Best cost" + ) + ax.plot( + np.arange(self.iters), + mean_pbest_history, + "k--", + lw=2, + label="Avg. personal best cost", + ) + ax.plot( + np.arange(self.iters), + mean_neighbor_history, + "k:", + lw=2, + label="Avg. neighborhood cost", + ) # Customize plot depending on parameters ax.set_title(title, fontsize=title_fontsize) ax.legend(fontsize=text_fontsize) - ax.set_xlabel('Iterations', fontsize=text_fontsize) - ax.set_ylabel('Cost', fontsize=text_fontsize) + ax.set_xlabel("Iterations", fontsize=text_fontsize) + ax.set_ylabel("Cost", fontsize=text_fontsize) ax.tick_params(labelsize=text_fontsize) return ax - def plot_particles2D(self, index=(0, 1), limits=((-1, 1), (-1, 1)), - labels=('x-axis', 'y-axis'), interval=80, - title='Particle Movement in 2D space', - ax=None, figsize=None, title_fontsize="large", - text_fontsize="medium"): + def plot_particles2D( + self, + index=(0, 1), + limits=((-1, 1), (-1, 1)), + labels=("x-axis", "y-axis"), + interval=80, + title="Particle Movement in 2D space", + ax=None, + figsize=None, + title_fontsize="large", + text_fontsize="medium", + ): """Creates an animation of particle movement in 2D-space Parameters @@ -238,7 +273,7 @@ def plot_particles2D(self, index=(0, 1), limits=((-1, 1), (-1, 1)), """ # Check inconsistencies with input if not (len(index) == len(limits) == 2): - raise ValueError('The index and limits should be of length 2') + raise ValueError("The index and limits should be of length 2") # Set-up tuples for plotting environment idx = self.Index(x=index[0], y=index[1], z=None) @@ -262,26 +297,35 @@ def plot_particles2D(self, index=(0, 1), limits=((-1, 1), (-1, 1)), ax.set_ylim(lmt.y) # Plot data - plot = ax.scatter(x=[], y=[], c='red') + plot = ax.scatter(x=[], y=[], c="red") data = self.optimizer.get_pos_history # Get the number of iterations n_iters = self.optimizer.get_pos_history.shape[0] # Perform animation - anim = animation.FuncAnimation(fig, func=self._animate2D, - frames=xrange(n_iters), - fargs=(data, plot, idx), - interval=interval, blit=True) + anim = animation.FuncAnimation( + fig, + func=self._animate2D, + frames=xrange(n_iters), + fargs=(data, plot, idx), + interval=interval, + blit=True, + ) return anim - def plot_particles3D(self, index=(0, 1, 2), - limits=((-1, 1), (-1, 1), (-1, 1)), - labels=('x-axis', 'y-axis', 'z-axis'), - interval=80, - title='Particle Movement in 3D space', ax=None, - figsize=None, title_fontsize="large", - text_fontsize="medium"): + def plot_particles3D( + self, + index=(0, 1, 2), + limits=((-1, 1), (-1, 1), (-1, 1)), + labels=("x-axis", "y-axis", "z-axis"), + interval=80, + title="Particle Movement in 3D space", + ax=None, + figsize=None, + title_fontsize="large", + text_fontsize="medium", + ): """Creates an animation of particle movement in 2D-space Parameters @@ -326,7 +370,7 @@ def plot_particles3D(self, index=(0, 1, 2), """ # Check inconsistencies with input if not (len(index) == len(limits) == 3): - raise ValueError('The index and limits should be of length 3') + raise ValueError("The index and limits should be of length 3") # Set-up tuples for plotting environment idx = self.Index(x=index[0], y=index[1], z=index[2]) @@ -353,17 +397,20 @@ def plot_particles3D(self, index=(0, 1, 2), ax.set_zlim(lmt.z) # Plot data - plot = ax.scatter(xs=[], ys=[], zs=[], c='red') + plot = ax.scatter(xs=[], ys=[], zs=[], c="red") data = self.optimizer.get_pos_history # Get the number of iterations n_iters = self.optimizer.get_pos_history.shape[0] # Perform animation - anim = animation.FuncAnimation(fig, func=self._animate3D, - frames=xrange(n_iters), - fargs=(data, plot, idx), - interval=interval) + anim = animation.FuncAnimation( + fig, + func=self._animate3D, + frames=xrange(n_iters), + fargs=(data, plot, idx), + interval=interval, + ) return anim def _animate2D(self, i, data, plot, idx): @@ -391,7 +438,7 @@ def _animate2D(self, i, data, plot, idx): current_pos = data[i] xy = current_pos[:, (idx.x, idx.y)] plot.set_offsets(xy) - return plot, + return (plot,) def _animate3D(self, i, data, plot, idx): """Helper animation function that is called seqentially @@ -416,7 +463,10 @@ def _animate3D(self, i, data, plot, idx): iterable of artists """ current_pos = data[i] - x, y, z = current_pos[:, idx.x], current_pos[:, idx.y], \ - current_pos[:, idx.z] + x, y, z = ( + current_pos[:, idx.x], + current_pos[:, idx.y], + current_pos[:, idx.z], + ) plot._offsets3d = (x, y, z) - return plot, + return (plot,) diff --git a/pyswarms/utils/functions/single_obj.py b/pyswarms/utils/functions/single_obj.py index ad1206ff..b1e4aa05 100644 --- a/pyswarms/utils/functions/single_obj.py +++ b/pyswarms/utils/functions/single_obj.py @@ -42,7 +42,7 @@ def sphere_func(x): numpy.ndarray computed cost of size :code:`(n_particles, )` """ - j = (x**2.0).sum(axis=1) + j = (x ** 2.0).sum(axis=1) return j @@ -70,11 +70,12 @@ def rastrigin_func(x): domain """ if not np.logical_and(x >= -5.12, x <= 5.12).all(): - raise ValueError('Input for Rastrigin function must be within ' - '[-5.12, 5.12].') + raise ValueError( + "Input for Rastrigin function must be within " "[-5.12, 5.12]." + ) d = x.shape[1] - j = 10.0 * d + (x**2.0 - 10.0 * np.cos(2.0 * np.pi * x)).sum(axis=1) + j = 10.0 * d + (x ** 2.0 - 10.0 * np.cos(2.0 * np.pi * x)).sum(axis=1) return j @@ -102,13 +103,15 @@ def ackley_func(x): domain """ if not np.logical_and(x >= -32, x <= 32).all(): - raise ValueError('Input for Ackley function must be within [-32, 32].') + raise ValueError("Input for Ackley function must be within [-32, 32].") d = x.shape[1] - j = (-20.0 * np.exp(-0.2 * np.sqrt((1/d) * (x**2).sum(axis=1))) - - np.exp((1/float(d)) * np.cos(2 * np.pi * x).sum(axis=1)) - + 20.0 - + np.exp(1)) + j = ( + -20.0 * np.exp(-0.2 * np.sqrt((1 / d) * (x ** 2).sum(axis=1))) + - np.exp((1 / float(d)) * np.cos(2 * np.pi * x).sum(axis=1)) + + 20.0 + + np.exp(1) + ) return j @@ -132,7 +135,9 @@ def rosenbrock_func(x): computed cost of size :code:`(n_particles, )` """ - r = np.sum(100*(x.T[1:] - x.T[:-1]**2.0)**2 + (1-x.T[:-1])**2.0, axis=0) + r = np.sum( + 100 * (x.T[1:] - x.T[:-1] ** 2.0) ** 2 + (1 - x.T[:-1]) ** 2.0, axis=0 + ) return r @@ -163,16 +168,19 @@ def beale_func(x): domain """ if not x.shape[1] == 2: - raise IndexError('Beale function only takes two-dimensional input.') + raise IndexError("Beale function only takes two-dimensional input.") if not np.logical_and(x >= -4.5, x <= 4.5).all(): - raise ValueError('Input for Beale function must be within ' - '[-4.5, 4.5].') + raise ValueError( + "Input for Beale function must be within " "[-4.5, 4.5]." + ) x_ = x[:, 0] y_ = x[:, 1] - j = ((1.5 - x_ + x_ * y_)**2.0 - + (2.25 - x_ + x_ * y_**2.0)**2.0 - + (2.625 - x_ + x_ * y_**3.0)**2.0) + j = ( + (1.5 - x_ + x_ * y_) ** 2.0 + + (2.25 - x_ + x_ * y_ ** 2.0) ** 2.0 + + (2.625 - x_ + x_ * y_ ** 3.0) ** 2.0 + ) return j @@ -204,18 +212,39 @@ def goldstein_func(x): domain """ if not x.shape[1] == 2: - raise IndexError('Goldstein function only takes two-dimensional ' - 'input.') + raise IndexError( + "Goldstein function only takes two-dimensional " "input." + ) if not np.logical_and(x >= -2, x <= 2).all(): - raise ValueError('Input for Goldstein-Price function must be within ' - '[-2, 2].') + raise ValueError( + "Input for Goldstein-Price function must be within " "[-2, 2]." + ) x_ = x[:, 0] y_ = x[:, 1] - j = ((1 + (x_ + y_ + 1)**2.0 - * (19 - 14*x_ + 3*x_**2.0 - 14*y_ + 6*x_*y_ + 3*y_**2.0)) - * (30 + (2*x_ - 3 * y_)**2.0 - * (18 - 32*x_ + 12*x_**2.0 + 48*y_ - 36*x_*y_ + 27*y_**2.0))) + j = ( + 1 + + (x_ + y_ + 1) ** 2.0 + * ( + 19 + - 14 * x_ + + 3 * x_ ** 2.0 + - 14 * y_ + + 6 * x_ * y_ + + 3 * y_ ** 2.0 + ) + ) * ( + 30 + + (2 * x_ - 3 * y_) ** 2.0 + * ( + 18 + - 32 * x_ + + 12 * x_ ** 2.0 + + 48 * y_ + - 36 * x_ * y_ + + 27 * y_ ** 2.0 + ) + ) return j @@ -247,13 +276,13 @@ def booth_func(x): domain """ if not x.shape[1] == 2: - raise IndexError('Booth function only takes two-dimensional input.') + raise IndexError("Booth function only takes two-dimensional input.") if not np.logical_and(x >= -10, x <= 10).all(): - raise ValueError('Input for Booth function must be within [-10, 10].') + raise ValueError("Input for Booth function must be within [-10, 10].") x_ = x[:, 0] y_ = x[:, 1] - j = (x_ + 2 * y_ - 7)**2.0 + (2 * x_ + y_ - 5)**2.0 + j = (x_ + 2 * y_ - 7) ** 2.0 + (2 * x_ + y_ - 5) ** 2.0 return j @@ -287,19 +316,23 @@ def bukin6_func(x): domain """ if not x.shape[1] == 2: - raise IndexError('Bukin N. 6 function only takes two-dimensional ' - 'input.') + raise IndexError( + "Bukin N. 6 function only takes two-dimensional " "input." + ) if not np.logical_and(x[:, 0] >= -15, x[:, 0] <= -5).all(): - raise ValueError('x-coord for Bukin N. 6 function must be within ' - '[-15, -5].') + raise ValueError( + "x-coord for Bukin N. 6 function must be within " "[-15, -5]." + ) if not np.logical_and(x[:, 1] >= -3, x[:, 1] <= 3).all(): - raise ValueError('y-coord for Bukin N. 6 function must be within ' - '[-3, 3].') + raise ValueError( + "y-coord for Bukin N. 6 function must be within " "[-3, 3]." + ) x_ = x[:, 0] y_ = x[:, 1] - j = 100 * np.sqrt(np.absolute(y_**2.0 - 0.01*x_**2.0)) + 0.01 * \ - np.absolute(x_ + 10) + j = 100 * np.sqrt( + np.absolute(y_ ** 2.0 - 0.01 * x_ ** 2.0) + ) + 0.01 * np.absolute(x_ + 10) return j @@ -321,14 +354,15 @@ def matyas_func(x): numpy.ndarray """ if not x.shape[1] == 2: - raise IndexError('Matyas function only takes two-dimensional input.') + raise IndexError("Matyas function only takes two-dimensional input.") if not np.logical_and(x >= -10, x <= 10).all(): - raise ValueError('Input for Matyas function must be within ' - '[-10, 10].') + raise ValueError( + "Input for Matyas function must be within " "[-10, 10]." + ) x_ = x[:, 0] y_ = x[:, 1] - j = 0.26 * (x_**2.0 + y_**2.0) - 0.48 * x_ * y_ + j = 0.26 * (x_ ** 2.0 + y_ ** 2.0) - 0.48 * x_ * y_ return j @@ -360,9 +394,9 @@ def levi_func(x): domain """ if not x.shape[1] == 2: - raise IndexError('Levi function only takes two-dimensional input.') + raise IndexError("Levi function only takes two-dimensional input.") if not np.logical_and(x >= -10, x <= 10).all(): - raise ValueError('Input for Levi function must be within [-10, 10].') + raise ValueError("Input for Levi function must be within [-10, 10].") mask = np.full(x.shape, False) mask[:, -1] = True @@ -372,11 +406,12 @@ def levi_func(x): masked_w_ = np.ma.array(w_, mask=mask) d_ = x.shape[1] - 1 - j = (np.sin(np.pi * w_[:, 0])**2.0 - + ((masked_x - 1)**2.0).sum(axis=1) - * (1 + 10 * np.sin(np.pi * (masked_w_).sum(axis=1) + 1)**2.0) - + (w_[:, d_] - 1)**2.0 - * (1 + np.sin(2 * np.pi * w_[:, d_])**2.0)) + j = ( + np.sin(np.pi * w_[:, 0]) ** 2.0 + + ((masked_x - 1) ** 2.0).sum(axis=1) + * (1 + 10 * np.sin(np.pi * (masked_w_).sum(axis=1) + 1) ** 2.0) + + (w_[:, d_] - 1) ** 2.0 * (1 + np.sin(2 * np.pi * w_[:, d_]) ** 2.0) + ) return j @@ -408,16 +443,19 @@ def schaffer2_func(x): domain """ if not x.shape[1] == 2: - raise IndexError('Schaffer N. 2 function only takes two-dimensional ' - 'input.') + raise IndexError( + "Schaffer N. 2 function only takes two-dimensional " "input." + ) if not np.logical_and(x >= -100, x <= 100).all(): - raise ValueError('Input for Schaffer function must be within ' - '[-100, 100].') + raise ValueError( + "Input for Schaffer function must be within " "[-100, 100]." + ) x_ = x[:, 0] y_ = x[:, 1] - j = (0.5 - + ((np.sin(x_**2.0 - y_**2.0)**2.0 - 0.5) - / ((1 + 0.001 * (x_**2.0 + y_**2.0))**2.0))) + j = 0.5 + ( + (np.sin(x_ ** 2.0 - y_ ** 2.0) ** 2.0 - 0.5) + / ((1 + 0.001 * (x_ ** 2.0 + y_ ** 2.0)) ** 2.0) + ) return j diff --git a/pyswarms/utils/plotters/__init__.py b/pyswarms/utils/plotters/__init__.py index 2ad916e9..c2b2b683 100644 --- a/pyswarms/utils/plotters/__init__.py +++ b/pyswarms/utils/plotters/__init__.py @@ -5,4 +5,4 @@ 3D spaces. """ -from .plotters import * \ No newline at end of file +from .plotters import * diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index fbffc31a..58de46b1 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -8,9 +8,10 @@ # Import modules import numpy as np -from attr import (attrs, attrib) +from attr import attrs, attrib from attr.validators import instance_of + @attrs class Designer(object): """Designer class for specifying a plot's formatting and design @@ -45,16 +46,23 @@ class Designer(object): Label to show in the x, y, or z-axis. For a 3D plot, please pass an iterable with three elements. """ + # Overall plot design - figsize = attrib(type=tuple, validator=instance_of(tuple), default=(10,8)) - title_fontsize = attrib(validator=instance_of((str, int, float)), - default='large') - text_fontsize = attrib(validator=instance_of((str, int, float)), - default='medium') - legend = attrib(validator=instance_of(str), default='Cost') - label = attrib(validator=instance_of((str, list, tuple)), default=['x-axis', 'y-axis']) - limits = attrib(validator=instance_of((list, tuple)), - default=[(-1,1),(-1,1)]) + figsize = attrib(type=tuple, validator=instance_of(tuple), default=(10, 8)) + title_fontsize = attrib( + validator=instance_of((str, int, float)), default="large" + ) + text_fontsize = attrib( + validator=instance_of((str, int, float)), default="medium" + ) + legend = attrib(validator=instance_of(str), default="Cost") + label = attrib( + validator=instance_of((str, list, tuple)), default=["x-axis", "y-axis"] + ) + limits = attrib( + validator=instance_of((list, tuple)), default=[(-1, 1), (-1, 1)] + ) + @attrs class Animator(object): @@ -84,10 +92,12 @@ class Animator(object): repeat : bool (default is :code:`True`) Pass :code:`False` if you don't want to repeat the animation. """ + interval = attrib(type=int, validator=instance_of(int), default=80) repeat_delay = attrib(default=None) repeat = attrib(type=bool, validator=instance_of(bool), default=True) + @attrs class Mesher(object): """Mesher class for plotting contours of objective functions @@ -121,12 +131,14 @@ class Mesher(object): alpha : float (default is :code:`0.3`) Transparency of the surface plot """ + func = attrib() # For mesh creation delta = attrib(type=float, default=0.001) - limits = attrib(validator=instance_of((list, tuple)), - default=[(-1,1),(-1,1)]) - levels = attrib(type=list, default=np.arange(-2.0,2.0,0.070)) + limits = attrib( + validator=instance_of((list, tuple)), default=[(-1, 1), (-1, 1)] + ) + levels = attrib(type=list, default=np.arange(-2.0, 2.0, 0.070)) # Surface transparency alpha = attrib(type=float, validator=instance_of(float), default=0.3) @@ -149,4 +161,4 @@ def compute_history_3d(self, pos_history): 3D position matrix of shape :code:`(iterations, n_particles, 3)` """ fitness = np.array(list(map(self.func, pos_history))) - return np.dstack((pos_history, fitness)) \ No newline at end of file + return np.dstack((pos_history, fitness)) diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py index 25db5506..0bbc9356 100644 --- a/pyswarms/utils/plotters/plotters.py +++ b/pyswarms/utils/plotters/plotters.py @@ -71,17 +71,19 @@ import matplotlib.pyplot as plt import numpy as np -from matplotlib import (animation, cm) +from matplotlib import animation, cm from mpl_toolkits.mplot3d import Axes3D # Import from package -from .formatters import (Designer, Animator, Mesher) +from .formatters import Designer, Animator, Mesher # Initialize logger logger = logging.getLogger(__name__) -def plot_cost_history(cost_history, ax=None, title='Cost History', - designer=None, **kwargs): + +def plot_cost_history( + cost_history, ax=None, title="Cost History", designer=None, **kwargs +): """Creates a simple line plot with the cost in the y-axis and the iteration at the x-axis @@ -113,14 +115,16 @@ def plot_cost_history(cost_history, ax=None, title='Cost History', # If no Designer class supplied, use defaults if designer is None: - designer = Designer(legend='Cost', label=['Iterations', 'Cost']) + designer = Designer(legend="Cost", label=["Iterations", "Cost"]) # If no ax supplied, create new instance if ax is None: - _, ax = plt.subplots(1,1, figsize=designer.figsize) + _, ax = plt.subplots(1, 1, figsize=designer.figsize) # Plot with iters in x-axis and the cost in y-axis - ax.plot(np.arange(iters), cost_history, 'k', lw=2, label=designer.legend) + ax.plot( + np.arange(iters), cost_history, "k", lw=2, label=designer.legend + ) # Customize plot depending on parameters ax.set_title(title, fontsize=designer.title_fontsize) @@ -133,8 +137,17 @@ def plot_cost_history(cost_history, ax=None, title='Cost History', else: return ax -def plot_contour(pos_history, canvas=None, title='Trajectory', mark=None, - designer=None, mesher=None, animator=None, **kwargs): + +def plot_contour( + pos_history, + canvas=None, + title="Trajectory", + mark=None, + designer=None, + mesher=None, + animator=None, + **kwargs +): """Draws a 2D contour map for particle trajectories Here, the space is represented as flat plane. The contours indicate the @@ -174,7 +187,9 @@ def plot_contour(pos_history, canvas=None, title='Trajectory', mark=None, try: # If no Designer class supplied, use defaults if designer is None: - designer = Designer(limits=[(-1,1), (-1,1)], label=['x-axis', 'y-axis']) + designer = Designer( + limits=[(-1, 1), (-1, 1)], label=["x-axis", "y-axis"] + ) # If no Animator class supplied, use defaults if animator is None: @@ -204,26 +219,37 @@ def plot_contour(pos_history, canvas=None, title='Trajectory', mark=None, # Mark global best if possible if mark is not None: - ax.scatter(mark[0], mark[1], color='red', marker='x') + ax.scatter(mark[0], mark[1], color="red", marker="x") # Put scatter skeleton - plot = ax.scatter(x=[], y=[], c='black', alpha=0.6, **kwargs) + plot = ax.scatter(x=[], y=[], c="black", alpha=0.6, **kwargs) # Do animation - anim = animation.FuncAnimation(fig=fig, - func=_animate, - frames=range(n_iters), - fargs=(pos_history, plot), - interval=animator.interval, - repeat=animator.repeat, - repeat_delay=animator.repeat_delay) + anim = animation.FuncAnimation( + fig=fig, + func=_animate, + frames=range(n_iters), + fargs=(pos_history, plot), + interval=animator.interval, + repeat=animator.repeat, + repeat_delay=animator.repeat_delay, + ) except TypeError: raise else: return anim -def plot_surface(pos_history, canvas=None, title='Trajectory', - designer=None, mesher=None, animator=None, mark=None, **kwargs): + +def plot_surface( + pos_history, + canvas=None, + title="Trajectory", + designer=None, + mesher=None, + animator=None, + mark=None, + **kwargs +): """Plots a swarm's trajectory in 3D This is useful for plotting the swarm's 2-dimensional position with @@ -294,8 +320,10 @@ def plot_surface(pos_history, canvas=None, title='Trajectory', try: # If no Designer class supplied, use defaults if designer is None: - designer = Designer(limits=[(-1,1), (-1,1), (-1,1)], - label=['x-axis', 'y-axis', 'z-axis']) + designer = Designer( + limits=[(-1, 1), (-1, 1), (-1, 1)], + label=["x-axis", "y-axis", "z-axis"], + ) # If no Animator class supplied, use defaults if animator is None: @@ -330,24 +358,27 @@ def plot_surface(pos_history, canvas=None, title='Trajectory', # Mark global best if possible if mark is not None: - ax.scatter(mark[0], mark[1], mark[2], color='red', marker='x') + ax.scatter(mark[0], mark[1], mark[2], color="red", marker="x") # Put scatter skeleton - plot = ax.scatter(xs=[], ys=[], zs=[], c='black', alpha=0.6, **kwargs) + plot = ax.scatter(xs=[], ys=[], zs=[], c="black", alpha=0.6, **kwargs) # Do animation - anim = animation.FuncAnimation(fig=fig, - func=_animate, - frames=range(n_iters), - fargs=(pos_history, plot), - interval=animator.interval, - repeat=animator.repeat, - repeat_delay=animator.repeat_delay) + anim = animation.FuncAnimation( + fig=fig, + func=_animate, + frames=range(n_iters), + fargs=(pos_history, plot), + interval=animator.interval, + repeat=animator.repeat, + repeat_delay=animator.repeat_delay, + ) except TypeError: raise else: return anim + def _animate(i, data, plot): """Helper animation function that is called sequentially :class:`matplotlib.animation.FuncAnimation` @@ -357,7 +388,8 @@ def _animate(i, data, plot): plot.set_offsets(current_pos) else: plot._offsets3d = current_pos.T - return plot, + return (plot,) + def _mesh(mesher): """Helper function to make a mesh""" @@ -370,4 +402,4 @@ def _mesh(mesher): # Get z-value z = mesher.func(xypairs) zz = z.reshape(xx.shape) - return (xx, yy, zz) \ No newline at end of file + return (xx, yy, zz) diff --git a/pyswarms/utils/search/__init__.py b/pyswarms/utils/search/__init__.py index dc1fcce8..7eee30bb 100644 --- a/pyswarms/utils/search/__init__.py +++ b/pyswarms/utils/search/__init__.py @@ -6,7 +6,4 @@ from .grid_search import GridSearch from .random_search import RandomSearch -__all__ = [ - "GridSearch", - "RandomSearch" - ] +__all__ = ["GridSearch", "RandomSearch"] diff --git a/pyswarms/utils/search/base_search.py b/pyswarms/utils/search/base_search.py index 6e3d529f..4aa3fc9b 100644 --- a/pyswarms/utils/search/base_search.py +++ b/pyswarms/utils/search/base_search.py @@ -11,7 +11,6 @@ class SearchBase(object): - def assertions(self): """Assertion method to check :code:`optimizer` input. @@ -21,13 +20,22 @@ def assertions(self): When :code:`optimizer` does not have an `'optimize'` attribute. """ # Check type of optimizer object - if not hasattr(self.optimizer, 'optimize'): - raise TypeError('Parameter `optimizer` must have an ' - '`\'optimize\'` attribute.') - - def __init__(self, optimizer, n_particles, dimensions, options, - objective_func, iters, - bounds=None, velocity_clamp=(0,1)): + if not hasattr(self.optimizer, "optimize"): + raise TypeError( + "Parameter `optimizer` must have an " "`'optimize'` attribute." + ) + + def __init__( + self, + optimizer, + n_particles, + dimensions, + options, + objective_func, + iters, + bounds=None, + velocity_clamp=(0, 1), + ): """Initializes the Search. Attributes @@ -91,8 +99,9 @@ def generate_score(self, options): """ # Intialize optimizer - f = self.optimizer(self.n_particles, self.dims, options, - self.bounds, self.vclamp) + f = self.optimizer( + self.n_particles, self.dims, options, self.bounds, self.vclamp + ) # Return score return f.optimize(self.objective_func, self.iters)[0] @@ -121,8 +130,7 @@ def search(self, maximum=False): # Catches the maximum bool flag if maximum: - idx, self.best_score = max(enumerate(scores), - key=op.itemgetter(1)) + idx, self.best_score = max(enumerate(scores), key=op.itemgetter(1)) # Return optimum hyperparameter value property from grid using index self.best_options = op.itemgetter(idx)(grid) diff --git a/pyswarms/utils/search/grid_search.py b/pyswarms/utils/search/grid_search.py index ac2bd1b1..4d518550 100644 --- a/pyswarms/utils/search/grid_search.py +++ b/pyswarms/utils/search/grid_search.py @@ -44,15 +44,30 @@ class GridSearch(SearchBase): """Exhaustive search of optimal performance on selected objective function over all combinations of specified hyperparameter values.""" - def __init__(self, optimizer, n_particles, dimensions, options, - objective_func, iters, bounds=None, velocity_clamp=(0,1)): + def __init__( + self, + optimizer, + n_particles, + dimensions, + options, + objective_func, + iters, + bounds=None, + velocity_clamp=(0, 1), + ): """Initializes the paramsearch.""" # Assign attributes - super(GridSearch, self).__init__(optimizer, n_particles, dimensions, - options, objective_func, iters, - bounds=bounds, - velocity_clamp=velocity_clamp) + super(GridSearch, self).__init__( + optimizer, + n_particles, + dimensions, + options, + objective_func, + iters, + bounds=bounds, + velocity_clamp=velocity_clamp, + ) # invoke assertions self.assertions() @@ -61,8 +76,10 @@ def generate_grid(self): # Extract keys and values from options dictionary params = self.options.keys() - items = [x if type(x) == list - else [x] for x in list(zip(*self.options.items()))[1]] + items = [ + x if type(x) == list else [x] + for x in list(zip(*self.options.items()))[1] + ] # Create list of cartesian products of hyperparameter values # from options diff --git a/pyswarms/utils/search/random_search.py b/pyswarms/utils/search/random_search.py index 1f1211c1..43215f17 100644 --- a/pyswarms/utils/search/random_search.py +++ b/pyswarms/utils/search/random_search.py @@ -60,12 +60,22 @@ def assertions(self): # Check type of n_selection_iters parameter if not isinstance(self.n_selection_iters, int): - raise TypeError('Parameter `n_selection_iters` must be of ' - 'type int') - - def __init__(self, optimizer, n_particles, dimensions, options, - objective_func, iters, n_selection_iters, - bounds=None, velocity_clamp=(0,1)): + raise TypeError( + "Parameter `n_selection_iters` must be of " "type int" + ) + + def __init__( + self, + optimizer, + n_particles, + dimensions, + options, + objective_func, + iters, + n_selection_iters, + bounds=None, + velocity_clamp=(0, 1), + ): """Initializes the paramsearch. Attributes @@ -77,10 +87,16 @@ def __init__(self, optimizer, n_particles, dimensions, options, # Assign n_selection_iters as attribute self.n_selection_iters = n_selection_iters # Assign attributes - super(RandomSearch, self).__init__(optimizer, n_particles, dimensions, - options, objective_func, iters, - bounds=bounds, - velocity_clamp=velocity_clamp) + super(RandomSearch, self).__init__( + optimizer, + n_particles, + dimensions, + options, + objective_func, + iters, + bounds=bounds, + velocity_clamp=velocity_clamp, + ) # Invoke assertions self.assertions() @@ -91,26 +107,31 @@ def generate_grid(self): params = {} # Remove 'p' to hold as a constant in the paramater combinations - p = options.pop('p') - params['p'] = [p for _ in xrange(self.n_selection_iters)] + p = options.pop("p") + params["p"] = [p for _ in xrange(self.n_selection_iters)] # Assign generators based on parameter type param_generators = { - 'c1': np.random.uniform, - 'c2': np.random.uniform, - 'w': np.random.uniform, - 'k': np.random.randint + "c1": np.random.uniform, + "c2": np.random.uniform, + "w": np.random.uniform, + "k": np.random.randint, } # Generate random values for hyperparameters 'c1', 'c2', 'w', and 'k' for idx, bounds in options.items(): params[idx] = param_generators[idx]( - *bounds, size=self.n_selection_iters) + *bounds, size=self.n_selection_iters + ) # Return list of dicts of hyperparameter combinations - return [{'c1': params['c1'][i], - 'c2': params['c2'][i], - 'w': params['w'][i], - 'k': params['k'][i], - 'p': params['p'][i]} - for i in xrange(self.n_selection_iters)] + return [ + { + "c1": params["c1"][i], + "c2": params["c2"][i], + "w": params["w"][i], + "k": params["k"][i], + "p": params["p"][i], + } + for i in xrange(self.n_selection_iters) + ] diff --git a/tests/backend/conftest.py b/tests/backend/conftest.py index 6f7096c1..bccc1047 100644 --- a/tests/backend/conftest.py +++ b/tests/backend/conftest.py @@ -10,17 +10,18 @@ # Import from package from pyswarms.backend.swarms import Swarm + @pytest.fixture def swarm(): """A contrived instance of the Swarm class at a certain timestep""" attrs_at_t = { - 'position' : np.array([[5,5,5], [3,3,3], [1,1,1]]), - 'velocity' : np.array([[1,1,1], [1,1,1], [1,1,1]]), - 'current_cost' : np.array([2,2,2]), - 'pbest_cost' : np.array([1,2,3]), - 'pbest_pos' : np.array([[1,2,3], [4,5,6], [7,8,9]]), - 'best_cost' : 1, - 'best_pos' : np.array([1,1,1]), - 'options' : {'c1' : 0.5, 'c2': 1, 'w': 2} + "position": np.array([[5, 5, 5], [3, 3, 3], [1, 1, 1]]), + "velocity": np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]]), + "current_cost": np.array([2, 2, 2]), + "pbest_cost": np.array([1, 2, 3]), + "pbest_pos": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), + "best_cost": 1, + "best_pos": np.array([1, 1, 1]), + "options": {"c1": 0.5, "c2": 1, "w": 2}, } return Swarm(**attrs_at_t) diff --git a/tests/backend/test_generators.py b/tests/backend/test_generators.py index d9c541c3..6c48c56c 100644 --- a/tests/backend/test_generators.py +++ b/tests/backend/test_generators.py @@ -9,12 +9,15 @@ import pyswarms.backend as P -@pytest.mark.parametrize('bounds', [None, ([2,2,2], [5,5,5]), ([-1,-1,0], [2,2,5])]) -@pytest.mark.parametrize('center', [1, [3,3,3], [0.2,0.2,0.1]]) +@pytest.mark.parametrize( + "bounds", [None, ([2, 2, 2], [5, 5, 5]), ([-1, -1, 0], [2, 2, 5])] +) +@pytest.mark.parametrize("center", [1, [3, 3, 3], [0.2, 0.2, 0.1]]) def test_generate_swarm_return_values(bounds, center): """Tests if generate_swarm() returns expected values""" - pos = P.generate_swarm(n_particles=2, dimensions=3, bounds=bounds, - center=center) + pos = P.generate_swarm( + n_particles=2, dimensions=3, bounds=bounds, center=center + ) if bounds is None: min_bounds, max_bounds = (0.0, 1.00) else: @@ -23,37 +26,46 @@ def test_generate_swarm_return_values(bounds, center): upper_bound = center * np.array(max_bounds) assert (pos <= upper_bound).all() and (pos >= lower_bound).all() + def test_generate_swarm_out_of_bounds(): """Tests if generate_swarm() raises ValueError when initialized with the wrong value""" - bounds = ([1,1,1], [5,5,5]) - init_pos = np.array([[-2,3,3], [6,8,1]]) + bounds = ([1, 1, 1], [5, 5, 5]) + init_pos = np.array([[-2, 3, 3], [6, 8, 1]]) with pytest.raises(ValueError): - pos = P.generate_swarm(n_particles=2, dimensions=3, bounds=bounds, - init_pos=init_pos) + pos = P.generate_swarm( + n_particles=2, dimensions=3, bounds=bounds, init_pos=init_pos + ) + -@pytest.mark.parametrize('binary', [False, True]) +@pytest.mark.parametrize("binary", [False, True]) def test_generate_discrete_binary_swarm(binary): """Tests if generate_discrete_swarm(binary=True) returns expected values""" dims = 3 - pos = P.generate_discrete_swarm(n_particles=2, dimensions=dims, binary=binary) + pos = P.generate_discrete_swarm( + n_particles=2, dimensions=dims, binary=binary + ) if binary: - assert len(np.unique(pos)) <= 2 # Might generate pure 0 or 1 + assert len(np.unique(pos)) <= 2 # Might generate pure 0 or 1 else: assert (np.max(pos, axis=1) == dims - 1).all() -@pytest.mark.parametrize('init_pos', [None, np.array([[4,2,1], [1,4,6]])]) + +@pytest.mark.parametrize("init_pos", [None, np.array([[4, 2, 1], [1, 4, 6]])]) def test_generate_discrete_swarm(init_pos): """Tests if init_pos actually sets the position properly""" dims = 3 - pos = P.generate_discrete_swarm(n_particles=2, dimensions=dims, init_pos=init_pos) + pos = P.generate_discrete_swarm( + n_particles=2, dimensions=dims, init_pos=init_pos + ) if init_pos is None: assert (np.max(pos, axis=1) == dims - 1).all() else: assert np.equal(pos, init_pos).all() -@pytest.mark.parametrize('clamp', [None, (0,1), (2,5), (1,6)]) + +@pytest.mark.parametrize("clamp", [None, (0, 1), (2, 5), (1, 6)]) def test_generate_velocity_return_values(clamp): """Tests if generate_velocity() returns expected values""" - min_clamp, max_clamp = (0,1) if clamp == None else clamp + min_clamp, max_clamp = (0, 1) if clamp == None else clamp velocity = P.generate_velocity(n_particles=2, dimensions=3, clamp=clamp) - assert (velocity <= max_clamp).all() and (velocity >= min_clamp).all() \ No newline at end of file + assert (velocity <= max_clamp).all() and (velocity >= min_clamp).all() diff --git a/tests/backend/test_operators.py b/tests/backend/test_operators.py index ccaa8517..7fdbe9aa 100644 --- a/tests/backend/test_operators.py +++ b/tests/backend/test_operators.py @@ -11,13 +11,14 @@ def test_compute_pbest_return_values(swarm): """Test if compute_pbest() gives the expected return values""" - expected_cost = np.array([1,2,2]) - expected_pos = np.array([[1,2,3], [4,5,6], [1,1,1]]) + expected_cost = np.array([1, 2, 2]) + expected_pos = np.array([[1, 2, 3], [4, 5, 6], [1, 1, 1]]) pos, cost = P.compute_pbest(swarm) assert (pos == expected_pos).all() assert (cost == expected_cost).all() -@pytest.mark.parametrize('clamp', [None, (0,1), (-1,1)]) + +@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) def test_compute_velocity_return_values(swarm, clamp): """Test if compute_velocity() gives the expected shape and range""" v = P.compute_velocity(swarm, clamp) @@ -25,8 +26,11 @@ def test_compute_velocity_return_values(swarm, clamp): if clamp is not None: assert (clamp[0] <= v).all() and (clamp[1] >= v).all() -@pytest.mark.parametrize('bounds', [None, ([-5,-5,-5],[5,5,5]), - ([-10, -10, -10],[10, 10, 10])]) + +@pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], +) def test_compute_position_return_values(swarm, bounds): """Test if compute_position() gives the expected shape and range""" p = P.compute_position(swarm, bounds) diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index fba52129..bccc1047 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -10,18 +10,18 @@ # Import from package from pyswarms.backend.swarms import Swarm + @pytest.fixture def swarm(): """A contrived instance of the Swarm class at a certain timestep""" attrs_at_t = { - 'position' : np.array([[5,5,5], [3,3,3], [1,1,1]]), - 'velocity' : np.array([[1,1,1], [1,1,1], [1,1,1]]), - 'current_cost' : np.array([2,2,2]), - 'pbest_cost' : np.array([1,2,3]), - 'pbest_pos' : np.array([[1,2,3], [4,5,6], [7,8,9]]), - 'best_cost' : 1, - 'best_pos' : np.array([1,1,1]), - 'options' : {'c1' : 0.5, 'c2': 1, 'w': 2} + "position": np.array([[5, 5, 5], [3, 3, 3], [1, 1, 1]]), + "velocity": np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]]), + "current_cost": np.array([2, 2, 2]), + "pbest_cost": np.array([1, 2, 3]), + "pbest_pos": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), + "best_cost": 1, + "best_pos": np.array([1, 1, 1]), + "options": {"c1": 0.5, "c2": 1, "w": 2}, } return Swarm(**attrs_at_t) - diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index bebf71e8..58306ab8 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -9,18 +9,19 @@ from pyswarms.backend.topology import Ring -@pytest.mark.parametrize('k', [1,2,3]) -@pytest.mark.parametrize('p', [1,2]) +@pytest.mark.parametrize("k", [1, 2, 3]) +@pytest.mark.parametrize("p", [1, 2]) def test_update_gbest_neighborhood(swarm, p, k): """Test if update_gbest_neighborhood gives the expected return values""" topology = Ring() pos, cost = topology.compute_gbest(swarm, p=p, k=k) - expected_pos = np.array([1,2,3]) + expected_pos = np.array([1, 2, 3]) expected_cost = 1 assert (pos == expected_pos).all() assert cost == expected_cost -@pytest.mark.parametrize('clamp', [None, (0,1), (-1,1)]) + +@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) def test_compute_velocity_return_values(swarm, clamp): """Test if compute_velocity() gives the expected shape and range""" topology = Ring() @@ -29,8 +30,11 @@ def test_compute_velocity_return_values(swarm, clamp): if clamp is not None: assert (clamp[0] <= v).all() and (clamp[1] >= v).all() -@pytest.mark.parametrize('bounds', [None, ([-5,-5,-5],[5,5,5]), - ([-10, -10, -10],[10, 10, 10])]) + +@pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], +) def test_compute_position_return_values(swarm, bounds): """Test if compute_position() gives the expected shape and range""" topology = Ring() diff --git a/tests/backend/topology/test_star.py b/tests/backend/topology/test_star.py index affd6770..71eca08c 100644 --- a/tests/backend/topology/test_star.py +++ b/tests/backend/topology/test_star.py @@ -13,12 +13,13 @@ def test_compute_gbest_return_values(swarm): """Test if compute_gbest() gives the expected return values""" topology = Star() expected_cost = 1 - expected_pos = np.array([1,2,3]) + expected_pos = np.array([1, 2, 3]) pos, cost = topology.compute_gbest(swarm) assert cost == expected_cost assert (pos == expected_pos).all() -@pytest.mark.parametrize('clamp', [None, (0,1), (-1,1)]) + +@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) def test_compute_velocity_return_values(swarm, clamp): """Test if compute_velocity() gives the expected shape and range""" topology = Star() @@ -27,12 +28,15 @@ def test_compute_velocity_return_values(swarm, clamp): if clamp is not None: assert (clamp[0] <= v).all() and (clamp[1] >= v).all() -@pytest.mark.parametrize('bounds', [None, ([-5,-5,-5],[5,5,5]), - ([-10, -10, -10],[10, 10, 10])]) + +@pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], +) def test_compute_position_return_values(swarm, bounds): """Test if compute_position() gives the expected shape and range""" topology = Star() p = topology.compute_position(swarm, bounds) assert p.shape == swarm.velocity.shape if bounds is not None: - assert (bounds[0] <= p).all() and (bounds[1] >= p).all() \ No newline at end of file + assert (bounds[0] <= p).all() and (bounds[1] >= p).all() diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py index 250ae3d7..4bf9f399 100644 --- a/tests/optimizers/conftest.py +++ b/tests/optimizers/conftest.py @@ -8,63 +8,70 @@ import numpy as np # Import from package -from pyswarms.single import (GlobalBestPSO, LocalBestPSO) +from pyswarms.single import GlobalBestPSO, LocalBestPSO from pyswarms.discrete import BinaryPSO from pyswarms.utils.functions.single_obj import sphere_func -@pytest.fixture(scope='module') + +@pytest.fixture(scope="module") def gbest_history(): """Returns a GlobalBestPSO instance run for 1000 iterations for checking history""" - pso = GlobalBestPSO(10, 2, {'c1': 0.5, 'c2': 0.7, 'w': 0.5}) + pso = GlobalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}) pso.optimize(sphere_func, 1000, verbose=0) return pso -@pytest.fixture(scope='module') + +@pytest.fixture(scope="module") def gbest_reset(): """Returns a GlobalBestPSO instance that has been run and reset to check default value""" - pso = GlobalBestPSO(10, 2, {'c1': 0.5, 'c2': 0.7, 'w': 0.5}) + pso = GlobalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}) pso.optimize(sphere_func, 10, verbose=0) pso.reset() return pso -@pytest.fixture(scope='module') + +@pytest.fixture(scope="module") def lbest_history(): """Returns a LocalBestPSO instance run for 1000 iterations for checking history""" - pso = LocalBestPSO(10, 2, {'c1': 0.5, 'c2': 0.7, 'w': 0.5, 'k':2, 'p':2}) + pso = LocalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) pso.optimize(sphere_func, 1000, verbose=0) return pso -@pytest.fixture(scope='module') + +@pytest.fixture(scope="module") def lbest_reset(): """Returns a LocalBestPSO instance that has been run and reset to check default value""" - pso = LocalBestPSO(10, 2, {'c1': 0.5, 'c2': 0.7, 'w': 0.5, 'k': 2, 'p': 2}) + pso = LocalBestPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) pso.optimize(sphere_func, 10, verbose=0) pso.reset() return pso -@pytest.fixture(scope='module') + +@pytest.fixture(scope="module") def binary_history(): """Returns a BinaryPSO instance run for 1000 iterations for checking history""" - pso = BinaryPSO(10, 2, {'c1': 0.5, 'c2': 0.7, 'w': 0.5, 'k':2, 'p':2}) + pso = BinaryPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) pso.optimize(sphere_func, 1000, verbose=0) return pso -@pytest.fixture(scope='module') + +@pytest.fixture(scope="module") def binary_reset(): """Returns a BinaryPSO instance that has been run and reset to check default value""" - pso = BinaryPSO(10, 2, {'c1': 0.5, 'c2': 0.7, 'w': 0.5, 'k': 2, 'p': 2}) + pso = BinaryPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}) pso.optimize(sphere_func, 10, verbose=0) pso.reset() return pso + @pytest.fixture def options(): """Default options dictionary for most PSO use-cases""" - options_ = {'c1':0.5, 'c2':0.7, 'w':0.5, 'k':2, 'p':2} - return options_ \ No newline at end of file + options_ = {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2} + return options_ diff --git a/tests/optimizers/test_binary.py b/tests/optimizers/test_binary.py index 572de45b..acf533b6 100644 --- a/tests/optimizers/test_binary.py +++ b/tests/optimizers/test_binary.py @@ -8,62 +8,79 @@ # Import from package from pyswarms.discrete import BinaryPSO -@pytest.mark.parametrize('options', [ - {'c2':0.7, 'w':0.5, 'k': 2, 'p': 2}, - {'c1':0.5, 'w':0.5, 'k': 2, 'p': 2}, - {'c1':0.5, 'c2':0.7, 'k': 2, 'p': 2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'p': 2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k': 2} -]) + +@pytest.mark.parametrize( + "options", + [ + {"c2": 0.7, "w": 0.5, "k": 2, "p": 2}, + {"c1": 0.5, "w": 0.5, "k": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "k": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2}, + ], +) def test_keyword_exception(options): """Tests if exceptions are thrown when keywords are missing""" with pytest.raises(KeyError): BinaryPSO(5, 2, options) -@pytest.mark.parametrize('options', [ - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k':-1, 'p':2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k':6, 'p':2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k':2, 'p':5} -]) + +@pytest.mark.parametrize( + "options", + [ + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": -1, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 6, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 5}, + ], +) def test_invalid_k_or_p_values(options): """Tests if exception is thrown when passing an invalid value for k or p""" with pytest.raises(ValueError): BinaryPSO(5, 2, options) -@pytest.mark.parametrize('velocity_clamp', [[1, 3],np.array([1, 3])]) + +@pytest.mark.parametrize("velocity_clamp", [[1, 3], np.array([1, 3])]) def test_vclamp_type_exception(velocity_clamp, options): """Tests if exception is raised when velocity_clamp type is not a tuple""" with pytest.raises(TypeError): BinaryPSO(5, 2, velocity_clamp=velocity_clamp, options=options) -@pytest.mark.parametrize('velocity_clamp', [(1,1,1), (2,3,1)]) + +@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) def test_vclamp_shape_exception(velocity_clamp, options): """Tests if exception is raised when velocity_clamp's size is not equal to 2""" with pytest.raises(IndexError): BinaryPSO(5, 2, velocity_clamp=velocity_clamp, options=options) -@pytest.mark.parametrize('velocity_clamp', [(3,2),(10,8)]) + +@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) def test_vclamp_maxmin_exception(velocity_clamp, options): """Tests if the max velocity_clamp is less than min velocity_clamp and vice-versa""" with pytest.raises(ValueError): BinaryPSO(5, 2, velocity_clamp=velocity_clamp, options=options) + def test_reset_default_values(binary_reset): """Tests if best cost and best pos are set properly when the reset() method is called""" assert binary_reset.swarm.best_cost == np.inf assert set(binary_reset.swarm.best_pos) == set(np.array([])) -@pytest.mark.parametrize('history, expected_shape', [ - ('cost_history', (1000,)), - ('mean_pbest_history', (1000,)), - ('mean_neighbor_history',(1000,)), - ('pos_history',(1000, 10, 2)), - ('velocity_history',(1000, 10, 2))]) + +@pytest.mark.parametrize( + "history, expected_shape", + [ + ("cost_history", (1000,)), + ("mean_pbest_history", (1000,)), + ("mean_neighbor_history", (1000,)), + ("pos_history", (1000, 10, 2)), + ("velocity_history", (1000, 10, 2)), + ], +) def test_training_history_shape(binary_history, history, expected_shape): """Test if training histories are of expected shape""" pso = vars(binary_history) diff --git a/tests/optimizers/test_global_best.py b/tests/optimizers/test_global_best.py index de212d40..434f9770 100644 --- a/tests/optimizers/test_global_best.py +++ b/tests/optimizers/test_global_best.py @@ -9,82 +9,103 @@ from pyswarms.single import GlobalBestPSO from pyswarms.utils.functions.single_obj import sphere_func -@pytest.mark.parametrize('options', [ - {'c2':0.7, 'w':0.5}, - {'c1':0.5, 'w':0.5}, - {'c1':0.5, 'c2':0.7} -]) + +@pytest.mark.parametrize( + "options", + [{"c2": 0.7, "w": 0.5}, {"c1": 0.5, "w": 0.5}, {"c1": 0.5, "c2": 0.7}], +) def test_keyword_exception(options): """Tests if exceptions are thrown when keywords are missing""" with pytest.raises(KeyError): GlobalBestPSO(5, 2, options) -@pytest.mark.parametrize('bounds', [ - tuple(np.array([-5,-5])), - (np.array([-5,-5,-5]), np.array([5,5])), - (np.array([-5,-5,-5]), np.array([5,5,5])) -]) + +@pytest.mark.parametrize( + "bounds", + [ + tuple(np.array([-5, -5])), + (np.array([-5, -5, -5]), np.array([5, 5])), + (np.array([-5, -5, -5]), np.array([5, 5, 5])), + ], +) def test_bounds_size_exception(bounds, options): """Tests if exceptions are raised when bound sizes are wrong""" with pytest.raises(IndexError): GlobalBestPSO(5, 2, options=options, bounds=bounds) -@pytest.mark.parametrize('bounds', [ - (np.array([5,5]), np.array([-5,-5])), - (np.array([5,-5]), np.array([-5,5])) -]) + +@pytest.mark.parametrize( + "bounds", + [ + (np.array([5, 5]), np.array([-5, -5])), + (np.array([5, -5]), np.array([-5, 5])), + ], +) def test_bounds_maxmin_exception(bounds, options): """Tests if the max bounds is less than min bounds and vice-versa""" with pytest.raises(ValueError): GlobalBestPSO(5, 2, options=options, bounds=bounds) -@pytest.mark.parametrize('bounds',[ - [np.array([-5, -5]), np.array([5,5])], - np.array([np.array([-5, -5]), np.array([5, 5])]) -]) + +@pytest.mark.parametrize( + "bounds", + [ + [np.array([-5, -5]), np.array([5, 5])], + np.array([np.array([-5, -5]), np.array([5, 5])]), + ], +) def test_bound_type_exception(bounds, options): """Tests if exception is raised when bound type is not a tuple""" with pytest.raises(TypeError): - GlobalBestPSO(5,2, options=options, bounds=bounds) + GlobalBestPSO(5, 2, options=options, bounds=bounds) + -@pytest.mark.parametrize('velocity_clamp', [(1,1,1), (2,3,1)]) +@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) def test_vclamp_shape_exception(velocity_clamp, options): """Tests if exception is raised when velocity_clamp's size is not equal to 2""" with pytest.raises(IndexError): GlobalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) -@pytest.mark.parametrize('velocity_clamp', [(3,2),(10,8)]) + +@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) def test_vclamp_maxmin_exception(velocity_clamp, options): """Tests if the max velocity_clamp is less than min velocity_clamp and vice-versa""" with pytest.raises(ValueError): GlobalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) -@pytest.mark.parametrize('err, center', - [(IndexError, [1.5, 3.2, 2.5])]) + +@pytest.mark.parametrize("err, center", [(IndexError, [1.5, 3.2, 2.5])]) def test_center_exception(err, center, options): """Tests if exception is thrown when center is not a list or of different shape""" with pytest.raises(err): GlobalBestPSO(5, 2, center=center, options=options) + def test_reset_default_values(gbest_reset): """Tests if best cost and best pos are set properly when the reset() method is called""" assert gbest_reset.swarm.best_cost == np.inf assert set(gbest_reset.swarm.best_pos) == set(np.array([])) -@pytest.mark.parametrize('history, expected_shape', [ - ('cost_history', (1000,)), - ('mean_pbest_history', (1000,)), - ('mean_neighbor_history',(1000,)), - ('pos_history',(1000, 10, 2)), - ('velocity_history',(1000, 10, 2))]) + +@pytest.mark.parametrize( + "history, expected_shape", + [ + ("cost_history", (1000,)), + ("mean_pbest_history", (1000,)), + ("mean_neighbor_history", (1000,)), + ("pos_history", (1000, 10, 2)), + ("velocity_history", (1000, 10, 2)), + ], +) def test_training_history_shape(gbest_history, history, expected_shape): """Test if training histories are of expected shape""" pso = vars(gbest_history) assert np.array(pso[history]).shape == expected_shape + def test_ftol_effect(options): """Test if setting the ftol breaks the optimization process accodingly""" pso = GlobalBestPSO(10, 2, options=options, ftol=1e-1) diff --git a/tests/optimizers/test_local_best.py b/tests/optimizers/test_local_best.py index df384469..3d3315fa 100644 --- a/tests/optimizers/test_local_best.py +++ b/tests/optimizers/test_local_best.py @@ -9,95 +9,124 @@ from pyswarms.single import LocalBestPSO from pyswarms.utils.functions.single_obj import sphere_func -@pytest.mark.parametrize('options', [ - {'c2':0.7, 'w':0.5, 'k': 2, 'p': 2}, - {'c1':0.5, 'w':0.5, 'k': 2, 'p': 2}, - {'c1':0.5, 'c2':0.7, 'k': 2, 'p': 2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'p': 2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k': 2} -]) + +@pytest.mark.parametrize( + "options", + [ + {"c2": 0.7, "w": 0.5, "k": 2, "p": 2}, + {"c1": 0.5, "w": 0.5, "k": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "k": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2}, + ], +) def test_keyword_exception(options): """Tests if exceptions are thrown when keywords are missing""" with pytest.raises(KeyError): LocalBestPSO(5, 2, options) -@pytest.mark.parametrize('options', [ - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k':-1, 'p':2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k':6, 'p':2}, - {'c1':0.5, 'c2':0.7, 'w':0.5, 'k':2, 'p':5} -]) + +@pytest.mark.parametrize( + "options", + [ + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": -1, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 6, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 5}, + ], +) def test_invalid_k_or_p_values(options): """Tests if exception is thrown when passing an invalid value for k or p""" with pytest.raises(ValueError): LocalBestPSO(5, 2, options) -@pytest.mark.parametrize('bounds', [ - tuple(np.array([-5,-5])), - (np.array([-5,-5,-5]), np.array([5,5])), - (np.array([-5,-5,-5]), np.array([5,5,5])) -]) + +@pytest.mark.parametrize( + "bounds", + [ + tuple(np.array([-5, -5])), + (np.array([-5, -5, -5]), np.array([5, 5])), + (np.array([-5, -5, -5]), np.array([5, 5, 5])), + ], +) def test_bounds_size_exception(bounds, options): """Tests if exceptions are raised when bound sizes are wrong""" with pytest.raises(IndexError): LocalBestPSO(5, 2, options=options, bounds=bounds) -@pytest.mark.parametrize('bounds', [ - (np.array([5,5]), np.array([-5,-5])), - (np.array([5,-5]), np.array([-5,5])) -]) + +@pytest.mark.parametrize( + "bounds", + [ + (np.array([5, 5]), np.array([-5, -5])), + (np.array([5, -5]), np.array([-5, 5])), + ], +) def test_bounds_maxmin_exception(bounds, options): """Tests if the max bounds is less than min bounds and vice-versa""" with pytest.raises(ValueError): LocalBestPSO(5, 2, options=options, bounds=bounds) -@pytest.mark.parametrize('bounds',[ - [np.array([-5, -5]), np.array([5,5])], - np.array([np.array([-5, -5]), np.array([5, 5])]) -]) + +@pytest.mark.parametrize( + "bounds", + [ + [np.array([-5, -5]), np.array([5, 5])], + np.array([np.array([-5, -5]), np.array([5, 5])]), + ], +) def test_bound_type_exception(bounds, options): """Tests if exception is raised when bound type is not a tuple""" with pytest.raises(TypeError): - LocalBestPSO(5,2, options=options, bounds=bounds) + LocalBestPSO(5, 2, options=options, bounds=bounds) -@pytest.mark.parametrize('velocity_clamp', [(1,1,1), (2,3,1)]) + +@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) def test_vclamp_shape_exception(velocity_clamp, options): """Tests if exception is raised when velocity_clamp's size is not equal to 2""" with pytest.raises(IndexError): LocalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) -@pytest.mark.parametrize('velocity_clamp', [(3,2),(10,8)]) + +@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) def test_vclamp_maxmin_exception(velocity_clamp, options): """Tests if the max velocity_clamp is less than min velocity_clamp and vice-versa""" with pytest.raises(ValueError): LocalBestPSO(5, 2, velocity_clamp=velocity_clamp, options=options) -@pytest.mark.parametrize('err, center', - [(IndexError, [1.5, 3.2, 2.5])]) + +@pytest.mark.parametrize("err, center", [(IndexError, [1.5, 3.2, 2.5])]) def test_center_exception(err, center, options): """Tests if exception is thrown when center is not a list or of different shape""" with pytest.raises(err): LocalBestPSO(5, 2, center=center, options=options) + def test_reset_default_values(lbest_reset): """Tests if best cost and best pos are set properly when the reset() method is called""" assert lbest_reset.swarm.best_cost == np.inf assert set(lbest_reset.swarm.best_pos) == set(np.array([])) -@pytest.mark.parametrize('history, expected_shape', [ - ('cost_history', (1000,)), - ('mean_pbest_history', (1000,)), - ('mean_neighbor_history',(1000,)), - ('pos_history',(1000, 10, 2)), - ('velocity_history',(1000, 10, 2))]) + +@pytest.mark.parametrize( + "history, expected_shape", + [ + ("cost_history", (1000,)), + ("mean_pbest_history", (1000,)), + ("mean_neighbor_history", (1000,)), + ("pos_history", (1000, 10, 2)), + ("velocity_history", (1000, 10, 2)), + ], +) def test_training_history_shape(lbest_history, history, expected_shape): """Test if training histories are of expected shape""" pso = vars(lbest_history) assert np.array(pso[history]).shape == expected_shape + def test_ftol_effect(options): """Test if setting the ftol breaks the optimization process accodingly""" pso = LocalBestPSO(10, 2, options=options, ftol=1e-1) diff --git a/tests/utils/environments/conftest.py b/tests/utils/environments/conftest.py index 022d5674..1f548e9b 100644 --- a/tests/utils/environments/conftest.py +++ b/tests/utils/environments/conftest.py @@ -9,31 +9,37 @@ from mock import Mock import matplotlib as mpl -if os.environ.get('DISPLAY','') == '': - mpl.use('Agg') +if os.environ.get("DISPLAY", "") == "": + mpl.use("Agg") # Import from package from pyswarms.single import GlobalBestPSO from pyswarms.utils.environments import PlotEnvironment from pyswarms.utils.functions.single_obj import sphere_func + @pytest.fixture def mock_pso(): """Returns a function that mocks a PSO class with missing attributes""" + def _mock_pso(index): class_methods = [ - 'cost_history', - 'pos_history', - 'velocity_history', - 'optimize', - 'reset' + "cost_history", + "pos_history", + "velocity_history", + "optimize", + "reset", + ] + get_specs = lambda idx: [ + x for i, x in enumerate(class_methods) if i != idx ] - get_specs = lambda idx: [x for i,x in enumerate(class_methods) if i != idx] return Mock(spec=get_specs(index)) + return _mock_pso + @pytest.fixture def plot_environment(): """Returns a PlotEnvironment instance""" - optimizer = GlobalBestPSO(10, 3, options={'c1': 0.5, 'c2': 0.3, 'w': 0.9}) + optimizer = GlobalBestPSO(10, 3, options={"c1": 0.5, "c2": 0.3, "w": 0.9}) return PlotEnvironment(optimizer, sphere_func, 1000) diff --git a/tests/utils/environments/test_plot_environment.py b/tests/utils/environments/test_plot_environment.py index aeaaec1e..600a5039 100644 --- a/tests/utils/environments/test_plot_environment.py +++ b/tests/utils/environments/test_plot_environment.py @@ -6,8 +6,8 @@ import pytest import matplotlib as mpl -if os.environ.get('DISPLAY','') == '': - mpl.use('Agg') +if os.environ.get("DISPLAY", "") == "": + mpl.use("Agg") from matplotlib.axes._subplots import SubplotBase from matplotlib.animation import FuncAnimation @@ -17,31 +17,35 @@ from pyswarms.utils.functions.single_obj import sphere_func class_methods = [ - 'cost_history', - 'pos_history', - 'velocity_history', - 'optimize', - 'reset' + "cost_history", + "pos_history", + "velocity_history", + "optimize", + "reset", ] -@pytest.mark.parametrize('attributes', [i for i in enumerate(class_methods)]) + +@pytest.mark.parametrize("attributes", [i for i in enumerate(class_methods)]) def test_getters_pso(mock_pso, attributes): """Tests an instance of the PSO class and should raise an exception when the class has missing attributes""" idx, _ = attributes with pytest.raises(AttributeError): - m = mock_pso(idx) - PlotEnvironment(m, sphere_func, 100) + m = mock_pso(idx) + PlotEnvironment(m, sphere_func, 100) + @pytest.mark.xfail def test_plot_cost_return_type(plot_environment): """Tests if plot_cost() returns a SubplotBase instance""" - assert isinstance(plot_environment.plot_cost(),SubplotBase) + assert isinstance(plot_environment.plot_cost(), SubplotBase) + @pytest.mark.xfail def test_plot2D_return_type(plot_environment): """Test if plot_particles2D() returns a FuncAnimation instance""" assert isinstance(plot_environment.plot_particles2D(), FuncAnimation) + @pytest.mark.xfail def test_plot3D_return_type(plot_environment): """Test if plot_particles3D() returns a FuncAnimation instance""" diff --git a/tests/utils/functions/conftest.py b/tests/utils/functions/conftest.py index a369ca25..ec3e3b16 100644 --- a/tests/utils/functions/conftest.py +++ b/tests/utils/functions/conftest.py @@ -7,36 +7,43 @@ import pytest import numpy as np + @pytest.fixture def outbound(): """Returns a function that generates a matrix out of bounds a given range""" + def _outbound(low, high, size, tol=100, nums=100): """Generates a matrix that is out of bounds""" - low_end = - np.random.uniform(tol, low, (nums,)) + low_end = -np.random.uniform(tol, low, (nums,)) high_end = np.random.uniform(tol, high, (nums,)) choices = np.hstack([low_end, high_end]) return np.random.choice(choices, size=size, replace=True) + return _outbound + @pytest.fixture def outdim(): """Returns a matrix of bad shape (3D matrix)""" - return np.zeros(shape=(3,3)) + return np.zeros(shape=(3, 3)) + @pytest.fixture def common_minima(): """Returns a zero-matrix with a common-minima for most objective functions""" - return np.zeros(shape=(3,2)) + return np.zeros(shape=(3, 2)) + @pytest.fixture def common_minima2(): """Returns a one-matrix with a common-minima for most objective functions""" - return np.ones(shape=(3,2)) + return np.ones(shape=(3, 2)) + @pytest.fixture def targetdim(): """Returns a baseline target dimension for most objective functions""" - return (3, ) \ No newline at end of file + return (3,) diff --git a/tests/utils/functions/test_singleobj_bounds.py b/tests/utils/functions/test_singleobj_bounds.py index 62daa069..d89c5535 100644 --- a/tests/utils/functions/test_singleobj_bounds.py +++ b/tests/utils/functions/test_singleobj_bounds.py @@ -11,74 +11,87 @@ # Import from package from pyswarms.utils.functions import single_obj as fx -Bounds = namedtuple('Bounds', 'low high') +Bounds = namedtuple("Bounds", "low high") b = { # Define all bounds here - 'rastrigin' : Bounds(low=-5.12, high=5.12), - 'ackley' : Bounds(low=-32, high=32), - 'beale' : Bounds(low=-4.5, high=4.5), - 'goldstein' : Bounds(low=-2, high=-2), - 'booth' : Bounds(low=-10, high=10), - 'matyas' : Bounds(low=-10, high=10), - 'levi' : Bounds(low=-10, high=10), - 'schaffer2' : Bounds(low=-100, high=100) - + "rastrigin": Bounds(low=-5.12, high=5.12), + "ackley": Bounds(low=-32, high=32), + "beale": Bounds(low=-4.5, high=4.5), + "goldstein": Bounds(low=-2, high=-2), + "booth": Bounds(low=-10, high=10), + "matyas": Bounds(low=-10, high=10), + "levi": Bounds(low=-10, high=10), + "schaffer2": Bounds(low=-100, high=100), } + def test_rastrigin_bound_fail(outbound): """Test rastrigin bound exception""" with pytest.raises(ValueError): - x = outbound(b['rastrigin'].low, b['rastrigin'].high, size=(3,2)) + x = outbound(b["rastrigin"].low, b["rastrigin"].high, size=(3, 2)) fx.rastrigin_func(x) + def test_ackley_bound_fail(outbound): """Test ackley bound exception""" with pytest.raises(ValueError): - x = outbound(b['ackley'].low, b['ackley'].high, size=(3,2)) + x = outbound(b["ackley"].low, b["ackley"].high, size=(3, 2)) fx.ackley_func(x) + def test_beale_bound_fail(outbound): """Test beale bound exception""" with pytest.raises(ValueError): - x = outbound(b['beale'].low, b['beale'].high, size=(3,2)) + x = outbound(b["beale"].low, b["beale"].high, size=(3, 2)) fx.beale_func(x) + def test_goldstein_bound_fail(outbound): """Test goldstein bound exception""" with pytest.raises(ValueError): - x = outbound(b['goldstein'].low, b['goldstein'].high, size=(3,2)) + x = outbound(b["goldstein"].low, b["goldstein"].high, size=(3, 2)) fx.goldstein_func(x) + def test_booth_bound_fail(outbound): """Test booth bound exception""" with pytest.raises(ValueError): - x = outbound(b['booth'].low, b['booth'].high, size=(3,2)) + x = outbound(b["booth"].low, b["booth"].high, size=(3, 2)) fx.booth_func(x) -@pytest.mark.parametrize("x", [ - -np.random.uniform(15.001, 100, (3,2)), - np.random.uniform(-5.001, -3.001, (3,2)), - np.random.uniform(-3.001, -100, (3,2)) -]) + +@pytest.mark.parametrize( + "x", + [ + -np.random.uniform(15.001, 100, (3, 2)), + np.random.uniform(-5.001, -3.001, (3, 2)), + np.random.uniform(-3.001, -100, (3, 2)), + ], +) def test_bukin6_bound_fail(x): """Test bukin6 bound exception""" with pytest.raises(ValueError): fx.bukin6_func(x) + def test_matyas_bound_fail(outbound): """Test matyas bound exception""" with pytest.raises(ValueError): - x = outbound(b['matyas'].low, b['matyas'].high, size=(3,2)) + x = outbound(b["matyas"].low, b["matyas"].high, size=(3, 2)) fx.matyas_func(x) + def test_levi_bound_fail(outbound): """Test levi bound exception""" with pytest.raises(ValueError): - x = outbound(b['levi'].low, b['levi'].high, size=(3,2)) + x = outbound(b["levi"].low, b["levi"].high, size=(3, 2)) fx.levi_func(x) + def test_schaffer2_bound_fail(outbound): """Test schaffer2 bound exception""" with pytest.raises(ValueError): - x = outbound(b['schaffer2'].low, b['schaffer2'].high, tol=200, size=(3,2)) - fx.schaffer2_func(x) \ No newline at end of file + x = outbound( + b["schaffer2"].low, b["schaffer2"].high, tol=200, size=(3, 2) + ) + fx.schaffer2_func(x) diff --git a/tests/utils/functions/test_singleobj_dims.py b/tests/utils/functions/test_singleobj_dims.py index 9258902f..9a4f1d76 100644 --- a/tests/utils/functions/test_singleobj_dims.py +++ b/tests/utils/functions/test_singleobj_dims.py @@ -11,37 +11,44 @@ # Import from package from pyswarms.utils.functions import single_obj as fx + def test_beale_dim_fail(outdim): """Test beale dim exception""" with pytest.raises(IndexError): fx.beale_func(outdim) + def test_goldstein_dim_fail(outdim): """Test goldstein dim exception""" with pytest.raises(IndexError): fx.goldstein_func(outdim) + def test_booth_dim_fail(outdim): """Test booth dim exception""" with pytest.raises(IndexError): fx.booth_func(outdim) + def test_bukin6_dim_fail(outdim): """Test bukin6 dim exception""" with pytest.raises(IndexError): fx.bukin6_func(outdim) + def test_matyas_dim_fail(outdim): """Test matyas dim exception""" with pytest.raises(IndexError): fx.matyas_func(outdim) + def test_levi_dim_fail(outdim): """Test levi dim exception""" with pytest.raises(IndexError): fx.levi_func(outdim) + def test_schaffer2_dim_fail(outdim): """Test schaffer2 dim exception""" with pytest.raises(IndexError): - fx.schaffer2_func(outdim) \ No newline at end of file + fx.schaffer2_func(outdim) diff --git a/tests/utils/functions/test_singleobj_return.py b/tests/utils/functions/test_singleobj_return.py index 5fc3b819..c4e3f28b 100644 --- a/tests/utils/functions/test_singleobj_return.py +++ b/tests/utils/functions/test_singleobj_return.py @@ -9,46 +9,67 @@ # Import from package from pyswarms.utils.functions import single_obj as fx + def test_sphere_output(common_minima): """Tests sphere function output.""" assert np.array_equal(fx.sphere_func(common_minima), np.zeros((3,))) + def test_rastrigin_output(common_minima): """Tests rastrigin function output.""" assert np.array_equal(fx.rastrigin_func(common_minima), np.zeros(3)) + def test_ackley_output(common_minima): """Tests ackley function output.""" assert np.isclose(fx.ackley_func(common_minima), np.zeros(3)).all() + def test_rosenbrock_output(common_minima2): """Tests rosenbrock function output.""" - assert np.array_equal(fx.rosenbrock_func(common_minima2).all(),np.zeros(3).all()) + assert np.array_equal( + fx.rosenbrock_func(common_minima2).all(), np.zeros(3).all() + ) + def test_beale_output(common_minima2): """Tests beale function output.""" - assert np.isclose(fx.beale_func([3, 0.5] * common_minima2), np.zeros(3)).all() + assert np.isclose( + fx.beale_func([3, 0.5] * common_minima2), np.zeros(3) + ).all() + def test_goldstein_output(common_minima2): """Tests goldstein-price function output.""" - assert np.isclose(fx.goldstein_func([0, -1] * common_minima2), (3 * np.ones(3))).all() + assert np.isclose( + fx.goldstein_func([0, -1] * common_minima2), (3 * np.ones(3)) + ).all() + def test_booth_output(common_minima2): """Test booth function output.""" - assert np.isclose(fx.booth_func([1, 3] * common_minima2), np.zeros(3)).all() + assert np.isclose( + fx.booth_func([1, 3] * common_minima2), np.zeros(3) + ).all() + def test_bukin6_output(common_minima2): """Test bukin function output.""" - assert np.isclose(fx.bukin6_func([-10, 1] * common_minima2), np.zeros(3)).all() + assert np.isclose( + fx.bukin6_func([-10, 1] * common_minima2), np.zeros(3) + ).all() + def test_bukin6_output(common_minima): """Test bukin function output.""" assert np.isclose(fx.matyas_func(common_minima), np.zeros(3)).all() + def test_levi_output(common_minima2): """Test levi function output.""" assert np.isclose(fx.levi_func(common_minima2), np.zeros(3)).all() + def test_schaffer2_output(common_minima): """Test schaffer2 function output.""" - assert np.isclose(fx.schaffer2_func(common_minima), np.zeros(3)).all() \ No newline at end of file + assert np.isclose(fx.schaffer2_func(common_minima), np.zeros(3)).all() diff --git a/tests/utils/functions/test_singleobj_returndims.py b/tests/utils/functions/test_singleobj_returndims.py index cbaa6d17..0051c4d5 100644 --- a/tests/utils/functions/test_singleobj_returndims.py +++ b/tests/utils/functions/test_singleobj_returndims.py @@ -9,42 +9,52 @@ # Import from package from pyswarms.utils.functions import single_obj as fx + def test_sphere_output_size(common_minima, targetdim): """Tests sphere output size.""" assert fx.sphere_func(common_minima).shape == targetdim + def test_rastrigin_output_size(common_minima, targetdim): """Tests rastrigin output size.""" assert fx.rastrigin_func(common_minima).shape == targetdim + def test_ackley_output_size(common_minima, targetdim): """Tests ackley output size.""" assert fx.ackley_func(common_minima).shape == targetdim + def test_rosenbrock_output_size(common_minima, targetdim): """Tests rosenbrock output size.""" assert fx.rosenbrock_func(common_minima).shape == targetdim + def test_beale_output_size(common_minima, targetdim): """Tests beale output size.""" assert fx.beale_func(common_minima).shape == targetdim + def test_goldstein_output_size(common_minima, targetdim): """Test goldstein output size.""" assert fx.goldstein_func(common_minima).shape == targetdim + def test_booth_output_size(common_minima, targetdim): """Test booth output size.""" assert fx.booth_func(common_minima).shape == targetdim + def test_bukin6_output_size(common_minima2, targetdim): """Test bukin6 output size.""" - assert fx.bukin6_func([-10,0] * common_minima2).shape == targetdim + assert fx.bukin6_func([-10, 0] * common_minima2).shape == targetdim + def test_levi_output_size(common_minima, targetdim): """Test levi output size.""" assert fx.levi_func(common_minima).shape == targetdim + def test_schaffer2_output_size(common_minima, targetdim): """Test schaffer2 output size.""" assert fx.schaffer2_func(common_minima).shape == targetdim diff --git a/tests/utils/plotters/conftest.py b/tests/utils/plotters/conftest.py index d109b893..6b8da6e4 100644 --- a/tests/utils/plotters/conftest.py +++ b/tests/utils/plotters/conftest.py @@ -10,28 +10,31 @@ from mock import Mock import matplotlib as mpl -if os.environ.get('DISPLAY','') == '': - mpl.use('Agg') +if os.environ.get("DISPLAY", "") == "": + mpl.use("Agg") # Import from package from pyswarms.single import GlobalBestPSO from pyswarms.utils.functions.single_obj import sphere_func from pyswarms.utils.plotters.formatters import Mesher + @pytest.fixture def trained_optimizer(): """Returns a trained optimizer instance with 100 iterations""" - options = {'c1':0.5, 'c2':0.3, 'w':0.9} + options = {"c1": 0.5, "c2": 0.3, "w": 0.9} optimizer = GlobalBestPSO(n_particles=10, dimensions=2, options=options) optimizer.optimize(sphere_func, iters=100) return optimizer + @pytest.fixture def pos_history(): """Returns a list containing a swarms' position history""" return np.random.uniform(size=(10, 5, 2)) + @pytest.fixture def mesher(): """A Mesher instance with sphere function and delta=0.1""" - return Mesher(func=sphere_func, delta=0.1) \ No newline at end of file + return Mesher(func=sphere_func, delta=0.1) diff --git a/tests/utils/plotters/test_plotters.py b/tests/utils/plotters/test_plotters.py index 3db3cb84..668129d4 100644 --- a/tests/utils/plotters/test_plotters.py +++ b/tests/utils/plotters/test_plotters.py @@ -6,50 +6,59 @@ import pytest import matplotlib as mpl -if os.environ.get('DISPLAY','') == '': - mpl.use('Agg') +if os.environ.get("DISPLAY", "") == "": + mpl.use("Agg") from matplotlib.axes._subplots import SubplotBase from matplotlib.animation import FuncAnimation # Import from package -from pyswarms.utils.plotters import (plot_cost_history, - plot_contour, - plot_surface) +from pyswarms.utils.plotters import ( + plot_cost_history, + plot_contour, + plot_surface, +) -from pyswarms.utils.plotters.plotters import (_mesh, _animate) +from pyswarms.utils.plotters.plotters import _mesh, _animate from pyswarms.utils.plotters.formatters import Mesher -@pytest.mark.parametrize('history', ['cost_history', 'mean_neighbor_history', - 'mean_pbest_history']) + +@pytest.mark.parametrize( + "history", ["cost_history", "mean_neighbor_history", "mean_pbest_history"] +) def test_plot_cost_history_return_type(trained_optimizer, history): """Tests if plot_cost_history() returns a SubplotBase instance""" opt_params = vars(trained_optimizer) plot = plot_cost_history(opt_params[history]) assert isinstance(plot, SubplotBase) -@pytest.mark.parametrize('bad_values', [2, 43.14]) + +@pytest.mark.parametrize("bad_values", [2, 43.14]) def test_plot_cost_history_error(bad_values): """Tests if plot_cost_history() raises an error given bad values""" with pytest.raises(TypeError): plot_cost_history(bad_values) + def test_plot_contour_return_type(pos_history): """Tests if the animation function returns the expected type""" assert isinstance(plot_contour(pos_history), FuncAnimation) + def test_plot_surface_return_type(pos_history): """Tests if the animation function returns the expected type""" assert isinstance(plot_surface(pos_history), FuncAnimation) + def test_mesh_hidden_function_shape(mesher): """Tests if the hidden _mesh() function returns the expected shape""" xx, yy, zz = _mesh(mesher) - assert (xx.shape == yy.shape == zz.shape == (20,20)) + assert xx.shape == yy.shape == zz.shape == (20, 20) + def test_animate_hidden_function_type(pos_history): """Tests if the hidden _animate() function returns the expected type""" - fig, ax = mpl.pyplot.subplots(1,1) + fig, ax = mpl.pyplot.subplots(1, 1) ax = mpl.pyplot.scatter(x=[], y=[]) return_plot = _animate(i=1, data=pos_history, plot=ax) - assert isinstance(return_plot, tuple) \ No newline at end of file + assert isinstance(return_plot, tuple) diff --git a/tests/utils/search/conftest.py b/tests/utils/search/conftest.py index dcc31b40..8ef81ce9 100644 --- a/tests/utils/search/conftest.py +++ b/tests/utils/search/conftest.py @@ -13,45 +13,83 @@ from pyswarms.single import LocalBestPSO from pyswarms.utils.functions.single_obj import sphere_func + @pytest.fixture def grid(): """Returns a GridSearch instance""" - options = {'c1': [1, 2, 3], - 'c2': [1, 2, 3], - 'k' : [5, 10, 15], - 'w' : [0.9, 0.7, 0.4], - 'p' : [1]} - return GridSearch(LocalBestPSO, n_particles=40, - dimensions=20, options=options, objective_func=sphere_func, iters=10, bounds=None) + options = { + "c1": [1, 2, 3], + "c2": [1, 2, 3], + "k": [5, 10, 15], + "w": [0.9, 0.7, 0.4], + "p": [1], + } + return GridSearch( + LocalBestPSO, + n_particles=40, + dimensions=20, + options=options, + objective_func=sphere_func, + iters=10, + bounds=None, + ) + @pytest.fixture def grid_mini(): """Returns a GridSearch instance with a smaller search-space""" - options = {'c1': [1, 2], - 'c2': 6, - 'k' : 5, - 'w' : 0.9, - 'p' : 0} - return GridSearch(LocalBestPSO, n_particles=40, - dimensions=20, options=options, objective_func=sphere_func, iters=10, bounds=None) + options = {"c1": [1, 2], "c2": 6, "k": 5, "w": 0.9, "p": 0} + return GridSearch( + LocalBestPSO, + n_particles=40, + dimensions=20, + options=options, + objective_func=sphere_func, + iters=10, + bounds=None, + ) + @pytest.fixture def random_unbounded(): """Returns a RandomSearch instance without bounds""" - options = {'c1': [1, 5], - 'c2': [6, 10], - 'k' : [11, 15], - 'w' : [0.4, 0.9], - 'p' : 1} - return RandomSearch(LocalBestPSO, n_particles=40, dimensions=20, options=options, objective_func=sphere_func, iters=10, n_selection_iters=100, bounds=None) + options = { + "c1": [1, 5], + "c2": [6, 10], + "k": [11, 15], + "w": [0.4, 0.9], + "p": 1, + } + return RandomSearch( + LocalBestPSO, + n_particles=40, + dimensions=20, + options=options, + objective_func=sphere_func, + iters=10, + n_selection_iters=100, + bounds=None, + ) + @pytest.fixture def random_bounded(): """Returns a RandomSearch instance with bounds""" - bounds = (np.array([-5,-5]), np.array([5,5])) - options = {'c1': [1, 5], - 'c2': [6, 10], - 'k' : [11, 15], - 'w' : [0.4, 0.9], - 'p' : 1} - return RandomSearch(LocalBestPSO, n_particles=40, dimensions=20, options=options, objective_func=sphere_func, iters=10, n_selection_iters=100, bounds=bounds) \ No newline at end of file + bounds = (np.array([-5, -5]), np.array([5, 5])) + options = { + "c1": [1, 5], + "c2": [6, 10], + "k": [11, 15], + "w": [0.4, 0.9], + "p": 1, + } + return RandomSearch( + LocalBestPSO, + n_particles=40, + dimensions=20, + options=options, + objective_func=sphere_func, + iters=10, + n_selection_iters=100, + bounds=bounds, + ) diff --git a/tests/utils/search/test_gridsearch.py b/tests/utils/search/test_gridsearch.py index 85cc4b86..8a9936f2 100644 --- a/tests/utils/search/test_gridsearch.py +++ b/tests/utils/search/test_gridsearch.py @@ -4,14 +4,18 @@ # Import modules import pytest -@pytest.mark.parametrize('maximum', [True, False]) + +@pytest.mark.parametrize("maximum", [True, False]) def test_search_best_options_return_type(grid, maximum): """Tests if best options returns a dictionary""" _, best_options = grid.search(maximum) assert isinstance(best_options, dict) + def test_grid_output(grid_mini): """Tests if generate_grid function returns expected value""" - expected = [{'c1': 1, 'c2': 6, 'k': 5, 'w': 0.9, 'p': 0}, - {'c1': 2, 'c2': 6, 'k': 5, 'w': 0.9, 'p': 0}] - assert grid_mini.generate_grid() == expected \ No newline at end of file + expected = [ + {"c1": 1, "c2": 6, "k": 5, "w": 0.9, "p": 0}, + {"c1": 2, "c2": 6, "k": 5, "w": 0.9, "p": 0}, + ] + assert grid_mini.generate_grid() == expected diff --git a/tests/utils/search/test_randomsearch.py b/tests/utils/search/test_randomsearch.py index e2677135..7ac99045 100644 --- a/tests/utils/search/test_randomsearch.py +++ b/tests/utils/search/test_randomsearch.py @@ -4,12 +4,14 @@ # Import modules import pytest -@pytest.mark.parametrize('maximum', [True, False]) + +@pytest.mark.parametrize("maximum", [True, False]) def test_search_best_options_return_type(random_unbounded, maximum): """Tests if best options returns a dictionary""" _, best_options = random_unbounded.search(maximum) assert isinstance(best_options, dict) + def test_generate_grid_combinations(random_bounded): """Test that the number of combinations in grid equals the number parameter selection iterations specficied""" @@ -17,7 +19,8 @@ def test_generate_grid_combinations(random_bounded): grid = random_bounded.generate_grid() assert len(grid) == expected -@pytest.mark.parametrize('options', ['c1', 'c2', 'k', 'w']) + +@pytest.mark.parametrize("options", ["c1", "c2", "k", "w"]) def test_generate_grid_parameter_mapping(random_bounded, options): """Test that generated values are correctly mapped to each parameter and are within the specified bounds """ @@ -25,4 +28,4 @@ def test_generate_grid_parameter_mapping(random_bounded, options): values = [x[options] for x in grid] for val in values: assert val >= random_bounded.options[options][0] - assert val <= random_bounded.options[options][1] \ No newline at end of file + assert val <= random_bounded.options[options][1] From fbe052d5140be46b10fb7d55d1f435d5fbe73886 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Sun, 17 Jun 2018 00:23:26 +0900 Subject: [PATCH 15/38] Add pre-commit hook configurations (#139) This commit adds the .flake8 and pyproject.toml to specify flake8 and black configuration. In addition, we updated the code to reflect the warnings found in flake8. Signed-off-by: Lester James V. Miranda --- .flake8 | 5 +++++ .pre-commit-config.yaml | 10 ++++++++++ pyproject.toml | 16 ++++++++++++++++ pyswarms/backend/__init__.py | 2 ++ pyswarms/backend/generators.py | 6 +++--- pyswarms/backend/operators.py | 10 ++++------ pyswarms/backend/swarms.py | 8 ++++---- pyswarms/backend/topology/ring.py | 2 +- pyswarms/backend/topology/star.py | 2 +- pyswarms/single/global_best.py | 12 ++++++------ pyswarms/utils/plotters/__init__.py | 4 +++- pyswarms/utils/plotters/formatters.py | 2 +- pyswarms/utils/plotters/plotters.py | 7 +++---- requirements_dev.txt | 3 ++- setup.py | 6 ++++-- 15 files changed, 65 insertions(+), 30 deletions(-) create mode 100644 .flake8 create mode 100644 .pre-commit-config.yaml create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..d9ad0b40 --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +ignore = E203, E266, E501, W503, F403, F401 +max-line-length = 79 +max-complexity = 18 +select = B,C,E,F,W,T4,B9 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..4bdcc6fc --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: +- repo: https://github.com/ambv/black + rev: stable + hooks: + - id: black + language_version: python3.6 +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v1.2.3 + hooks: + - id: flake8 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..dfab0250 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[tool.black] +line-length = 79 +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' diff --git a/pyswarms/backend/__init__.py b/pyswarms/backend/__init__.py index ff1d88c1..425d3450 100644 --- a/pyswarms/backend/__init__.py +++ b/pyswarms/backend/__init__.py @@ -7,3 +7,5 @@ from .generators import * from .operators import * from .swarms import * + +__all__ = ["generators", "operators", "swarms"] diff --git a/pyswarms/backend/generators.py b/pyswarms/backend/generators.py index 10f28b90..2ef6f5a8 100644 --- a/pyswarms/backend/generators.py +++ b/pyswarms/backend/generators.py @@ -76,7 +76,7 @@ def generate_discrete_swarm( n_particles, dimensions, binary=False, init_pos=None ): """Generates a discrete swarm - + Parameters ---------- n_particles : int @@ -128,7 +128,7 @@ def generate_velocity(n_particles, dimensions, clamp=None): velocity matrix of shape (n_particles, dimensions) """ try: - min_velocity, max_velocity = (0, 1) if clamp == None else clamp + min_velocity, max_velocity = (0, 1) if clamp is None else clamp velocity = (max_velocity - min_velocity) * np.random.random_sample( size=(n_particles, dimensions) ) + min_velocity @@ -150,7 +150,7 @@ def create_swarm( clamp=None, ): """Abstracts the generate_swarm() and generate_velocity() methods - + Parameters ---------- n_particles : int diff --git a/pyswarms/backend/operators.py b/pyswarms/backend/operators.py index bf8419cb..f91b7675 100644 --- a/pyswarms/backend/operators.py +++ b/pyswarms/backend/operators.py @@ -20,7 +20,7 @@ def compute_pbest(swarm): """Takes a swarm instance and updates the personal best scores - + You can use this method to update your personal best positions. .. code-block:: python @@ -38,7 +38,7 @@ def compute_pbest(swarm): It updates your :code:`current_pbest` with the personal bests acquired by comparing the (1) cost of the current positions and the (2) personal bests your swarm has attained. - + If the cost of the current position is less than the cost of the personal best, then the current position replaces the previous personal best position. @@ -60,9 +60,7 @@ def compute_pbest(swarm): dimensions = swarm.dimensions # Create a 1-D and 2-D mask based from comparisons mask_cost = swarm.current_cost < swarm.pbest_cost - mask_pos = np.repeat( - mask_cost[:, np.newaxis], swarm.dimensions, axis=1 - ) + mask_pos = np.repeat(mask_cost[:, np.newaxis], dimensions, axis=1) # Apply masks new_pbest_pos = np.where(~mask_pos, swarm.pbest_pos, swarm.position) new_pbest_cost = np.where( @@ -82,7 +80,7 @@ def compute_velocity(swarm, clamp): This method updates the velocity matrix using the best and current positions of the swarm. The velocity matrix is computed using the cognitive and social terms of the swarm. - + A sample usage can be seen with the following: .. code-block :: python diff --git a/pyswarms/backend/swarms.py b/pyswarms/backend/swarms.py index 8a63bd7c..8cc3d2d9 100644 --- a/pyswarms/backend/swarms.py +++ b/pyswarms/backend/swarms.py @@ -17,17 +17,17 @@ @attrs class Swarm(object): """A Swarm Class - + This class offers a generic swarm that can be used in most use-cases such as single-objective optimization, etc. It contains various attributes that are commonly-used in most swarm implementations. To initialize this class, **simply supply values for the position and velocity matrix**. The other attributes are automatically filled. If you want to - initialize random values, take a look at: + initialize random values, take a look at: * :func:`pyswarms.backend.generators.generate_swarm`: for generating positions randomly. - * :func:`pyswarms.backend.generators.generate_velocity`: for generating velocities randomly. + * :func:`pyswarms.backend.generators.generate_velocity`: for generating velocities randomly. If your swarm requires additional parameters (say c1, c2, and w in gbest PSO), simply pass them to the :code:`options` dictionary. @@ -73,7 +73,7 @@ class Swarm(object): pbest_cost : numpy.ndarray (default is empty array) personal best costs of each particle of shape :code:`(n_particles, )` best_cost : float (default is :code:`np.inf`) - best cost found by the swarm + best cost found by the swarm current_cost : numpy.ndarray (default is empty array) the current cost found by the swarm of shape :code:`(n_particles, dimensions)` """ diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index cfebb69a..f7dd81b3 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -88,7 +88,7 @@ def compute_velocity(self, swarm, clamp=None): This method updates the velocity matrix using the best and current positions of the swarm. The velocity matrix is computed using the cognitive and social terms of the swarm. - + A sample usage can be seen with the following: .. code-block :: python diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index e655217d..a88d7896 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -77,7 +77,7 @@ def compute_velocity(self, swarm, clamp=None): This method updates the velocity matrix using the best and current positions of the swarm. The velocity matrix is computed using the cognitive and social terms of the swarm. - + A sample usage can be seen with the following: .. code-block :: python diff --git a/pyswarms/single/global_best.py b/pyswarms/single/global_best.py index 9d31401e..eb05356f 100644 --- a/pyswarms/single/global_best.py +++ b/pyswarms/single/global_best.py @@ -20,15 +20,15 @@ .. math:: - v_{ij}(t + 1) = m * v_{ij}(t) + c_{1}r_{1j}(t)[y_{ij}(t) − x_{ij}(t)] + v_{ij}(t + 1) = m * v_{ij}(t) + c_{1}r_{1j}(t)[y_{ij}(t) − x_{ij}(t)] + c_{2}r_{2j}(t)[\hat{y}_{j}(t) − x_{ij}(t)] Here, :math:`c1` and :math:`c2` are the cognitive and social parameters -respectively. They control the particle's options in choosing how to -react given two choices: (1) to follow its *personal best* or (2) follow -the swarm's *global best* position. Overall, this dictates if the swarm -is explorative or exploitative in nature. In addition, a parameter -:math:`w` controls the inertia of the swarm's movement. +respectively. They control the particle's behavior given two choices: (1) to +follow its *personal best* or (2) follow the swarm's *global best* position. +Overall, this dictates if the swarm is explorative or exploitative in nature. +In addition, a parameter :math:`w` controls the inertia of the swarm's +movement. An example usage is as follows: diff --git a/pyswarms/utils/plotters/__init__.py b/pyswarms/utils/plotters/__init__.py index c2b2b683..b961aa10 100644 --- a/pyswarms/utils/plotters/__init__.py +++ b/pyswarms/utils/plotters/__init__.py @@ -5,4 +5,6 @@ 3D spaces. """ -from .plotters import * +from .plotters import plot_cost_history, plot_contour, plot_surface + +__all__ = ["plotters", "formatters"] diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index 58de46b1..f20899c6 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -71,7 +71,7 @@ class Animator(object): You can use this class to modify options on how the animation will be run in the :func:`pyswarms.utils.plotters.plot_contour` and :func:`pyswarms.utils.plotters.plot_surface` methods. - + .. code-block :: python from pyswarms.utils.plotters import plot_contour diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py index 0bbc9356..f317dc6f 100644 --- a/pyswarms/utils/plotters/plotters.py +++ b/pyswarms/utils/plotters/plotters.py @@ -67,7 +67,6 @@ # Import modules import logging -from collections import namedtuple import matplotlib.pyplot as plt import numpy as np @@ -75,7 +74,7 @@ from mpl_toolkits.mplot3d import Axes3D # Import from package -from .formatters import Designer, Animator, Mesher +from .formatters import Designer, Animator # Initialize logger logger = logging.getLogger(__name__) @@ -255,10 +254,10 @@ def plot_surface( This is useful for plotting the swarm's 2-dimensional position with respect to the objective function. The value in the z-axis is the fitness of the 2D particle when passed to the objective function. When preparing the - position history, make sure that the: + position history, make sure that the: * first column is the position in the x-axis, - * second column is the position in the y-axis; and + * second column is the position in the y-axis; and * third column is the fitness of the 2D particle The :class:`pyswarms.utils.plotters.formatters.Mesher` class provides a diff --git a/requirements_dev.txt b/requirements_dev.txt index f5539309..3ee24f97 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -14,4 +14,5 @@ scipy>=0.17.0 numpy>=1.13.0 matplotlib>=1.3.1 pytest==3.6.1 -attrs==18.1.0 \ No newline at end of file +attrs==18.1.0 +pre-commit diff --git a/setup.py b/setup.py index a997fe0f..1c8db6cc 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,8 @@ 'matplotlib>=1.3.1', 'mock==2.0.0', 'pytest==3.2.1', - 'attrs==18.1.0' + 'attrs==18.1.0', + 'pre-commit' ] setup_requirements = [ @@ -34,7 +35,8 @@ 'matplotlib>=1.3.1', 'mock==2.0.0', 'pytest==3.2.1', - 'attrs==18.1.0' + 'attrs==18.1.0', + 'pre-commit' ] setup( From 297a403ec4ab1c658ef0db16f60e61fd503f4c51 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Mon, 18 Jun 2018 20:07:39 +0900 Subject: [PATCH 16/38] Mention black in README and CONTRIBUTING (#139) In this commit, we explicitly say that we are using black as our code formatter. Signed-off-by: Lester James V. Miranda --- CONTRIBUTING.rst | 6 ++++-- README.md | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ac5a56aa..2f58b7f7 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -77,13 +77,15 @@ Ready to contribute? Here's how to set up `pyswarms` for local development. Now you can make your changes locally. -5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: +5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox. In addition, ensure that your code is formatted using black:: $ flake8 pyswarms tests + $ black pyswarms tests $ python setup.py test or py.test $ tox - To get flake8 and tox, just pip install them into your virtualenv. + To get flake8, black, and tox, just pip install them into your virtualenv. If you wish, + you can add pre-commit hooks for both flake8 and black to make all formatting easier. 6. Commit your changes and push your branch to GitHub:: diff --git a/README.md b/README.md index e75e796e..89924553 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![Documentation Status](https://readthedocs.org/projects/pyswarms/badge/?version=master)](https://pyswarms.readthedocs.io/en/master/?badge=development) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg )](https://raw.githubusercontent.com/ljvmiranda921/pyswarms/master/LICENSE) [![DOI](http://joss.theoj.org/papers/10.21105/joss.00433/status.svg)](https://doi.org/10.21105/joss.00433) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) [![Gitter Chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/pyswarms/Issues) PySwarms is an extensible research toolkit for particle swarm optimization From 2dd59669b710e5127d63b597c0bee51cd6a5ad07 Mon Sep 17 00:00:00 2001 From: ljvmiranda921 Date: Mon, 18 Jun 2018 20:22:21 +0900 Subject: [PATCH 17/38] Add PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..52edb2ac --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,37 @@ + + +## Description + + +## Related Issue + + + + + +## Motivation and Context + + +## How Has This Been Tested? + + + + +## Screenshots (if appropriate): + +## Types of changes + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to change) + +## Checklist: + + +- [ ] My code follows the code style of this project. +- [ ] My change requires a change to the documentation. +- [ ] I have updated the documentation accordingly. +- [ ] I have read the **CONTRIBUTING** document. +- [ ] I have added tests to cover my changes. +- [ ] All new and existing tests passed. + From b643bfdd64b90db3dacbaf8de050ab4f7e5f4be5 Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Mon, 25 Jun 2018 12:38:00 +0200 Subject: [PATCH 18/38] Create Inverse Kinematics tutorial (#141) Resolves #136 Created an Inverse Kinematics Tutorial in a Jupyter Notebook. Committed with @whzup --- examples/inverse_kinematics.ipynb | 381 ++++++++++++++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 examples/inverse_kinematics.ipynb diff --git a/examples/inverse_kinematics.ipynb b/examples/inverse_kinematics.ipynb new file mode 100644 index 00000000..0e295b17 --- /dev/null +++ b/examples/inverse_kinematics.ipynb @@ -0,0 +1,381 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Solving the Inverse Kinematics problem using Particle Swarm Optimization\n", + "====================================\n", + "\n", + "In this example we are going to use the `pyswarms` library to solve a 6-DOF (Degrees of Freedom) Inverse Kinematics (IK) problem by treating it as a optimization problem. We will use the `pyswarms` library to find an *optimal* solution from a set of candidate solutions." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "# Change directory to access the pyswarms module\n", + "sys.path.append('../')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running on Python version: 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)]\n" + ] + } + ], + "source": [ + "print('Running on Python version: {}'.format(sys.version))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Import modules\n", + "import numpy as np\n", + "\n", + "# Import PySwarms\n", + "import pyswarms as ps\n", + "\n", + "# Some more magic so that the notebook will reload external python modules;\n", + "# see http://stackoverflow.com/questions/1907993/autoreload-of-modules-in-ipython\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "# Styling for the text below" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%html\n", + "\n", + "# Styling for the text below" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Introduction\n", + "======\n", + "\n", + "Inverse Kinematics is one of the most challenging problems in robotics. The problem involves finding an optimal *pose* for a manipulator given the position of the end-tip effector as opposed to forward kinematics , where the end-tip position is sought given the pose or joint configuration. Normally, this position is expressed as a point in a coordinate system (e.g., in a Cartesian system with $x$, $y$ and $z$ coordinates). However, the pose of the manipulator can also be expressed as the collection of joint variables that describe the angle of bending or twist (in revolute joints) or length of extension (in prismatic joints).\n", + "\n", + "IK is particularly difficult because an abundance of solutions can arise. Intuitively, one can imagine that a robotic arm can have multiple ways of reaching through a certain point. It's the same when you touch the table and move your arm without moving the point you're touching the table at. Moreover, the calculation of of these positions can be very difficult. Simple solutions can be found for 3-DOF manipulators but trying to solve the problem for 6 or even more DOF can lead to challenging algebraic problems.\n", + "\n", + "IK as an Optimization Problem\n", + "===============\n", + "\n", + "In this implementation, we are going to use a *6-DOF Stanford Manipulator* with 5 revolute joints and 1 prismatic joint. Furthermore, the constraints of the joints are going to be as follows:\n", + "\n", + "| Parameters | Lower Boundary | Upper Boundary |\n", + "|:---:|:----------------:|:----------------:|\n", + "|$\\theta_1$ | $-\\pi$ | $\\pi$ |\n", + "|$\\theta_2$ |$-\\frac{\\pi}{2}$| $\\frac{\\pi}{2}$|\n", + "|$d_3$ | $1$ | $3$ |\n", + "|$\\theta_4$ | $-\\pi$ | $\\pi$ |\n", + "|$\\theta_5$ |$-\\frac{5\\pi}{36}$|$\\frac{5\\pi}{36}$|\n", + "|$\\theta_6$ | $-\\pi$ | $\\pi$ |\n", + "\n", + "**Table 1**: *Physical constraints for the joint variables*\n", + "\n", + "Now, if we are given an *end-tip position* (in this case a $xyz$ coordinate) we need to find the optimal parameters with the constraints imposed in **Table 1**. These conditions are then sufficient in order to treat this problem as an optimization problem. We define our parameter vector $\\mathbf{X}$ as follows:\n", + "\n", + "$$\\mathbf{X}\\,:=\\, [ \\, \\theta_1 \\quad \\theta_2 \\quad d_3\\ \\quad \\theta_4 \\quad \\theta_5 \\, ]$$\n", + "\n", + "And for our end-tip position we define the target vector $\\mathbf{T}$ as:\n", + "\n", + "$$\\mathbf{T}\\,:=\\, [\\, T_x \\quad T_y \\quad T_z \\,]$$\n", + "\n", + "We can then start implementing our optimization algorithm.\n", + "\n", + "Initializing the Swarm\n", + "===========\n", + "\n", + "The main idea for PSO is that we set a swarm $\\mathbf{S}$ composed of particles $\\mathbf{P}_n$ into a search space in order to find the optimal solution. The movement of the swarm depends on the cognitive ($c_1$) and social ($c_2$) of all the particles. The cognitive component speaks of the particle's bias towards its personal best from its past experience (i.e., how attracted it is to its own best position). The social component controls how the particles are attracted to the best score found by the swarm (i.e., the global best). High $c_1$ paired with low $c_2$ values can often cause the swarm to stagnate. The inverse can cause the swarm to converge too fast, resulting in suboptimal solutions.\n", + "\n", + "We define our particle $\\mathbf{P}$ as:\n", + "\n", + "$$\\mathbf{P}\\,:=\\,\\mathbf{X}$$\n", + "\n", + "And the swarm as being composed of $N$ particles with certain positions at a timestep $t$:\n", + "\n", + "$$\\mathbf{S}_t\\,:=\\,[\\,\\mathbf{P}_1\\quad\\mathbf{P}_2\\quad ... \\quad\\mathbf{P}_N\\,]$$\n", + "\n", + "In this implementation, we designate $\\mathbf{P}_1$ as the initial configuration of the manipulator at the zero-position. This means that the angles are equal to 0 and the link offset is also zero. We then generate the $N-1$ particles using a uniform distribution which is controlled by the hyperparameter $\\epsilon$.\n", + "\n", + "Finding the global optimum\n", + "=============\n", + "\n", + "In order to find the global optimum the swarm must be moved. This movement is then translated by an update of the current position given the swarm's velocity $\\mathbf{V}$. That is:\n", + "\n", + "$$\\mathbf{S}_{t+1} = \\mathbf{S}_t + \\mathbf{V}_{t+1}$$\n", + "\n", + "The velocity is then computed as follows:\n", + "\n", + "$$\\mathbf{V}_{t+1} = w\\mathbf{V}_t + c_1 r_1 (\\mathbf{p}_{best} - \\mathbf{p}) + c_2 r_2(\\mathbf{g}_{best} - \\mathbf{p})$$\n", + "\n", + "Where $r_1$ and $r_2$ denote random values in the intervall $[0,1]$, $\\mathbf{p}_{best}$ is the best and $\\mathbf{p}$ is the current personal position and $\\mathbf{g}_{best}$ is the best position of all the particles. Moreover, $w$ is the inertia weight that controls the \"memory\" of the swarm's previous position.\n", + "\n", + "Preparations\n", + "------------\n", + "\n", + "Let us now see how this works with the `pyswarms` library. We use the point $[-2,2,3]$ as our target for which we want to find an optimal pose of the manipulator. We start by defining a function to get the distance from the current position to the target position:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def distance(query, target):\n", + " x_dist = (target[0] - query[0])**2\n", + " y_dist = (target[1] - query[1])**2\n", + " z_dist = (target[2] - query[2])**2\n", + " dist = np.sqrt(x_dist + y_dist + z_dist)\n", + " return dist" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We are going to use the distance function to compute the cost, the further away the more costly the position is. \n", + "\n", + "The optimization algorithm needs some paramters (the swarm size, $c_1$, $c_2$ and $\\epsilon$). For the *options* ($c_1$,$c_2$ and $w$) we have to create a dictionary and for the constraints a tuple with a list of the respective minimal values and a list of the respective maximal values. The rest can be handled with variables. Additionally, we define the joint lengths to be 3 units long:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "swarm_size = 20\n", + "dim = 6 # Dimension of X\n", + "epsilon = 1.0\n", + "options = {'c1': 1.5, 'c2':1.5, 'w':0.5}\n", + "\n", + "constraints = (np.array([-np.pi , -np.pi/2 , 1 , -np.pi , -5*np.pi/36 , -np.pi]),\n", + " np.array([np.pi , np.pi/2 , 3 , np.pi , 5*np.pi/36 , np.pi]))\n", + "\n", + "d1 = d2 = d3 = d4 = d5 = d6 = 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to obtain the current position, we need to calculate the matrices of rotation and translation for every joint. Here we use the [Denvait-Hartenberg parameters](https://en.wikipedia.org/wiki/Denavit–Hartenberg_parameters) for that. So we define a function that calculates these. The function uses the rotation angle and the extension $d$ of a prismatic joint as input:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def getTransformMatrix(theta, d, a, alpha):\n", + " T = np.array([[np.cos(theta) , -np.sin(theta)*np.cos(alpha) , np.sin(theta)*np.sin(alpha) , a*np.cos(theta)],\n", + " [np.sin(theta) , np.cos(theta)*np.cos(alpha) , -np.cos(theta)*np.sin(alpha) , a*np.sin(theta)],\n", + " [0 , np.sin(alpha) , np.cos(alpha) , d ],\n", + " [0 , 0 , 0 , 1 ]\n", + " ])\n", + " return T" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can calculate the transformation matrix to obtain the end tip position. For this we create another function that takes our vector $\\mathbf{X}$ with the joint variables as input:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def get_end_tip_position(params):\n", + " # Create the transformation matrices for the respective joints\n", + " t_00 = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]])\n", + " t_01 = getTransformMatrix(params[0] , d2 , 0 , -np.pi/2)\n", + " t_12 = getTransformMatrix(params[1] , d2 , 0 , -np.pi/2)\n", + " t_23 = getTransformMatrix(0 , params[2] , 0 , -np.pi/2)\n", + " t_34 = getTransformMatrix(params[3] , d4 , 0 , -np.pi/2)\n", + " t_45 = getTransformMatrix(params[4] , 0 , 0 , np.pi/2)\n", + " t_56 = getTransformMatrix(params[5] , d6 ,0 , 0)\n", + "\n", + " # Get the overall transformation matrix\n", + " end_tip_m = t_00.dot(t_01).dot(t_12).dot(t_23).dot(t_34).dot(t_45).dot(t_56)\n", + " \n", + " # The coordinates of the end tip are the 3 upper entries in the 4th column\n", + " pos = np.array([end_tip_m[0,3],end_tip_m[1,3],end_tip_m[2,3]])\n", + " return pos" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last thing we need to prepare in order to run the algorithm is the actual function that we want to optimize. We just need to calculate the distance between the position of each swarm particle and the target point:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def opt_func(X):\n", + " n_particles = X.shape[0] # number of particles\n", + " target = np.array([-2,2,3])\n", + " dist = [distance(get_end_tip_position(X[i]), target) for i in range(n_particles)]\n", + " return np.array(dist)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Running the algorithm\n", + "----------------------\n", + "\n", + "Braced with these preparations we can finally start using the algorithm:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 0.9638223076369133\n", + "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 2.5258875519324167e-07\n", + "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 4.7236564972673785e-14\n", + "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:================================\n", + "Optimization finished!\n", + "Final cost: 0.0000\n", + "Best value: [ -2.182725 1.323111 1.579636 ...]\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Wall time: 13.6 s\n" + ] + } + ], + "source": [ + "%%time\n", + "# Call an instance of PSO\n", + "optimizer = ps.single.GlobalBestPSO(n_particles=swarm_size,\n", + " dimensions=dim,\n", + " options=options,\n", + " bounds=constraints)\n", + "\n", + "# Perform optimization\n", + "cost, joint_vars = optimizer.optimize(opt_func, print_step=100, iters=1000, verbose=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's see if the algorithm really worked and test the output for `joint_vars`:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[-2. 2. 3.]\n" + ] + } + ], + "source": [ + "print(get_end_tip_position(joint_vars))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hooray! That's exactly the position we wanted the tip to be in. Of course this example is quite primitive. Some extensions of this idea could involve the consideration of the current position of the manipulator and the amout of rotation and extension in the optimization function such that the result is the path with the least movement." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 6bd896dd7c86fd70be4bbc133dfc564a9b82befb Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Tue, 26 Jun 2018 15:22:39 +0200 Subject: [PATCH 19/38] Add pyramid topology (#142) Reference: #129 Created a class for the implementation of a pyramid topology using Delaunay triangulation. Additionally, @whzup changed a small error in the description of the `compute_velocity()` method in the Ring class. Notes: - For v.0.2.0-dev.3. - TODO: Update README in dev Committed with: @whzup Signed-off-by: Lester James V. Miranda --- pyswarms/backend/topology/__init__.py | 3 +- pyswarms/backend/topology/pyramid.py | 131 +++++++++++++++++++++++++ pyswarms/backend/topology/ring.py | 2 +- tests/backend/topology/test_pyramid.py | 42 ++++++++ 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 pyswarms/backend/topology/pyramid.py create mode 100644 tests/backend/topology/test_pyramid.py diff --git a/pyswarms/backend/topology/__init__.py b/pyswarms/backend/topology/__init__.py index 95673091..b9d5a418 100644 --- a/pyswarms/backend/topology/__init__.py +++ b/pyswarms/backend/topology/__init__.py @@ -8,6 +8,7 @@ from .star import Star from .ring import Ring +from .pyramid import Pyramid -__all__ = ["Star", "Ring"] +__all__ = ["Star", "Ring", "Pyramid"] diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py new file mode 100644 index 00000000..c6f00122 --- /dev/null +++ b/pyswarms/backend/topology/pyramid.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- + +""" +A Pyramid Network Topology + +This class implements a star topology where all particles are connected in a +pyramid like fashion. +""" + +# 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 .base import Topology + +# Create a logger +logger = logging.getLogger(__name__) + +class Pyramid(Topology): + def __init__(self): + super(Pyramid, self).__init__() + + def compute_gbest(self, swarm): + """Updates the global best using a pyramid neighborhood approach + + This uses the Delaunay method from :code:`scipy` to triangulate space + with simplices + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + + Returns + ------- + numpy.ndarray + Best position of shape :code:`(n_dimensions, )` + float + Best cost + """ + try: + # If there are less than 5 particles they are all connected + if swarm.n_particles < 5: + best_pos = swarm.pbest_pos[np.argmin(swarm.pbest_cost)] + best_cost = np.min(swarm.pbest_cost) + else: + pyramid = Delaunay(swarm.position) + indices, index_pointer = pyramid.vertex_neighbor_vertices + # Insert all the neighbors for each particle in the idx array + idx = np.array([index_pointer[indices[i]:indices[i+1]] for i in range(swarm.n_particles)]) + idx_min = swarm.pbest_cost[idx].argmin(axis=1) + best_neighbor = idx[np.arange(len(idx)), idx_min] + + # Obtain best cost and position + best_cost = np.min(swarm.pbest_cost[best_neighbor]) + best_pos = swarm.pbest_pos[ + np.argmin(swarm.pbest_cost[best_neighbor]) + ] + except AttributeError: + msg = "Please pass a Swarm class. You passed {}".format( + type(swarm) + ) + logger.error(msg) + raise + else: + return (best_pos, best_cost) + + def compute_velocity(self, swarm, clamp=None): + """Computes the velocity matrix + + This method updates the velocity matrix using the best and current + positions of the swarm. The velocity matrix is computed using the + cognitive and social terms of the swarm. + + A sample usage can be seen with the following: + + .. code-block :: python + + import pyswarms.backend as P + from pyswarms.swarms.backend import Swarm + from pyswarms.backend.topology import Pyramid + + my_swarm = P.create_swarm(n_particles, dimensions) + my_topology = Pyramid() + + for i in range(iters): + # Inside the for-loop + my_swarm.velocity = my_topology.update_velocity(my_swarm, clamp) + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + clamp : tuple of floats (default is :code:`None`) + a tuple of size 2 where the first entry is the minimum velocity + and the second entry is the maximum velocity. It + sets the limits for velocity clamping. + + Returns + ------- + numpy.ndarray + Updated velocity matrix + """ + return ops.compute_velocity(swarm, clamp) + + def compute_position(self, swarm, bounds=None): + """Updates the position matrix + + This method updates the position matrix given the current position and + the velocity. If bounded, it waives updating the position. + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + bounds : tuple of :code:`np.ndarray` or list (default is :code:`None`) + a tuple of size 2 where the first entry is the minimum bound while + the second entry is the maximum bound. Each array must be of shape + :code:`(dimensions,)`. + + Returns + ------- + numpy.ndarray + New position-matrix + """ + return ops.compute_position(swarm, bounds) diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index f7dd81b3..c1fc30eb 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -95,7 +95,7 @@ def compute_velocity(self, swarm, clamp=None): import pyswarms.backend as P from pyswarms.swarms.backend import Swarm - from pyswarms.backend.topology import Star + from pyswarms.backend.topology import Ring my_swarm = P.create_swarm(n_particles, dimensions) my_topology = Ring() diff --git a/tests/backend/topology/test_pyramid.py b/tests/backend/topology/test_pyramid.py new file mode 100644 index 00000000..36f877f6 --- /dev/null +++ b/tests/backend/topology/test_pyramid.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import modules +import pytest +import numpy as np + +# Import from package +from pyswarms.backend.topology import Pyramid + + +def test_compute_gbest_return_values(swarm): + """Test if compute_gbest() gives the expected return values""" + topology = Pyramid() + expected_cost = 1 + expected_pos = np.array([1, 2, 3]) + pos, cost = topology.compute_gbest(swarm) + assert cost == expected_cost + assert (pos == expected_pos).all() + + +@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) +def test_compute_velocity_return_values(swarm, clamp): + """Test if compute_velocity() gives the expected shape and range""" + topology = Pyramid() + v = topology.compute_velocity(swarm, clamp) + assert v.shape == swarm.position.shape + if clamp is not None: + assert (clamp[0] <= v).all() and (clamp[1] >= v).all() + + +@pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], +) +def test_compute_position_return_values(swarm, bounds): + """Test if compute_position() gives the expected shape and range""" + topology = Pyramid() + p = topology.compute_position(swarm, bounds) + assert p.shape == swarm.velocity.shape + if bounds is not None: + assert (bounds[0] <= p).all() and (bounds[1] >= p).all() From 89f12cb3d77149d9c4047f2b50ff2be81e8cf68e Mon Sep 17 00:00:00 2001 From: bradahoward Date: Thu, 28 Jun 2018 10:38:33 -0500 Subject: [PATCH 20/38] Add feature to pass args to objective function (#144) Resolves #143 This commit adds a `**kwargs` parameter to the `optimize()` method to pass arguments directly on the objective function. Additional tests and documentation were also provided. Committed with @bradahoward Signed-off-by: Lester James V. Miranda --- .../basic_optimization_with_arguments.ipynb | 236 +++++++++++++++++ pyswarms/base/base_discrete.py | 4 +- pyswarms/base/base_single.py | 4 +- pyswarms/discrete/binary.py | 14 +- pyswarms/single/global_best.py | 15 +- pyswarms/single/local_best.py | 14 +- .../test_objective_func_with_kwargs.py | 247 ++++++++++++++++++ 7 files changed, 517 insertions(+), 17 deletions(-) create mode 100644 examples/basic_optimization_with_arguments.ipynb create mode 100644 tests/optimizers/test_objective_func_with_kwargs.py diff --git a/examples/basic_optimization_with_arguments.ipynb b/examples/basic_optimization_with_arguments.ipynb new file mode 100644 index 00000000..0398a008 --- /dev/null +++ b/examples/basic_optimization_with_arguments.ipynb @@ -0,0 +1,236 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic Optimization with Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, we will run a basic optimization using an objective function that needs parameterization. We will use the ``single.GBestPSO`` and a version of the rosenbrock function to demonstrate" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 2 2016, 17:53:06) \n", + "[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\n" + ] + } + ], + "source": [ + "import sys\n", + "# change directory to access pyswarms\n", + "sys.path.append('../')\n", + "\n", + "print(\"Running Python {}\".format(sys.version))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [], + "source": [ + "# import modules\n", + "import numpy as np\n", + "\n", + "# create a parameterized version of the classic Rosenbrock unconstrained optimzation function\n", + "def rosenbrock_with_args(x, a, b, c=0):\n", + "\n", + " f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 + c\n", + " return f" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Arguments can either be passed in using a tuple or a dictionary, using the ``kwargs={}`` paradigm. First lets optimize the Rosenbrock function using keyword arguments. Note in the definition of the Rosenbrock function above, there were two arguments that need to be passed other than the design variables, and one optional keyword argument, ``a``, ``b``, and ``c``, respectively" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100, 'a': 1}\n", + "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1022.9667801907804\n", + "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0011172801146408992\n", + "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 7.845605970774126e-07\n", + "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 1.313503109901238e-09\n", + "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 5.187079604907219e-10\n", + "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 1.0115283486088853e-10\n", + "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 2.329870757208421e-13\n", + "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 4.826176894160183e-15\n", + "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 3.125715456651088e-17\n", + "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 1.4236768129666014e-19\n", + "INFO:pyswarms.single.global_best:================================\n", + "Optimization finished!\n", + "Final cost: 0.0000\n", + "Best value: [0.99999999996210465, 0.9999999999218413]\n", + "\n" + ] + } + ], + "source": [ + "from pyswarms.single.global_best import GlobalBestPSO\n", + "\n", + "# instatiate the optimizer\n", + "x_max = 10 * np.ones(2)\n", + "x_min = -1 * x_max\n", + "bounds = (x_min, x_max)\n", + "options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n", + "optimizer = GlobalBestPSO(n_particles=10, dimensions=2, options=options, bounds=bounds)\n", + "\n", + "# now run the optimization, pass a=1 and b=100 as a tuple assigned to args\n", + "\n", + "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100, c=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is also possible to pass a dictionary of key word arguments by using ``**`` decorator when passing the dict" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100.0, 'a': 1.0}\n", + "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1.996797703363527e-21\n", + "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 1.0061676299213387e-24\n", + "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 4.8140236741112245e-28\n", + "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 2.879342304056693e-29\n", + "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:================================\n", + "Optimization finished!\n", + "Final cost: 0.0000\n", + "Best value: [1.0, 1.0]\n", + "\n" + ] + } + ], + "source": [ + "kwargs={\"a\": 1.0, \"b\": 100.0, 'c':0}\n", + "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, **kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "Any key word arguments in the objective function can be left out as they will be passed the default as defined in the prototype. Note here, ``c`` is not passed into the function." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'b': 100, 'a': 1}\n", + "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:================================\n", + "Optimization finished!\n", + "Final cost: 0.0000\n", + "Best value: [1.0, 1.0]\n", + "\n" + ] + } + ], + "source": [ + "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python [conda env:anaconda3]", + "language": "python", + "name": "conda-env-anaconda3-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/pyswarms/base/base_discrete.py b/pyswarms/base/base_discrete.py index 52781888..a52aced9 100644 --- a/pyswarms/base/base_discrete.py +++ b/pyswarms/base/base_discrete.py @@ -193,7 +193,7 @@ def _populate_history(self, hist): self.pos_history.append(hist.position) self.velocity_history.append(hist.velocity) - def optimize(self, objective_func, iters, print_step=1, verbose=1): + def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): """Optimizes the swarm for a number of iterations. Performs the optimization to evaluate the objective @@ -210,6 +210,8 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): amount of steps for printing into console. verbose : int (the default is 1) verbosity setting. + kwargs : dict + arguments for objective function Raises ------ diff --git a/pyswarms/base/base_single.py b/pyswarms/base/base_single.py index 339559e2..ef633387 100644 --- a/pyswarms/base/base_single.py +++ b/pyswarms/base/base_single.py @@ -224,7 +224,7 @@ def _populate_history(self, hist): self.pos_history.append(hist.position) self.velocity_history.append(hist.velocity) - def optimize(self, objective_func, iters, print_step=1, verbose=1): + def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): """Optimizes the swarm for a number of iterations. Performs the optimization to evaluate the objective @@ -241,6 +241,8 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): amount of steps for printing into console. verbose : int (the default is 1) verbosity setting. + kwargs : dict + arguments for objective function Raises ------ diff --git a/pyswarms/discrete/binary.py b/pyswarms/discrete/binary.py index a9e41d2c..1497e4fb 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -149,7 +149,7 @@ def __init__( # Initialize the topology self.top = Ring() - def optimize(self, objective_func, iters, print_step=1, verbose=1): + def optimize(self, objective_func, iters, print_step=1, verbose=1,**kwargs): """Optimizes the swarm for a number of iterations. Performs the optimization to evaluate the objective @@ -165,6 +165,8 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): amount of steps for printing into console. verbose : int (the default is 1) verbosity setting. + kwargs : dict + arguments for objective function Returns ------- @@ -172,10 +174,13 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): 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) + for i in range(iters): # Compute cost for current position and personal best - self.swarm.current_cost = objective_func(self.swarm.position) - self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos) + 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 ) @@ -187,8 +192,7 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): # Print to console if i % print_step == 0: cli_print( - "Iteration %s/%s, cost: %s" - % (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/global_best.py b/pyswarms/single/global_best.py index eb05356f..4ff47fb9 100644 --- a/pyswarms/single/global_best.py +++ b/pyswarms/single/global_best.py @@ -131,7 +131,7 @@ def __init__( # Initialize the topology self.top = Star() - def optimize(self, objective_func, iters, print_step=1, verbose=1): + def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): """Optimizes the swarm for a number of iterations. Performs the optimization to evaluate the objective @@ -147,16 +147,22 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): amount of steps for printing into console. verbose : int (default is 1) verbosity setting. + kwargs : dict + arguments for the objective function Returns ------- tuple the global best cost and the global best position. """ + + 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) - self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos) + 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 ) @@ -169,8 +175,7 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): # Print to console if i % print_step == 0: cli_print( - "Iteration %s/%s, cost: %s" - % (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 6d368ca3..364cbb1f 100644 --- a/pyswarms/single/local_best.py +++ b/pyswarms/single/local_best.py @@ -174,7 +174,7 @@ def __init__( # Initialize the topology self.top = Ring() - def optimize(self, objective_func, iters, print_step=1, verbose=1): + def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): """Optimizes the swarm for a number of iterations. Performs the optimization to evaluate the objective @@ -190,6 +190,8 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): amount of steps for printing into console. verbose : int (default is 1) verbosity setting. + kwargs : dict + arguments for the objective function Returns ------- @@ -197,10 +199,13 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): 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) + for i in range(iters): # Compute cost for current position and personal best - self.swarm.current_cost = objective_func(self.swarm.position) - self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos) + 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 ) @@ -212,8 +217,7 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1): # Print to console if i % print_step == 0: cli_print( - "Iteration %s/%s, cost: %s" - % (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/tests/optimizers/test_objective_func_with_kwargs.py b/tests/optimizers/test_objective_func_with_kwargs.py new file mode 100644 index 00000000..a980044b --- /dev/null +++ b/tests/optimizers/test_objective_func_with_kwargs.py @@ -0,0 +1,247 @@ +#!/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_func + + +def rosenbrock_with_args(x, a, b): + + f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 + return f + + +@pytest.mark.parametrize('func', [ + rosenbrock_with_args +]) +def test_global_kwargs(func): + """Tests if kwargs are passed properly to the objective function for when kwargs are present""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = GlobalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + cost, pos = opt_ps.optimize(func, 1000, 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 +]) +def test_global_kwargs_without_named_arguments(func): + """Tests if kwargs are passed properly to the objective function for when kwargs are present and + other named arguments are not passed, such as print_step""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = GlobalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + cost, pos = opt_ps.optimize(func, 1000, 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_func +]) +def test_global_no_kwargs(func): + """Tests if args are passed properly to the objective function for when no args are present""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = GlobalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3) + + assert np.isclose(cost, 0, rtol=1e-03) + assert np.isclose(pos[0], 1.0, rtol=1e-03) + assert np.isclose(pos[1], 1.0, rtol=1e-03) + + +@pytest.mark.parametrize('func', [ + rosenbrock_with_args +]) +def test_local_kwargs(func): + """Tests if kwargs are passed properly to the objective function for when kwargs are present""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = LocalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + cost, pos = opt_ps.optimize(func, 1000, 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_func +]) +def test_local_no_kwargs(func): + """Tests if no kwargs/args are passed properly to the objective function for when kwargs are present""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = LocalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + cost, pos = opt_ps.optimize(func, iters=1000, print_step=10, verbose=3) + + 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_func +]) +def test_global_uneeded_kwargs(func): + """Tests kwargs are passed the objective function for when kwargs do not exist""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = GlobalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + with pytest.raises(TypeError) as excinfo: + cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + assert 'unexpected keyword' in str(excinfo.value) + + +@pytest.mark.parametrize('func', [ + rosenbrock_with_args +]) +def test_global_missed_kwargs(func): + """Tests kwargs are passed the objective function for when kwargs do not exist""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = GlobalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + with pytest.raises(TypeError) as excinfo: + cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + assert 'missing 1 required positional argument' in str(excinfo.value) + + +@pytest.mark.parametrize('func', [ + rosenbrock_func +]) +def test_local_uneeded_kwargs(func): + """Tests kwargs are passed the objective function for when kwargs do not exist""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = LocalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + with pytest.raises(TypeError) as excinfo: + cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + assert 'unexpected keyword' in str(excinfo.value) + + +@pytest.mark.parametrize('func', [ + rosenbrock_with_args +]) +def test_local_missed_kwargs(func): + """Tests kwargs are passed the objective function for when kwargs do not exist""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = LocalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + with pytest.raises(TypeError) as excinfo: + cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, a=1) + assert 'missing 1 required positional argument' in str(excinfo.value) + + +@pytest.mark.parametrize('func', [ + rosenbrock_with_args +]) +def test_local_wrong_kwargs(func): + """Tests kwargs are passed the objective function for when kwargs do not exist""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = LocalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + with pytest.raises(TypeError) as excinfo: + cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, c=1, d=100) + assert 'unexpected keyword' in str(excinfo.value) + + +@pytest.mark.parametrize('func', [ + rosenbrock_with_args +]) +def test_global_wrong_kwargs(func): + """Tests kwargs are passed the objective function for when kwargs do not exist""" + + # setup optimizer + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2} + + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + opt_ps = GlobalBestPSO(n_particles=100, dimensions=2, options=options, bounds=bounds) + + # run it + with pytest.raises(TypeError) as excinfo: + cost, pos = opt_ps.optimize(func, 1000, print_step=10, verbose=3, c=1, d=100) + assert 'unexpected keyword' in str(excinfo.value) From 6051bfae117ac8ed7b2c75420b1cd25394206580 Mon Sep 17 00:00:00 2001 From: whzup <39431903+whzup@users.noreply.github.com> Date: Sat, 30 Jun 2018 13:49:45 +0200 Subject: [PATCH 21/38] Create GeneralOptimizer class Created a GeneralOptimizer class with a topology parameter so one can choose which topology to use. Some special additions implemented: - Added an error message in case the topology attribute has the wrong type. - Added a special case for the ring topology so it's the same as local optimization. Also, created the corresponding test file and updated the documentation as well as the RST files for the GeneralOptimizer as well as the Pyramid topology. Resolves: #148 --- docs/api/pyswarms.single.rst | 12 +- docs/api/pyswarms.topology.rst | 11 +- pyswarms/__init__.py | 4 +- pyswarms/backend/topology/__init__.py | 3 +- pyswarms/backend/topology/pyramid.py | 16 +- pyswarms/single/__init__.py | 3 +- pyswarms/single/general_optimizer.py | 254 +++++++++++++++++++++ tests/optimizers/conftest.py | 67 +++++- tests/optimizers/test_general_optimizer.py | 155 +++++++++++++ 9 files changed, 510 insertions(+), 15 deletions(-) create mode 100644 pyswarms/single/general_optimizer.py create mode 100644 tests/optimizers/test_general_optimizer.py diff --git a/docs/api/pyswarms.single.rst b/docs/api/pyswarms.single.rst index 7c1ea34e..ae8a17b0 100644 --- a/docs/api/pyswarms.single.rst +++ b/docs/api/pyswarms.single.rst @@ -21,4 +21,14 @@ pyswarms.single.local_best module :undoc-members: :show-inheritance: :private-members: - :special-members: __init__ \ No newline at end of file + :special-members: __init__ + +pyswarms.single.general_optimizer module +--------------------------------- + +.. automodule:: pyswarms.single.general_optimizer + :members: + :undoc-members: + :show-inheritance: + :private-members: + :special-members: __init__ diff --git a/docs/api/pyswarms.topology.rst b/docs/api/pyswarms.topology.rst index da49d34b..da6d1034 100644 --- a/docs/api/pyswarms.topology.rst +++ b/docs/api/pyswarms.topology.rst @@ -34,4 +34,13 @@ pyswarms.backend.topology.ring module :members: :undoc-members: :show-inheritance: - :special-members: __init__ \ No newline at end of file + :special-members: __init__ + +pyswarms.backend.topology.pyramid module +-------------------------------------- + +.. automodule:: pyswarms.backend.topology.pyramid + :members: + :undoc-members: + :show-inheritance: + :special-members: __init__ diff --git a/pyswarms/__init__.py b/pyswarms/__init__.py index ab501398..768900fe 100644 --- a/pyswarms/__init__.py +++ b/pyswarms/__init__.py @@ -14,7 +14,7 @@ __email__ = "ljvmiranda@gmail.com" __version__ = "0.2.0" -from .single import global_best, local_best +from .single import global_best, local_best, general_optimizer from .discrete import binary -__all__ = ["global_best", "local_best", "binary"] +__all__ = ["global_best", "local_best", "general_optimizer", "binary"] diff --git a/pyswarms/backend/topology/__init__.py b/pyswarms/backend/topology/__init__.py index b9d5a418..49a82abf 100644 --- a/pyswarms/backend/topology/__init__.py +++ b/pyswarms/backend/topology/__init__.py @@ -6,9 +6,10 @@ - update_position(): updates the position-matrix depending on the topology. """ +from .base import Topology from .star import Star from .ring import Ring from .pyramid import Pyramid -__all__ = ["Star", "Ring", "Pyramid"] +__all__ = ["Topology", "Star", "Ring", "Pyramid"] diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index c6f00122..3e3f3494 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -3,8 +3,7 @@ """ A Pyramid Network Topology -This class implements a star topology where all particles are connected in a -pyramid like fashion. +This class implements a pyramid topology where all particles are connected in a N-dimensional simplex fashion. """ # Import from stdlib @@ -21,15 +20,16 @@ # Create a logger logger = logging.getLogger(__name__) + class Pyramid(Topology): def __init__(self): super(Pyramid, self).__init__() - + def compute_gbest(self, swarm): """Updates the global best using a pyramid neighborhood approach - This uses the Delaunay method from :code:`scipy` to triangulate space - with simplices + This uses the Delaunay method from :code:`scipy` to triangulate N-dimensional space + with simplices consisting of swarm particles Parameters ---------- @@ -55,7 +55,7 @@ def compute_gbest(self, swarm): idx = np.array([index_pointer[indices[i]:indices[i+1]] for i in range(swarm.n_particles)]) idx_min = swarm.pbest_cost[idx].argmin(axis=1) best_neighbor = idx[np.arange(len(idx)), idx_min] - + # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) best_pos = swarm.pbest_pos[ @@ -69,7 +69,7 @@ def compute_gbest(self, swarm): raise else: return (best_pos, best_cost) - + def compute_velocity(self, swarm, clamp=None): """Computes the velocity matrix @@ -107,7 +107,7 @@ def compute_velocity(self, swarm, clamp=None): Updated velocity matrix """ return ops.compute_velocity(swarm, clamp) - + def compute_position(self, swarm, bounds=None): """Updates the position matrix diff --git a/pyswarms/single/__init__.py b/pyswarms/single/__init__.py index 26cbc982..34aafcc5 100644 --- a/pyswarms/single/__init__.py +++ b/pyswarms/single/__init__.py @@ -6,5 +6,6 @@ from .global_best import GlobalBestPSO from .local_best import LocalBestPSO +from .general_optimizer import GeneralOptimizerPSO -__all__ = ["GlobalBestPSO", "LocalBestPSO"] +__all__ = ["GlobalBestPSO", "LocalBestPSO", "GeneralOptimizerPSO"] diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py new file mode 100644 index 00000000..b500f21e --- /dev/null +++ b/pyswarms/single/general_optimizer.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 -*- + +""" +A general Particle Swarm Optimization (general PSO) algorithm. + +It takes a set of candidate solutions, and tries to find the best +solution using a position-velocity update method. Uses a user specified +topology. + +The position update can be defined as: + +.. math:: + + x_{i}(t+1) = x_{i}(t) + v_{i}(t+1) + +Where the position at the current timestep :math:`t` is updated using +the computed velocity at :math:`t+1`. Furthermore, the velocity update +is defined as: + +.. math:: + + v_{ij}(t + 1) = m * v_{ij}(t) + c_{1}r_{1j}(t)[y_{ij}(t) − x_{ij}(t)] + + c_{2}r_{2j}(t)[\hat{y}_{j}(t) − x_{ij}(t)] + +Here, :math:`c1` and :math:`c2` are the cognitive and social parameters +respectively. They control the particle's behavior given two choices: (1) to +follow its *personal best* or (2) follow the swarm's *global best* position. +Overall, this dictates if the swarm is explorative or exploitative in nature. +In addition, a parameter :math:`w` controls the inertia of the swarm's +movement. + +An example usage is as follows: + +.. code-block:: python + + import pyswarms as ps + from pyswarms.backend.topology import Pyramid + from pyswarms.utils.functions import single_obj as fx + + # Set-up hyperparameters and topology + options = {'c1': 0.5, 'c2': 0.3, 'w':0.9} + my_topology = Pyramid() + + # Call instance of GlobalBestPSO + optimizer = ps.single.GeneralOptimizerPSO(n_particles=10, dimensions=2, + options=options, topology=my_topology) + + # Perform optimization + stats = optimizer.optimize(fx.sphere_func, iters=100) + +This algorithm was adapted from the earlier works of J. Kennedy and +R.C. Eberhart in Particle Swarm Optimization [IJCNN1995]_. + +.. [IJCNN1995] J. Kennedy and R.C. Eberhart, "Particle Swarm Optimization," + Proceedings of the IEEE International Joint Conference on Neural + Networks, 1995, pp. 1942-1948. +""" +# Import from stdlib +import logging + +# 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 +from ..utils.console_utils import cli_print, end_report + + +class GeneralOptimizerPSO(SwarmOptimizer): + def __init__( + self, + n_particles, + dimensions, + options, + topology, + bounds=None, + velocity_clamp=None, + center=1.00, + ftol=-np.inf, + init_pos=None, + ): + """Initializes the swarm. + + Attributes + ---------- + n_particles : int + number of particles in the swarm. + dimensions : int + number of dimensions in the space. + options : dict with keys :code:`{'c1', 'c2', 'w'}` or :code:`{'c1', 'c2', 'w', 'k', 'p'}` + a dictionary containing the parameters for the specific + optimization technique. + * c1 : float + cognitive parameter + * c2 : float + social parameter + * w : float + inertia parameter + if used with the :code:`Ring` topology the additional + parameters k and p must be included + * k : int + number of neighbors to be considered. Must be a + positive integer less than :code:`n_particles` + * p: int {1,2} + the Minkowski p-norm to use. 1 is the + sum-of-absolute values (or L1 distance) while 2 is + the Euclidean (or L2) distance. + topology : :code:`Topology` object + a :code:`Topology` object that defines the topology to use + in the optimization process + bounds : tuple of :code:`np.ndarray` (default is :code:`None`) + a tuple of size 2 where the first entry is the minimum bound + while the second entry is the maximum bound. Each array must + be of shape :code:`(dimensions,)`. + velocity_clamp : tuple (default is :code:`None`) + a tuple of size 2 where the first entry is the minimum velocity + and the second entry is the maximum velocity. It + sets the limits for velocity clamping. + center : list (default is :code:`None`) + an array of size :code:`dimensions` + ftol : float + relative error in objective_func(best_pos) acceptable for + convergence + """ + super(GeneralOptimizerPSO, self).__init__( + n_particles, + dimensions=dimensions, + options=options, + bounds=bounds, + velocity_clamp=velocity_clamp, + center=center, + ftol=ftol, + init_pos=init_pos, + ) + + # Initialize logger + self.logger = logging.getLogger(__name__) + # Invoke assertions + self.assertions() + # Initialize the resettable attributes + self.reset() + # Initialize the topology and check for type + if not isinstance(topology, Topology): + raise TypeError("Parameter `topology` must be a Topology object") + else: + self.top = topology + + # Case for the Ring topology + if isinstance(topology, Ring): + # Assign k-neighbors and p-value as attributes + self.k, self.p = options["k"], options["p"] + + # Exceptions for the k and p values + if not all(key in self.options for key in ("k", "p")): + raise KeyError("Missing either k or p in options") + if not 0 <= self.k <= self.n_particles: + raise ValueError( + "No. of neighbors must be between 0 and no. " "of particles." + ) + if self.p not in [1, 2]: + raise ValueError( + "p-value should either be 1 (for L1/Minkowski) " + "or 2 (for L2/Euclidean)." + ) + + def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): + """Optimizes the swarm for a number of iterations. + + Performs the optimization to evaluate the objective + function :code:`f` for a number of iterations :code:`iter.` + + Parameters + ---------- + objective_func : function + 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. + kwargs : dict + arguments for the objective function + + Returns + ------- + tuple + the global best cost and the global best position. + """ + + 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.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): + # Update gbest from neighborhood + self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( + self.swarm, self.p, self.k + ) + else: + # Get minima of pbest and check if it's less than gbest + if np.min(self.swarm.pbest_cost) < self.swarm.best_cost: + self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( + self.swarm + ) + # 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 + hist = self.ToHistory( + best_cost=self.swarm.best_cost, + mean_pbest_cost=np.mean(self.swarm.pbest_cost), + mean_neighbor_cost=self.swarm.best_cost, + position=self.swarm.position, + velocity=self.swarm.velocity + ) + self._populate_history(hist) + # Verify stop criteria based on the relative acceptable cost ftol + relative_measure = self.ftol * (1 + np.abs(best_cost_yet_found)) + if ( + np.abs(self.swarm.best_cost - best_cost_yet_found) + < relative_measure + ): + break + # Perform velocity and position updates + self.swarm.velocity = self.top.compute_velocity( + self.swarm, self.velocity_clamp + ) + self.swarm.position = self.top.compute_position( + self.swarm, self.bounds + ) + # 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() + # Write report in log and return final cost and position + end_report( + final_best_cost, final_best_pos, verbose, logger=self.logger + ) + return(final_best_cost, final_best_pos) diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py index 4bf9f399..5b02feb1 100644 --- a/tests/optimizers/conftest.py +++ b/tests/optimizers/conftest.py @@ -8,9 +8,67 @@ import numpy as np # Import from package -from pyswarms.single import GlobalBestPSO, LocalBestPSO +from pyswarms.single import GlobalBestPSO, LocalBestPSO, GeneralOptimizerPSO from pyswarms.discrete import BinaryPSO from pyswarms.utils.functions.single_obj import sphere_func +from pyswarms.backend.topology import Star, Ring, Pyramid + + +@pytest.fixture(scope="module") +def general_opt_history(): + """Returns a GeneralOptimizerPSO with Star topology instance run for 1000 iterations for checking + history""" + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, Star()) + pso.optimize(sphere_func, 1000, verbose=0) + return pso + + +@pytest.fixture(scope="module") +def general_opt_reset(): + """Returns a GeneralOptimizerPSO instance with Star topology that has been run and reset to check + default value""" + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, Star()) + pso.optimize(sphere_func, 10, verbose=0) + pso.reset() + return pso + + +@pytest.fixture(scope="module") +def general_opt_history(): + """Returns a GeneralOptimizerPSO instance with Star topology run for 1000 iterations for checking + history""" + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}, Ring()) + pso.optimize(sphere_func, 1000, verbose=0) + return pso + + +@pytest.fixture(scope="module") +def general_opt_reset(): + """Returns a GeneralOptimizerPSO instance with Star topology that has been run and reset to check + default value""" + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}, Ring()) + pso.optimize(sphere_func, 10, verbose=0) + pso.reset() + return pso + + +@pytest.fixture(scope="module") +def general_opt_history(): + """Returns a GeneralOptimizerPSO instance with Star topology run for 1000 iterations for checking + history""" + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, Pyramid()) + pso.optimize(sphere_func, 1000, verbose=0) + return pso + + +@pytest.fixture(scope="module") +def general_opt_reset(): + """Returns a GeneralOptimizerPSO instance with Star topology that has been run and reset to check + default value""" + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, Pyramid()) + pso.optimize(sphere_func, 10, verbose=0) + pso.reset() + return pso @pytest.fixture(scope="module") @@ -75,3 +133,10 @@ def options(): """Default options dictionary for most PSO use-cases""" options_ = {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2} return options_ + + +@pytest.fixture +def topology(): + """Default topology for most PSO use-cases""" + topology_ = Pyramid() + return topology_ diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py new file mode 100644 index 00000000..d7622ff5 --- /dev/null +++ b/tests/optimizers/test_general_optimizer.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import modules +import pytest +import numpy as np + +# Import from package +from pyswarms.single import GeneralOptimizerPSO +from pyswarms.backend.topology import Ring +from pyswarms.utils.functions.single_obj import sphere_func + + +@pytest.mark.parametrize( + "options", + [{"c2": 0.7, "w": 0.5}, {"c1": 0.5, "w": 0.5}, {"c1": 0.5, "c2": 0.7}], +) +def test_keyword_exception(options, topology): + """Tests if exceptions are thrown when keywords are missing""" + with pytest.raises(KeyError): + GeneralOptimizerPSO(5, 2, options, topology) + + +@pytest.mark.parametrize( + "options", + [ + {"c2": 0.7, "w": 0.5, "k": 2, "p": 2}, + {"c1": 0.5, "w": 0.5, "k": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "k": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2}, + ], +) +def test_keyword_exception(options): + """Tests if exceptions are thrown when keywords are missing and a Ring topology is chosen""" + with pytest.raises(KeyError): + GeneralOptimizerPSO(5, 2, options, Ring()) + + +@pytest.mark.parametrize( + "options", + [ + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": -1, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 6, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 5}, + ], +) +def test_invalid_k_or_p_values(options): + """Tests if exception is thrown when passing + an invalid value for k or p when using a Ring topology""" + with pytest.raises(ValueError): + GeneralOptimizerPSO(5, 2, options, Ring()) + + +@pytest.mark.parametrize( + "topology", + [object(), int(), dict()] +) +def test_topology_type_exception(options, topology): + """Tests if exceptions are thrown when the topology has the wrong type""" + with pytest.raises(TypeError): + GeneralOptimizerPSO(5, 2, options, topology) + + +@pytest.mark.parametrize( + "bounds", + [ + tuple(np.array([-5, -5])), + (np.array([-5, -5, -5]), np.array([5, 5])), + (np.array([-5, -5, -5]), np.array([5, 5, 5])), + ], +) +def test_bounds_size_exception(bounds, options, topology): + """Tests if exceptions are raised when bound sizes are wrong""" + with pytest.raises(IndexError): + GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) + + +@pytest.mark.parametrize( + "bounds", + [ + (np.array([5, 5]), np.array([-5, -5])), + (np.array([5, -5]), np.array([-5, 5])), + ], +) +def test_bounds_maxmin_exception(bounds, options, topology): + """Tests if the max bounds is less than min bounds and vice-versa""" + with pytest.raises(ValueError): + GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) + + +@pytest.mark.parametrize( + "bounds", + [ + [np.array([-5, -5]), np.array([5, 5])], + np.array([np.array([-5, -5]), np.array([5, 5])]), + ], +) +def test_bound_type_exception(bounds, options, topology): + """Tests if exception is raised when bound type is not a tuple""" + with pytest.raises(TypeError): + GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) + + +@pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) +def test_vclamp_shape_exception(velocity_clamp, options, topology): + """Tests if exception is raised when velocity_clamp's size is not equal + to 2""" + with pytest.raises(IndexError): + GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) + + +@pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) +def test_vclamp_maxmin_exception(velocity_clamp, options, topology): + """Tests if the max velocity_clamp is less than min velocity_clamp and + vice-versa""" + with pytest.raises(ValueError): + GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, 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) + + +def test_reset_default_values(gbest_reset): + """Tests if best cost and best pos are set properly when the reset() + method is called""" + assert gbest_reset.swarm.best_cost == np.inf + assert set(gbest_reset.swarm.best_pos) == set(np.array([])) + + +@pytest.mark.parametrize( + "history, expected_shape", + [ + ("cost_history", (1000,)), + ("mean_pbest_history", (1000,)), + ("mean_neighbor_history", (1000,)), + ("pos_history", (1000, 10, 2)), + ("velocity_history", (1000, 10, 2)), + ], +) +def test_training_history_shape(gbest_history, history, expected_shape): + """Test if training histories are of expected shape""" + pso = vars(gbest_history) + assert np.array(pso[history]).shape == expected_shape + + +def test_ftol_effect(options, topology): + """Test if setting the ftol breaks the optimization process accodingly""" + pso = GeneralOptimizerPSO(10, 2, options=options, topology=topology, ftol=1e-1) + pso.optimize(sphere_func, 2000, verbose=0) + assert np.array(pso.cost_history).shape != (2000,) From 38546d66561af3ff405cfe67fce534c49f42bcf2 Mon Sep 17 00:00:00 2001 From: whzup <39431903+whzup@users.noreply.github.com> Date: Mon, 2 Jul 2018 10:29:07 +0200 Subject: [PATCH 22/38] Fix index error in the Pyramid class Fixed an index error in the Pyramid class that occured because the type of the idx array did not match 'int' --- pyswarms/backend/topology/pyramid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 3e3f3494..19ee0ca5 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -53,8 +53,8 @@ def compute_gbest(self, swarm): indices, index_pointer = pyramid.vertex_neighbor_vertices # Insert all the neighbors for each particle in the idx array idx = np.array([index_pointer[indices[i]:indices[i+1]] for i in range(swarm.n_particles)]) - idx_min = swarm.pbest_cost[idx].argmin(axis=1) - best_neighbor = idx[np.arange(len(idx)), idx_min] + idx_min = np.array([swarm.pbest_cost[idx[i]].argmin() for i in range(idx.size)]) + best_neighbor = np.array([idx[i][idx_min[i]] for i in range(idx.size)]).astype(int) # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) From cca8d3cc11ed17e1f4bd20d4e068ef3718c2e9ba Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Sat, 14 Jul 2018 06:04:08 +0200 Subject: [PATCH 23/38] Add Random topology class (#155) Reference: #129 Added a new topology with random neighbors. Added documentation and a test file for it and reworked the documentation of the GeneralOptimizer class to incorporate all available topologies. Simplified the fixture function for the GeneralOptimizer class. Cited the relevant paper for the algorithm implemented. Updated the documentation of the Random class. Especially the __compute_neighbor() method. Added the comments inside the method to the docstring and deleted irrelevant comments. Changed the nested for-loops to one loop with the itertools library. Added a new test for the return value of the __compute_neighbor() method, which checks the shape and the symmetry. Added a new test for the return value of the __compute_neighbors() method, which compares the returned matrix with a preset comparison matrix using a seed. Signed-off-by: Lester James V. Miranda Committed-by: @whzup --- docs/api/pyswarms.topology.rst | 9 + pyswarms/backend/topology/__init__.py | 3 +- pyswarms/backend/topology/pyramid.py | 2 +- pyswarms/backend/topology/random.py | 201 +++++++++++++++++++++ pyswarms/single/general_optimizer.py | 59 ++++-- tests/backend/topology/conftest.py | 7 + tests/backend/topology/test_random.py | 61 +++++++ tests/optimizers/conftest.py | 72 +++----- tests/optimizers/test_general_optimizer.py | 63 ++++++- 9 files changed, 408 insertions(+), 69 deletions(-) create mode 100644 pyswarms/backend/topology/random.py create mode 100644 tests/backend/topology/test_random.py diff --git a/docs/api/pyswarms.topology.rst b/docs/api/pyswarms.topology.rst index da6d1034..8a10782b 100644 --- a/docs/api/pyswarms.topology.rst +++ b/docs/api/pyswarms.topology.rst @@ -44,3 +44,12 @@ pyswarms.backend.topology.pyramid module :undoc-members: :show-inheritance: :special-members: __init__ + +pyswarms.backend.topology.random module +-------------------------------------- + +.. automodule:: pyswarms.backend.topology.random + :members: + :undoc-members: + :show-inheritance: + :special-members: __init__ diff --git a/pyswarms/backend/topology/__init__.py b/pyswarms/backend/topology/__init__.py index 49a82abf..9764b39d 100644 --- a/pyswarms/backend/topology/__init__.py +++ b/pyswarms/backend/topology/__init__.py @@ -10,6 +10,7 @@ from .star import Star from .ring import Ring from .pyramid import Pyramid +from .random import Random -__all__ = ["Topology", "Star", "Ring", "Pyramid"] +__all__ = ["Topology", "Star", "Ring", "Pyramid", "Random"] diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 19ee0ca5..b68c46a1 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -54,7 +54,7 @@ def compute_gbest(self, swarm): # Insert all the neighbors for each particle in the idx array idx = np.array([index_pointer[indices[i]:indices[i+1]] for i in range(swarm.n_particles)]) idx_min = np.array([swarm.pbest_cost[idx[i]].argmin() for i in range(idx.size)]) - best_neighbor = np.array([idx[i][idx_min[i]] for i in range(idx.size)]).astype(int) + best_neighbor = np.array([idx[i][idx_min[i]] for i in range(len(idx))]).astype(int) # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) diff --git a/pyswarms/backend/topology/random.py b/pyswarms/backend/topology/random.py new file mode 100644 index 00000000..6ea47c05 --- /dev/null +++ b/pyswarms/backend/topology/random.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- + +""" +A Random Network Topology + +This class implements a random topology. All particles are connected in a random fashion. +""" + +# Import from stdlib +import logging +import itertools + +# Import modules +import numpy as np +from scipy.sparse.csgraph import connected_components, dijkstra + +# Import from package +from ..import operators as ops +from .base import Topology + +# Create a logger +logger = logging.getLogger(__name__) + + +class Random(Topology): + def __init__(self): + super(Random, self).__init__() + + def compute_gbest(self, swarm, k): + """Update the global best using a random neighborhood approach + + This uses random class from :code:`numpy` to give every particle k + randomly distributed, non-equal neighbors. The resulting topology + is a connected graph. The algorithm to obtain the neighbors was adapted + from [TSWJ2013]. + + [TSWJ2013] Qingjian Ni and Jianming Deng, “A New Logistic Dynamic + Particle Swarm Optimization Algorithm Based on Random Topology,” + The Scientific World Journal, vol. 2013, Article ID 409167, 8 pages, 2013. + https://doi.org/10.1155/2013/409167. + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + k : int + number of neighbors to be considered. Must be a + positive integer less than :code:`n_particles-1` + + Returns + ------- + numpy.ndarray + Best position of shape :code:`(n_dimensions, )` + float + Best cost + """ + try: + adj_matrix = self.__compute_neighbors(swarm, k) + idx = np.array([adj_matrix[i].nonzero()[0] for i in range(swarm.n_particles)]) + idx_min = np.array([swarm.pbest_cost[idx[i]].argmin() for i in range(len(idx))]) + best_neighbor = np.array([idx[i][idx_min[i]] for i in range(len(idx))]).astype(int) + + # Obtain best cost and position + best_cost = np.min(swarm.pbest_cost[best_neighbor]) + best_pos = swarm.pbest_pos[ + np.argmin(swarm.pbest_cost[best_neighbor]) + ] + + except AttributeError: + msg = "Please pass a Swarm class. You passed {}".format( + type(swarm) + ) + logger.error(msg) + raise + else: + return (best_pos, best_cost) + + def compute_velocity(self, swarm, clamp=None): + """Compute the velocity matrix + + This method updates the velocity matrix using the best and current + positions of the swarm. The velocity matrix is computed using the + cognitive and social terms of the swarm. + + A sample usage can be seen with the following: + + .. code-block :: python + + import pyswarms.backend as P + from pyswarms.swarms.backend import Swarm + from pyswarms.backend.topology import Random + + my_swarm = P.create_swarm(n_particles, dimensions) + my_topology = Random() + + for i in range(iters): + # Inside the for-loop + my_swarm.velocity = my_topology.update_velocity(my_swarm, clamp) + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + clamp : tuple of floats (default is :code:`None`) + a tuple of size 2 where the first entry is the minimum velocity + and the second entry is the maximum velocity. It + sets the limits for velocity clamping. + + Returns + ------- + numpy.ndarray + Updated velocity matrix + """ + return ops.compute_velocity(swarm, clamp) + + def compute_position(self, swarm, bounds=None): + """Update the position matrix + + This method updates the position matrix given the current position and + the velocity. If bounded, it waives updating the position. + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + bounds : tuple of :code:`np.ndarray` or list (default is :code:`None`) + a tuple of size 2 where the first entry is the minimum bound while + the second entry is the maximum bound. Each array must be of shape + :code:`(dimensions,)`. + + Returns + ------- + numpy.ndarray + New position-matrix + """ + return ops.compute_position(swarm, bounds) + + def __compute_neighbors(self, swarm, k): + """Helper method to compute the adjacency matrix of the topology + + This method computes the adjacency matrix of the topology using + the randomized algorithm proposed in [TSWJ2013]. The resulting + topology is a connected graph. This is achieved by creating three + matrices: + + * adj_matrix : The adjacency matrix of the generated graph. + It's initialized as an identity matrix to + make sure that every particle has itself as + a neighbour. This matrix is the return + value of the method. + * neighbor_matrix : The matrix of randomly generated neighbors. + This matrix is a matrix of shape + :code:`(swarm.n_particles, k)`: + with randomly generated elements. It's used + to create connections in the adj_matrix. + * dist_matrix : The distance matrix computed with Dijkstra's + algorithm. It is used to determine where the + graph needs edges to change it to a connected + graph. + + .. note:: If the graph isn't connected, it is possible that the + PSO algorithm does not find the best position within + the swarm. + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + k : int + number of neighbors to be considered. Must be a + positive integer less than :code:`n_particles-1` + + Returns + ------- + numpy.ndarray + Adjacency matrix of the topology + """ + + 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)]) + + # 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) + + # Generate connected graph. + 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] == 0: + adj_matrix[i][j] = 1 + + return adj_matrix diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py index b500f21e..b5cc3c0c 100644 --- a/pyswarms/single/general_optimizer.py +++ b/pyswarms/single/general_optimizer.py @@ -64,7 +64,7 @@ # Import from package from ..base import SwarmOptimizer from ..backend.operators import compute_pbest -from ..backend.topology import Topology, Ring +from ..backend.topology import Topology, Ring, Random from ..utils.console_utils import cli_print, end_report @@ -98,18 +98,29 @@ def __init__( social parameter * w : float inertia parameter - if used with the :code:`Ring` topology the additional - parameters k and p must be included + if used with the :code:`Ring` or :code:`Random` topology the additional + parameter k must be included * k : int number of neighbors to be considered. Must be a positive integer less than :code:`n_particles` + if used with the :code:`Ring` topology the additional + parameter p must be included * p: int {1,2} the Minkowski p-norm to use. 1 is the sum-of-absolute values (or L1 distance) while 2 is the Euclidean (or L2) distance. - topology : :code:`Topology` object + topology : pyswarms.backend.topology.Topology a :code:`Topology` object that defines the topology to use - in the optimization process + in the optimization process. The currently available topologies + are: + * Star + All particles are connected + * Ring + Particles are connected with the k nearest neighbours + * Pyramid + Particles are connected in N-dimensional simplices + * Random + Particles are connected to k random particles bounds : tuple of :code:`np.ndarray` (default is :code:`None`) a tuple of size 2 where the first entry is the minimum bound while the second entry is the maximum bound. Each array must @@ -149,22 +160,33 @@ def __init__( # Case for the Ring topology if isinstance(topology, Ring): - # Assign k-neighbors and p-value as attributes - self.k, self.p = options["k"], options["p"] - - # Exceptions for the k and p values - if not all(key in self.options for key in ("k", "p")): - raise KeyError("Missing either k or p in options") - if not 0 <= self.k <= self.n_particles: - raise ValueError( - "No. of neighbors must be between 0 and no. " "of particles." - ) + # Assign p-value as attributes + self.p = options["p"] + # Exceptions for the p value + if "p" not in self.options: + raise KeyError("Missing p in options") if self.p not in [1, 2]: raise ValueError( "p-value should either be 1 (for L1/Minkowski) " "or 2 (for L2/Euclidean)." ) + # Case for Random and Ring topologies + if isinstance(topology, (Random, Ring)): + # Assign k-neighbors as attribute + self.k = options["k"] + if not isinstance(self.k, int): + raise ValueError( + "No. of neighbors must be an integer between" + "0 and no. of particles." + ) + if not 0 <= self.k <= self.n_particles-1: + raise ValueError( + "No. of neighbors must be between 0 and no. " "of particles." + ) + if "k" not in self.options: + raise KeyError("Missing k in options") + def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): """Optimizes the swarm for a number of iterations. @@ -207,6 +229,13 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( self.swarm, self.p, self.k ) + # If the topology is a random topology pass the neighbor attribute to the compute_gbest() method + elif isinstance(self.top, Random): + # Get minima of pbest and check if it's less than gbest + if np.min(self.swarm.pbest_cost) < self.swarm.best_cost: + self.swarm.best_pos, self.swarm.best_cost = self.top.compute_gbest( + self.swarm, self.k + ) else: # Get minima of pbest and check if it's less than gbest if np.min(self.swarm.pbest_cost) < self.swarm.best_cost: diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index bccc1047..b2f42d91 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -25,3 +25,10 @@ def swarm(): "options": {"c1": 0.5, "c2": 1, "w": 2}, } return Swarm(**attrs_at_t) + + +@pytest.fixture +def k(): + """Default neighbor number""" + _k = 1 + return _k diff --git a/tests/backend/topology/test_random.py b/tests/backend/topology/test_random.py new file mode 100644 index 00000000..8457e2a8 --- /dev/null +++ b/tests/backend/topology/test_random.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import modules +import pytest +import numpy as np + +# Import from package +from pyswarms.backend.topology import Random + + +@pytest.mark.parametrize("k", [1, 2]) +def test_update_gbest_neighborhood(swarm, k): + """Test if update_gbest_neighborhood gives the expected return values""" + topology = Random() + pos, cost = topology.compute_gbest(swarm, k=k) + expected_pos = np.array([1, 2, 3]) + expected_cost = 1 + assert (pos == expected_pos).all() + assert cost == expected_cost + + +@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) +def test_compute_velocity_return_values(swarm, clamp): + """Test if compute_velocity() gives the expected shape and range""" + topology = Random() + v = topology.compute_velocity(swarm, clamp) + assert v.shape == swarm.position.shape + if clamp is not None: + assert (clamp[0] <= v).all() and (clamp[1] >= v).all() + + +@pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], +) +def test_compute_position_return_values(swarm, bounds): + """Test if compute_position() gives the expected shape and range""" + topology = Random() + p = topology.compute_position(swarm, bounds) + assert p.shape == swarm.velocity.shape + if bounds is not None: + assert (bounds[0] <= p).all() and (bounds[1] >= p).all() + + +@pytest.mark.parametrize("k", [1, 2]) +def test_compute_neighbors_return_values(swarm, k): + """Test if __compute_neighbors() gives the expected shape and symmetry""" + topology = Random() + adj_matrix = topology._Random__compute_neighbors(swarm, k) + assert adj_matrix.shape == (swarm.n_particles, swarm.n_particles) + assert np.allclose(adj_matrix, adj_matrix.T, atol=1e-8) # Symmetry test + + +def test_compute_neighbors_adjacency_matrix(swarm, k): + """Test if __compute_neighbors() gives the expected matrix""" + np.random.seed(1) + topology = Random() + adj_matrix = topology._Random__compute_neighbors(swarm, k) + comparison_matrix = np.array([[1, 1, 0], [1, 1, 1], [0, 1, 1]]) + assert np.allclose(adj_matrix, comparison_matrix, atol=1e-8) diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py index 5b02feb1..994207cb 100644 --- a/tests/optimizers/conftest.py +++ b/tests/optimizers/conftest.py @@ -11,61 +11,35 @@ from pyswarms.single import GlobalBestPSO, LocalBestPSO, GeneralOptimizerPSO from pyswarms.discrete import BinaryPSO from pyswarms.utils.functions.single_obj import sphere_func -from pyswarms.backend.topology import Star, Ring, Pyramid - - -@pytest.fixture(scope="module") -def general_opt_history(): - """Returns a GeneralOptimizerPSO with Star topology instance run for 1000 iterations for checking - history""" - pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, Star()) - pso.optimize(sphere_func, 1000, verbose=0) - return pso - - -@pytest.fixture(scope="module") -def general_opt_reset(): - """Returns a GeneralOptimizerPSO instance with Star topology that has been run and reset to check - default value""" - pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, Star()) - pso.optimize(sphere_func, 10, verbose=0) - pso.reset() - return pso - - -@pytest.fixture(scope="module") -def general_opt_history(): - """Returns a GeneralOptimizerPSO instance with Star topology run for 1000 iterations for checking +from pyswarms.backend.topology import Star, Ring, Pyramid, Random + + +@pytest.fixture(scope="module", + params=[ + Star(), + Ring(), + Pyramid(), + Random() + ]) +def general_opt_history(top): + """Returns a GeneralOptimizerPSO instance run for 1000 iterations for checking history""" - pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}, Ring()) + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=top) pso.optimize(sphere_func, 1000, verbose=0) return pso -@pytest.fixture(scope="module") -def general_opt_reset(): - """Returns a GeneralOptimizerPSO instance with Star topology that has been run and reset to check - default value""" - pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2}, Ring()) - pso.optimize(sphere_func, 10, verbose=0) - pso.reset() - return pso - - -@pytest.fixture(scope="module") -def general_opt_history(): - """Returns a GeneralOptimizerPSO instance with Star topology run for 1000 iterations for checking - history""" - pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, Pyramid()) - pso.optimize(sphere_func, 1000, verbose=0) - return pso - - -@pytest.fixture(scope="module") -def general_opt_reset(): - """Returns a GeneralOptimizerPSO instance with Star topology that has been run and reset to check +@pytest.fixture(scope="module", + params=[ + Star(), + Ring(), + Pyramid(), + Random() + ]) +def general_opt_reset(top): + """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}, Pyramid()) + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=top) pso.optimize(sphere_func, 10, verbose=0) pso.reset() return pso diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py index d7622ff5..bc9f2382 100644 --- a/tests/optimizers/test_general_optimizer.py +++ b/tests/optimizers/test_general_optimizer.py @@ -7,7 +7,7 @@ # Import from package from pyswarms.single import GeneralOptimizerPSO -from pyswarms.backend.topology import Ring +from pyswarms.backend.topology import Star, Ring, Pyramid, Random from pyswarms.utils.functions.single_obj import sphere_func @@ -31,12 +31,27 @@ def test_keyword_exception(options, topology): {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2}, ], ) -def test_keyword_exception(options): +def test_keyword_exception_ring(options): """Tests if exceptions are thrown when keywords are missing and a Ring topology is chosen""" with pytest.raises(KeyError): GeneralOptimizerPSO(5, 2, options, Ring()) +@pytest.mark.parametrize( + "options", + [ + {"c2": 0.7, "w": 0.5, "k": 2}, + {"c1": 0.5, "w": 0.5, "k": 2}, + {"c1": 0.5, "c2": 0.7, "k": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5}, + ], +) +def test_keyword_exception_random(options): + """Tests if exceptions are thrown when keywords are missing and a Random topology is chosen""" + with pytest.raises(KeyError): + GeneralOptimizerPSO(5, 2, options, Random()) + + @pytest.mark.parametrize( "options", [ @@ -52,6 +67,21 @@ def test_invalid_k_or_p_values(options): GeneralOptimizerPSO(5, 2, options, Ring()) +@pytest.mark.parametrize( + "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} + ], +) +def test_invalid_k_value(options): + """Tests if exception is thrown when passing + an invalid value for k when using a Random topology""" + with pytest.raises(ValueError): + GeneralOptimizerPSO(5, 2, options, Random()) + + @pytest.mark.parametrize( "topology", [object(), int(), dict()] @@ -62,6 +92,10 @@ def test_topology_type_exception(options, topology): GeneralOptimizerPSO(5, 2, options, topology) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize( "bounds", [ @@ -76,6 +110,10 @@ def test_bounds_size_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize( "bounds", [ @@ -89,6 +127,10 @@ def test_bounds_maxmin_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize( "bounds", [ @@ -102,6 +144,10 @@ def test_bound_type_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) def test_vclamp_shape_exception(velocity_clamp, options, topology): """Tests if exception is raised when velocity_clamp's size is not equal @@ -110,6 +156,10 @@ def test_vclamp_shape_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) def test_vclamp_maxmin_exception(velocity_clamp, options, topology): """Tests if the max velocity_clamp is less than min velocity_clamp and @@ -118,6 +168,10 @@ def test_vclamp_maxmin_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @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""" @@ -147,7 +201,10 @@ def test_training_history_shape(gbest_history, history, expected_shape): pso = vars(gbest_history) assert np.array(pso[history]).shape == expected_shape - +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) def test_ftol_effect(options, topology): """Test if setting the ftol breaks the optimization process accodingly""" pso = GeneralOptimizerPSO(10, 2, options=options, topology=topology, ftol=1e-1) From c95661b2b72589f58282245cbb916906217959f5 Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Tue, 17 Jul 2018 06:50:21 +0200 Subject: [PATCH 24/38] Add static/dynamic functionality for topologies (#164) Reference: #129 Added two new attributes to the topologies. `static` decides whether the topology is a static or a dynamic one, it is initialized together with the class. `neighbor_idx` is an array that stores the indices of the neighbors of every particle. Changed all occurrences of topologies to fit the new initialization. Reworked the tests to fit the new functionality and added more parametrization of fixture functions. Updated the documentation to incorporate the new functionality. Signed-off-by: Lester James V. Miranda Commited-by: @whzup --- pyswarms/backend/topology/base.py | 23 ++++++++++- pyswarms/backend/topology/pyramid.py | 35 ++++++++++++----- pyswarms/backend/topology/random.py | 25 ++++++++---- pyswarms/backend/topology/ring.py | 27 ++++++++----- pyswarms/backend/topology/star.py | 3 +- pyswarms/discrete/binary.py | 12 +++--- pyswarms/single/general_optimizer.py | 17 ++++---- pyswarms/single/local_best.py | 8 +++- tests/backend/topology/conftest.py | 4 +- tests/backend/topology/test_pyramid.py | 15 +++++--- tests/backend/topology/test_random.py | 25 +++++++----- tests/backend/topology/test_ring.py | 15 +++++--- tests/optimizers/conftest.py | 44 ++++++++++----------- tests/optimizers/test_general_optimizer.py | 45 +++++----------------- 14 files changed, 173 insertions(+), 125 deletions(-) diff --git a/pyswarms/backend/topology/base.py b/pyswarms/backend/topology/base.py index aedfb9fc..d5a368ff 100644 --- a/pyswarms/backend/topology/base.py +++ b/pyswarms/backend/topology/base.py @@ -12,11 +12,30 @@ :mod:`pyswarms.backend.swarms.Swarm` module. """ +# Import from stdlib +import logging + +# Import from package +from ...utils.console_utils import cli_print + class Topology(object): - def __init__(self, **kwargs): + def __init__(self, static, **kwargs): """Initializes the class""" - pass + + # Initialize logger + self.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) def compute_gbest(self, swarm): """Computes the best particle of the swarm and returns the cost and diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index b68c46a1..7ea4e129 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -22,8 +22,16 @@ class Pyramid(Topology): - def __init__(self): - super(Pyramid, self).__init__() + def __init__(self, static=False): + """Initialize the class + + Parameters + ---------- + static : bool (Default is :code:`False`) + a boolean that decides whether the topology + is static or dynamic + """ + super(Pyramid, self).__init__(static) def compute_gbest(self, swarm): """Updates the global best using a pyramid neighborhood approach @@ -49,12 +57,21 @@ def compute_gbest(self, swarm): best_pos = swarm.pbest_pos[np.argmin(swarm.pbest_cost)] best_cost = np.min(swarm.pbest_cost) else: - pyramid = Delaunay(swarm.position) - indices, index_pointer = pyramid.vertex_neighbor_vertices - # Insert all the neighbors for each particle in the idx array - idx = np.array([index_pointer[indices[i]:indices[i+1]] for i in range(swarm.n_particles)]) - idx_min = np.array([swarm.pbest_cost[idx[i]].argmin() for i in range(idx.size)]) - best_neighbor = np.array([idx[i][idx_min[i]] for i in range(len(idx))]).astype(int) + # 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) + 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)] + ) + + 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))] + ).astype(int) # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) @@ -86,7 +103,7 @@ def compute_velocity(self, swarm, clamp=None): from pyswarms.backend.topology import Pyramid my_swarm = P.create_swarm(n_particles, dimensions) - my_topology = Pyramid() + my_topology = Pyramid(static=False) for i in range(iters): # Inside the for-loop diff --git a/pyswarms/backend/topology/random.py b/pyswarms/backend/topology/random.py index 6ea47c05..d53cdd97 100644 --- a/pyswarms/backend/topology/random.py +++ b/pyswarms/backend/topology/random.py @@ -23,8 +23,15 @@ class Random(Topology): - def __init__(self): - super(Random, self).__init__() + def __init__(self, static=False): + """Initializes the class + + Parameters + ---------- + static : bool (Default is :code:`False`) + a boolean that decides whether the topology + is static or dynamic""" + super(Random, self).__init__(static) def compute_gbest(self, swarm, k): """Update the global best using a random neighborhood approach @@ -55,10 +62,14 @@ def compute_gbest(self, swarm, k): Best cost """ try: - adj_matrix = self.__compute_neighbors(swarm, k) - idx = np.array([adj_matrix[i].nonzero()[0] for i in range(swarm.n_particles)]) - idx_min = np.array([swarm.pbest_cost[idx[i]].argmin() for i in range(len(idx))]) - best_neighbor = np.array([idx[i][idx_min[i]] for i in range(len(idx))]).astype(int) + # 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))]) + best_neighbor = np.array( + [self.neighbor_idx[i][idx_min[i]] for i in range(len(self.neighbor_idx))] + ).astype(int) # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) @@ -91,7 +102,7 @@ def compute_velocity(self, swarm, clamp=None): from pyswarms.backend.topology import Random my_swarm = P.create_swarm(n_particles, dimensions) - my_topology = Random() + my_topology = Random(static=False) for i in range(iters): # Inside the for-loop diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index c1fc30eb..3f1ed440 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -24,8 +24,15 @@ class Ring(Topology): - def __init__(self): - super(Ring, self).__init__() + def __init__(self, static=False): + """Initializes the class + + Parameters + ---------- + static : bool (Default is :code:`False`) + a boolean that decides whether the topology + is static or dynamic""" + super(Ring, self).__init__(static) def compute_gbest(self, swarm, p, k): """Updates the global best using a neighborhood approach @@ -53,21 +60,23 @@ def compute_gbest(self, swarm, p, k): Best cost """ try: - # Obtain the nearest-neighbors for each particle - tree = cKDTree(swarm.position) - _, idx = tree.query(swarm.position, p=p, k=k) + # Check if the topology is static or not and assign neighbors + if (self.static and self.neighbor_idx is None) or not self.static: + # Obtain the nearest-neighbors for each particle + tree = cKDTree(swarm.position) + _, self.neighbor_idx = tree.query(swarm.position, p=p, k=k) # Map the computed costs to the neighbour indices and take the # argmin. If k-neighbors is equal to 1, then the swarm acts # independently of each other. if k == 1: # The minimum index is itself, no mapping needed. - best_neighbor = swarm.pbest_cost[idx][:, np.newaxis].argmin( + best_neighbor = swarm.pbest_cost[self.neighbor_idx][:, np.newaxis].argmin( axis=1 ) else: - idx_min = swarm.pbest_cost[idx].argmin(axis=1) - best_neighbor = idx[np.arange(len(idx)), idx_min] + idx_min = swarm.pbest_cost[self.neighbor_idx].argmin(axis=1) + 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[ @@ -98,7 +107,7 @@ def compute_velocity(self, swarm, clamp=None): from pyswarms.backend.topology import Ring my_swarm = P.create_swarm(n_particles, dimensions) - my_topology = Ring() + my_topology = Ring(static=False) for i in range(iters): # Inside the for-loop diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index a88d7896..c4b52d0f 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -24,7 +24,7 @@ class Star(Topology): def __init__(self): - super(Star, self).__init__() + super(Star, self).__init__(static=False) def compute_gbest(self, swarm): """Obtains the global best cost and position based on a star topology @@ -60,6 +60,7 @@ def compute_gbest(self, swarm): Best cost """ try: + self.neighbor_idx = np.repeat(np.array([np.arange(swarm.n_particles)]), swarm.n_particles, axis=0) best_pos = swarm.pbest_pos[np.argmin(swarm.pbest_cost)] best_cost = np.min(swarm.pbest_cost) except AttributeError: diff --git a/pyswarms/discrete/binary.py b/pyswarms/discrete/binary.py index 1497e4fb..ba2cc2c6 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -19,10 +19,10 @@ For the velocity update rule, a particle compares its current position with respect to its neighbours. The nearest neighbours are being determined by a kD-tree given a distance metric, similar to local-best -PSO. However, this whole behavior can be modified into a global-best PSO -by changing the nearest neighbours equal to the number of particles in -the swarm. In this case, all particles see each other, and thus a global -best particle can be established. +PSO. The neighbours are computed for every iteration. However, this whole +behavior can be modified into a global-best PSO by changing the nearest +neighbours equal to the number of particles in the swarm. In this case, +all particles see each other, and thus a global best particle can be established. In addition, one notable change for binary PSO is that the position update rule is now decided upon by the following case expression: @@ -147,9 +147,9 @@ def __init__( # Initialize the resettable attributes self.reset() # Initialize the topology - self.top = Ring() + 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): """Optimizes the swarm for a number of iterations. Performs the optimization to evaluate the objective diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py index b5cc3c0c..fa22da76 100644 --- a/pyswarms/single/general_optimizer.py +++ b/pyswarms/single/general_optimizer.py @@ -39,7 +39,7 @@ # Set-up hyperparameters and topology options = {'c1': 0.5, 'c2': 0.3, 'w':0.9} - my_topology = Pyramid() + my_topology = Pyramid(static=False) # Call instance of GlobalBestPSO optimizer = ps.single.GeneralOptimizerPSO(n_particles=10, dimensions=2, @@ -104,7 +104,7 @@ def __init__( number of neighbors to be considered. Must be a positive integer less than :code:`n_particles` if used with the :code:`Ring` topology the additional - parameter p must be included + parameters k and p must be included * p: int {1,2} the Minkowski p-norm to use. 1 is the sum-of-absolute values (or L1 distance) while 2 is @@ -115,12 +115,15 @@ def __init__( are: * Star All particles are connected - * Ring - Particles are connected with the k nearest neighbours - * Pyramid + * Ring (static and dynamic) + Particles are connected to the k nearest neighbours + * Pyramid (static and dynamic) Particles are connected in N-dimensional simplices - * Random + * Random (static and dynamic) Particles are connected to k random particles + Static variants of the topologies remain with the same neighbours + over the course of the optimization. Dynamic variants calculate + new neighbours every time step. bounds : tuple of :code:`np.ndarray` (default is :code:`None`) a tuple of size 2 where the first entry is the minimum bound while the second entry is the maximum bound. Each array must @@ -180,7 +183,7 @@ def __init__( "No. of neighbors must be an integer between" "0 and no. of particles." ) - if not 0 <= self.k <= self.n_particles-1: + if not 0 <= self.k <= self.n_particles - 1: raise ValueError( "No. of neighbors must be between 0 and no. " "of particles." ) diff --git a/pyswarms/single/local_best.py b/pyswarms/single/local_best.py index 364cbb1f..a2999f65 100644 --- a/pyswarms/single/local_best.py +++ b/pyswarms/single/local_best.py @@ -31,7 +31,7 @@ In this implementation, a neighbor is selected via a k-D tree imported from :code:`scipy`. Distance are computed with either the L1 or L2 distance. The nearest-neighbours are then queried from -this k-D tree. +this k-D tree. They are computed for every iteration. An example usage is as follows: @@ -113,6 +113,7 @@ def __init__( center=1.00, ftol=-np.inf, init_pos=None, + static=False ): """Initializes the swarm. @@ -151,6 +152,9 @@ def __init__( the Minkowski p-norm to use. 1 is the sum-of-absolute values (or L1 distance) while 2 is the Euclidean (or L2) distance. + static: bool (Default is :code:`False`) + a boolean that decides whether the Ring topology + used is static or dynamic """ # Initialize logger self.logger = logging.getLogger(__name__) @@ -172,7 +176,7 @@ def __init__( # Initialize the resettable attributes self.reset() # Initialize the topology - self.top = Ring() + self.top = Ring(static=static) def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): """Optimizes the swarm for a number of iterations. diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index b2f42d91..d4e5e199 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -11,7 +11,7 @@ from pyswarms.backend.swarms import Swarm -@pytest.fixture +@pytest.fixture(scope="module") def swarm(): """A contrived instance of the Swarm class at a certain timestep""" attrs_at_t = { @@ -27,7 +27,7 @@ def swarm(): return Swarm(**attrs_at_t) -@pytest.fixture +@pytest.fixture(scope="module") def k(): """Default neighbor number""" _k = 1 diff --git a/tests/backend/topology/test_pyramid.py b/tests/backend/topology/test_pyramid.py index 36f877f6..54386836 100644 --- a/tests/backend/topology/test_pyramid.py +++ b/tests/backend/topology/test_pyramid.py @@ -9,9 +9,10 @@ from pyswarms.backend.topology import Pyramid -def test_compute_gbest_return_values(swarm): +@pytest.mark.parametrize("static", [True, False]) +def test_compute_gbest_return_values(swarm, static): """Test if compute_gbest() gives the expected return values""" - topology = Pyramid() + topology = Pyramid(static=static) expected_cost = 1 expected_pos = np.array([1, 2, 3]) pos, cost = topology.compute_gbest(swarm) @@ -19,23 +20,25 @@ def test_compute_gbest_return_values(swarm): assert (pos == expected_pos).all() +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp): +def test_compute_velocity_return_values(swarm, clamp, static): """Test if compute_velocity() gives the expected shape and range""" - topology = Pyramid() + topology = Pyramid(static=static) v = topology.compute_velocity(swarm, clamp) assert v.shape == swarm.position.shape if clamp is not None: assert (clamp[0] <= v).all() and (clamp[1] >= v).all() +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "bounds", [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], ) -def test_compute_position_return_values(swarm, bounds): +def test_compute_position_return_values(swarm, bounds, static): """Test if compute_position() gives the expected shape and range""" - topology = Pyramid() + topology = Pyramid(static=static) p = topology.compute_position(swarm, bounds) assert p.shape == swarm.velocity.shape if bounds is not None: diff --git a/tests/backend/topology/test_random.py b/tests/backend/topology/test_random.py index 8457e2a8..0955d777 100644 --- a/tests/backend/topology/test_random.py +++ b/tests/backend/topology/test_random.py @@ -9,10 +9,11 @@ from pyswarms.backend.topology import Random +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize("k", [1, 2]) -def test_update_gbest_neighborhood(swarm, k): +def test_update_gbest_neighborhood(swarm, k, static): """Test if update_gbest_neighborhood gives the expected return values""" - topology = Random() + topology = Random(static=static) pos, cost = topology.compute_gbest(swarm, k=k) expected_pos = np.array([1, 2, 3]) expected_cost = 1 @@ -20,42 +21,46 @@ def test_update_gbest_neighborhood(swarm, k): assert cost == expected_cost +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp): +def test_compute_velocity_return_values(swarm, clamp, static): """Test if compute_velocity() gives the expected shape and range""" - topology = Random() + topology = Random(static=static) v = topology.compute_velocity(swarm, clamp) assert v.shape == swarm.position.shape if clamp is not None: assert (clamp[0] <= v).all() and (clamp[1] >= v).all() +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "bounds", [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], ) -def test_compute_position_return_values(swarm, bounds): +def test_compute_position_return_values(swarm, bounds, static): """Test if compute_position() gives the expected shape and range""" - topology = Random() + topology = Random(static=static) p = topology.compute_position(swarm, bounds) assert p.shape == swarm.velocity.shape if bounds is not None: assert (bounds[0] <= p).all() and (bounds[1] >= p).all() +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize("k", [1, 2]) -def test_compute_neighbors_return_values(swarm, k): +def test_compute_neighbors_return_values(swarm, k, static): """Test if __compute_neighbors() gives the expected shape and symmetry""" - topology = Random() + topology = Random(static=static) adj_matrix = topology._Random__compute_neighbors(swarm, k) assert adj_matrix.shape == (swarm.n_particles, swarm.n_particles) assert np.allclose(adj_matrix, adj_matrix.T, atol=1e-8) # Symmetry test -def test_compute_neighbors_adjacency_matrix(swarm, k): +@pytest.mark.parametrize("static", [True, False]) +def test_compute_neighbors_adjacency_matrix(swarm, k, static): """Test if __compute_neighbors() gives the expected matrix""" np.random.seed(1) - topology = Random() + topology = Random(static=static) adj_matrix = topology._Random__compute_neighbors(swarm, k) comparison_matrix = np.array([[1, 1, 0], [1, 1, 1], [0, 1, 1]]) assert np.allclose(adj_matrix, comparison_matrix, atol=1e-8) diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index 58306ab8..f03ee87d 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -9,11 +9,12 @@ from pyswarms.backend.topology import Ring +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize("k", [1, 2, 3]) @pytest.mark.parametrize("p", [1, 2]) -def test_update_gbest_neighborhood(swarm, p, k): +def test_update_gbest_neighborhood(swarm, p, k, static): """Test if update_gbest_neighborhood gives the expected return values""" - topology = Ring() + topology = Ring(static=static) pos, cost = topology.compute_gbest(swarm, p=p, k=k) expected_pos = np.array([1, 2, 3]) expected_cost = 1 @@ -21,23 +22,25 @@ def test_update_gbest_neighborhood(swarm, p, k): assert cost == expected_cost +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) -def test_compute_velocity_return_values(swarm, clamp): +def test_compute_velocity_return_values(swarm, clamp, static): """Test if compute_velocity() gives the expected shape and range""" - topology = Ring() + topology = Ring(static=static) v = topology.compute_velocity(swarm, clamp) assert v.shape == swarm.position.shape if clamp is not None: assert (clamp[0] <= v).all() and (clamp[1] >= v).all() +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "bounds", [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], ) -def test_compute_position_return_values(swarm, bounds): +def test_compute_position_return_values(swarm, bounds, static): """Test if compute_position() gives the expected shape and range""" - topology = Ring() + topology = Ring(static=static) p = topology.compute_position(swarm, bounds) assert p.shape == swarm.velocity.shape if bounds is not None: diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py index 994207cb..ba1f4697 100644 --- a/tests/optimizers/conftest.py +++ b/tests/optimizers/conftest.py @@ -14,32 +14,20 @@ from pyswarms.backend.topology import Star, Ring, Pyramid, Random -@pytest.fixture(scope="module", - params=[ - Star(), - Ring(), - Pyramid(), - Random() - ]) -def general_opt_history(top): +@pytest.fixture(scope="module") +def general_opt_history(topology): """Returns a GeneralOptimizerPSO instance run for 1000 iterations for checking history""" - pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=top) + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=topology) pso.optimize(sphere_func, 1000, verbose=0) return pso -@pytest.fixture(scope="module", - params=[ - Star(), - Ring(), - Pyramid(), - Random() - ]) -def general_opt_reset(top): +@pytest.fixture(scope="module") +def general_opt_reset(topology): """Returns a GeneralOptimizerPSO instance that has been run and reset to check default value""" - pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=top) + pso = GeneralOptimizerPSO(10, 2, {"c1": 0.5, "c2": 0.7, "w": 0.5}, topology=topology) pso.optimize(sphere_func, 10, verbose=0) pso.reset() return pso @@ -109,8 +97,20 @@ def options(): return options_ -@pytest.fixture -def topology(): - """Default topology for most PSO use-cases""" - topology_ = Pyramid() +@pytest.fixture(params=[ + Star(), + Ring(static=False), Ring(static=True), + Pyramid(static=False), Pyramid(static=True), + Random(static=False), Random(static=True) + ]) +def topology(request): + """Parametrized topology parameter""" + topology_ = request.param return topology_ + + +@pytest.fixture(scope="module", params=[True, False]) +def static(request): + """Parametrized static parameter""" + static_ = request.param + return static_ diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py index bc9f2382..5d396dfb 100644 --- a/tests/optimizers/test_general_optimizer.py +++ b/tests/optimizers/test_general_optimizer.py @@ -31,10 +31,10 @@ def test_keyword_exception(options, topology): {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2}, ], ) -def test_keyword_exception_ring(options): +def test_keyword_exception_ring(options, static): """Tests if exceptions are thrown when keywords are missing and a Ring topology is chosen""" with pytest.raises(KeyError): - GeneralOptimizerPSO(5, 2, options, Ring()) + GeneralOptimizerPSO(5, 2, options, Ring(static=static)) @pytest.mark.parametrize( @@ -46,10 +46,10 @@ def test_keyword_exception_ring(options): {"c1": 0.5, "c2": 0.7, "w": 0.5}, ], ) -def test_keyword_exception_random(options): +def test_keyword_exception_random(options, static): """Tests if exceptions are thrown when keywords are missing and a Random topology is chosen""" with pytest.raises(KeyError): - GeneralOptimizerPSO(5, 2, options, Random()) + GeneralOptimizerPSO(5, 2, options, Random(static=static)) @pytest.mark.parametrize( @@ -60,11 +60,11 @@ def test_keyword_exception_random(options): {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 5}, ], ) -def test_invalid_k_or_p_values(options): +def test_invalid_k_or_p_values(options, static): """Tests if exception is thrown when passing an invalid value for k or p when using a Ring topology""" with pytest.raises(ValueError): - GeneralOptimizerPSO(5, 2, options, Ring()) + GeneralOptimizerPSO(5, 2, options, Ring(static=static)) @pytest.mark.parametrize( @@ -75,11 +75,11 @@ def test_invalid_k_or_p_values(options): {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 0.5} ], ) -def test_invalid_k_value(options): +def test_invalid_k_value(options, static): """Tests if exception is thrown when passing an invalid value for k when using a Random topology""" with pytest.raises(ValueError): - GeneralOptimizerPSO(5, 2, options, Random()) + GeneralOptimizerPSO(5, 2, options, Random(static=static)) @pytest.mark.parametrize( @@ -92,10 +92,6 @@ def test_topology_type_exception(options, topology): GeneralOptimizerPSO(5, 2, options, topology) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize( "bounds", [ @@ -110,10 +106,6 @@ def test_bounds_size_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize( "bounds", [ @@ -127,10 +119,6 @@ def test_bounds_maxmin_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize( "bounds", [ @@ -144,10 +132,6 @@ def test_bound_type_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) def test_vclamp_shape_exception(velocity_clamp, options, topology): """Tests if exception is raised when velocity_clamp's size is not equal @@ -156,10 +140,6 @@ def test_vclamp_shape_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) def test_vclamp_maxmin_exception(velocity_clamp, options, topology): """Tests if the max velocity_clamp is less than min velocity_clamp and @@ -168,10 +148,6 @@ def test_vclamp_maxmin_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @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""" @@ -201,10 +177,7 @@ def test_training_history_shape(gbest_history, history, expected_shape): pso = vars(gbest_history) assert np.array(pso[history]).shape == expected_shape -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) + def test_ftol_effect(options, topology): """Test if setting the ftol breaks the optimization process accodingly""" pso = GeneralOptimizerPSO(10, 2, options=options, topology=topology, ftol=1e-1) From b90c9738980164b0cc8ecd34ebe91f1c5d9aa24d Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Wed, 18 Jul 2018 05:58:12 +0200 Subject: [PATCH 25/38] Add test for the neighbor_idx attribute of topologies (#171) Added a new test to the topology classes for the neighbor_idx attribute, checks if it is assigned. Changed the way the neighbor_idx attribute is assigned in the Star and Pyramid topology. Signed-off-by: Lester James V. Miranda Committed-by: @whzup --- pyswarms/backend/topology/pyramid.py | 1 + pyswarms/backend/topology/star.py | 2 +- tests/backend/topology/test_pyramid.py | 8 ++++++++ tests/backend/topology/test_random.py | 8 ++++++++ tests/backend/topology/test_ring.py | 10 ++++++++++ tests/backend/topology/test_star.py | 7 +++++++ 6 files changed, 35 insertions(+), 1 deletion(-) diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 7ea4e129..3d618e82 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -54,6 +54,7 @@ def compute_gbest(self, swarm): try: # If there are less than 5 particles they are all connected if swarm.n_particles < 5: + 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: diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index c4b52d0f..9ddff269 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -60,7 +60,7 @@ def compute_gbest(self, swarm): Best cost """ try: - self.neighbor_idx = np.repeat(np.array([np.arange(swarm.n_particles)]), swarm.n_particles, axis=0) + 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/tests/backend/topology/test_pyramid.py b/tests/backend/topology/test_pyramid.py index 54386836..bd8bf359 100644 --- a/tests/backend/topology/test_pyramid.py +++ b/tests/backend/topology/test_pyramid.py @@ -43,3 +43,11 @@ def test_compute_position_return_values(swarm, bounds, static): assert p.shape == swarm.velocity.shape if bounds is not None: assert (bounds[0] <= p).all() and (bounds[1] >= p).all() + + +@pytest.mark.parametrize("static", [True, False]) +def test_neighbor_idx(swarm, static): + """Test if the neighbor_idx attribute is assigned""" + topology = Pyramid(static=static) + p = topology.compute_gbest(swarm) + assert topology.neighbor_idx is not None diff --git a/tests/backend/topology/test_random.py b/tests/backend/topology/test_random.py index 0955d777..d1ba10c4 100644 --- a/tests/backend/topology/test_random.py +++ b/tests/backend/topology/test_random.py @@ -64,3 +64,11 @@ def test_compute_neighbors_adjacency_matrix(swarm, k, static): adj_matrix = topology._Random__compute_neighbors(swarm, k) comparison_matrix = np.array([[1, 1, 0], [1, 1, 1], [0, 1, 1]]) assert np.allclose(adj_matrix, comparison_matrix, atol=1e-8) + + +@pytest.mark.parametrize("static", [True, False]) +def test_neighbor_idx(swarm, k, static): + """Test if the neighbor_idx attribute is assigned""" + topology = Random(static=static) + p = topology.compute_gbest(swarm, k) + assert topology.neighbor_idx is not None diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index f03ee87d..eb776a7a 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -45,3 +45,13 @@ def test_compute_position_return_values(swarm, bounds, static): assert p.shape == swarm.velocity.shape if bounds is not None: assert (bounds[0] <= p).all() and (bounds[1] >= p).all() + + +@pytest.mark.parametrize("static", [True, False]) +@pytest.mark.parametrize("k", [1, 2, 3]) +@pytest.mark.parametrize("p", [1, 2]) +def test_neighbor_idx(swarm, static, p , k): + """Test if the neighbor_idx attribute is assigned""" + topology = Ring(static=static) + p = topology.compute_gbest(swarm, p=p, k=k) + assert topology.neighbor_idx is not None diff --git a/tests/backend/topology/test_star.py b/tests/backend/topology/test_star.py index 71eca08c..d47dd5d1 100644 --- a/tests/backend/topology/test_star.py +++ b/tests/backend/topology/test_star.py @@ -40,3 +40,10 @@ def test_compute_position_return_values(swarm, bounds): assert p.shape == swarm.velocity.shape if bounds is not None: assert (bounds[0] <= p).all() and (bounds[1] >= p).all() + + +def test_neighbor_idx(swarm): + """Test if the neighbor_idx attribute is assigned""" + topology = Star() + p = topology.compute_gbest(swarm) + assert topology.neighbor_idx is not None From 966516b83945ae7879aefab824f5398cde56f928 Mon Sep 17 00:00:00 2001 From: Jay Speidell Date: Tue, 17 Jul 2018 23:58:00 -0700 Subject: [PATCH 26/38] Add plotter functionalities (#172) Resolves #167 1. The designer class in formatters.py is used for 3D graphing, but the default label list attribute only includes two axes. This causes an index out of range error. I added "z-axis". 2. The delta attribute of the Mesher class in formatters.py is 0.001. This is too detailed and can lead to mesher objects that are large enough to cause memory errors. I changed it to 0.1. 3. The user can't choose a colormap when graphing, so I added a colormaps property to the Designer class in formatters.py to allow the user to choose whichever one they want. It uses the same style as other properties of the class, and the module imports matplotlib.cm to validate both types of colormaps in matplotlib. I edited plotters/plot_countour to initialize a Designer object with a default colormap if none is provided. I've tested with every category of colormap as well as custom colormaps and verified that it is working as intended. Signed-off-by: Lester James V. Miranda Commited-with: Jay Speidell (@jayspeidell) --- pyswarms/utils/plotters/formatters.py | 10 ++++++++-- pyswarms/utils/plotters/plotters.py | 9 ++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index f20899c6..94802d65 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -10,6 +10,7 @@ import numpy as np from attr import attrs, attrib from attr.validators import instance_of +from matplotlib import cm, colors @attrs @@ -57,10 +58,15 @@ class Designer(object): ) legend = attrib(validator=instance_of(str), default="Cost") label = attrib( - validator=instance_of((str, list, tuple)), default=["x-axis", "y-axis"] + validator=instance_of((str, list, tuple)), + default=["x-axis", "y-axis", "z-axis"], ) limits = attrib( - validator=instance_of((list, tuple)), default=[(-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, ) diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py index f317dc6f..f2ce0e0a 100644 --- a/pyswarms/utils/plotters/plotters.py +++ b/pyswarms/utils/plotters/plotters.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -r""" +""" Plotting tool for Optimizer Analysis This module is built on top of :code:`matplotlib` to render quick and easy @@ -322,12 +322,14 @@ def plot_surface( designer = Designer( limits=[(-1, 1), (-1, 1), (-1, 1)], label=["x-axis", "y-axis", "z-axis"], + colormap=cm.viridis, ) # If no Animator class supplied, use defaults if animator is None: animator = Animator() + # Get number of iterations # If ax is default, then create new plot. Set-up the figure, the # axis, and the plot element that we want to animate if canvas is None: @@ -338,7 +340,6 @@ def plot_surface( # Initialize 3D-axis ax = Axes3D(fig) - # Get number of iterations n_iters = len(pos_history) # Customize plot @@ -353,7 +354,9 @@ def plot_surface( # Make a contour map if possible if mesher is not None: xx, yy, zz, = _mesh(mesher) - ax.plot_surface(xx, yy, zz, cmap=cm.viridis, alpha=mesher.alpha) + ax.plot_surface( + xx, yy, zz, cmap=designer.colormap, alpha=mesher.alpha + ) # Mark global best if possible if mark is not None: From 4d0e69a8e9cb54eff45c41016d796cd92da30bf8 Mon Sep 17 00:00:00 2001 From: Jay Speidell Date: Wed, 18 Jul 2018 10:04:22 -0700 Subject: [PATCH 27/38] Add objective functions (#168) Reference: #158 Verified Goldstein and Booth functions working and removed TODO tags. Fixed Bulkin No 6 and removed TODO tag. Implemented Cross-in-Tray, Easom, Eggholder, Himmelblau's, Holder Table, and Three Hump Camel functions. The file is getting long so I sorted the functions and added a list of functions and their corresponding function name to the docstring. Added the new functions in the test files for the objective functions. Signed-off-by: Aaron (@whzup) Commited-with: Jay Speidell (@jayspeidell) --- pyswarms/utils/functions/single_obj.py | 498 ++++++++++++++---- tests/utils/functions/conftest.py | 2 +- .../utils/functions/test_singleobj_bounds.py | 91 +++- tests/utils/functions/test_singleobj_dims.py | 53 +- .../utils/functions/test_singleobj_return.py | 130 ++++- .../functions/test_singleobj_returndims.py | 70 ++- 6 files changed, 675 insertions(+), 169 deletions(-) diff --git a/pyswarms/utils/functions/single_obj.py b/pyswarms/utils/functions/single_obj.py index b1e4aa05..a4dce495 100644 --- a/pyswarms/utils/functions/single_obj.py +++ b/pyswarms/utils/functions/single_obj.py @@ -15,6 +15,25 @@ of a new objective function, be sure to perform unittesting in order to check if all functions implemented adheres to the design pattern stated above. + +Function list: +- Ackley's, ackley_func +- Beale, beale_func +- Booth, booth_func +- Bukin's No 6, bukin6_func +- Cross-in-Tray, crossintray_func +- Easom, easom_func +- Eggholder, eggholder_func +- Goldstein, goldstein_func +- Himmelblau's, himmelblau_func +- Holder Table, holdertable_func +- Levi, levi_func +- Matyas, matyas_func +- Rastrigin, rastrigin_func +- Rosenbrock, rosenbrock_func +- Schaffer No 2, schaffer2_func +- Sphere, sphere_func +- Three Hump Camel, threehump_func """ # Import from __future__ @@ -26,11 +45,11 @@ import numpy as np -def sphere_func(x): - """Sphere objective function. +def ackley_func(x): + """Ackley's objective function. - Has a global minimum at :code:`0` and with a search domain of - :code:`[-inf, inf]` + Has a global minimum of `0` at :code:`f(0,0,...,0)` with a search + domain of [-32, 32] Parameters ---------- @@ -41,17 +60,32 @@ def sphere_func(x): ------- numpy.ndarray computed cost of size :code:`(n_particles, )` + + + ------ + ValueError + When the input is out of bounds with respect to the function + domain """ - j = (x ** 2.0).sum(axis=1) + if not np.logical_and(x >= -32, x <= 32).all(): + raise ValueError("Input for Ackley function must be within [-32, 32].") + + d = x.shape[1] + j = ( + -20.0 * np.exp(-0.2 * np.sqrt((1 / d) * (x ** 2).sum(axis=1))) + - np.exp((1 / float(d)) * np.cos(2 * np.pi * x).sum(axis=1)) + + 20.0 + + np.exp(1) + ) return j -def rastrigin_func(x): - """Rastrigin objective function. +def beale_func(x): + """Beale objective function. - Has a global minimum at :code:`f(0,0,...,0)` with a search - domain of :code:`[-5.12, 5.12]` + Only takes two dimensions and has a global minimum of `0` at + :code:`f([3,0.5])` Its domain is bounded between :code:`[-4.5, 4.5]` Parameters ---------- @@ -65,26 +99,36 @@ def rastrigin_func(x): Raises ------ + IndexError + When the input dimensions is greater than what the function + allows ValueError When the input is out of bounds with respect to the function domain """ - if not np.logical_and(x >= -5.12, x <= 5.12).all(): + if not x.shape[1] == 2: + raise IndexError("Beale function only takes two-dimensional input.") + if not np.logical_and(x >= -4.5, x <= 4.5).all(): raise ValueError( - "Input for Rastrigin function must be within " "[-5.12, 5.12]." + "Input for Beale function must be within " "[-4.5, 4.5]." ) - d = x.shape[1] - j = 10.0 * d + (x ** 2.0 - 10.0 * np.cos(2.0 * np.pi * x)).sum(axis=1) + x_ = x[:, 0] + y_ = x[:, 1] + j = ( + (1.5 - x_ + x_ * y_) ** 2.0 + + (2.25 - x_ + x_ * y_ ** 2.0) ** 2.0 + + (2.625 - x_ + x_ * y_ ** 3.0) ** 2.0 + ) return j -def ackley_func(x): - """Ackley's objective function. +def booth_func(x): + """Booth's objective function. - Has a global minimum at :code:`f(0,0,...,0)` with a search - domain of [-32, 32] + Only takes two dimensions and has a global minimum of `0` at + :code:`f([1,3])`. Its domain is bounded between :code:`[-10, 10]` Parameters ---------- @@ -98,31 +142,83 @@ def ackley_func(x): Raises ------ + IndexError + When the input dimensions is greater than what the function + allows ValueError When the input is out of bounds with respect to the function domain """ - if not np.logical_and(x >= -32, x <= 32).all(): - raise ValueError("Input for Ackley function must be within [-32, 32].") + if not x.shape[1] == 2: + raise IndexError("Booth function only takes two-dimensional input.") + if not np.logical_and(x >= -10, x <= 10).all(): + raise ValueError("Input for Booth function must be within [-10, 10].") - d = x.shape[1] - j = ( - -20.0 * np.exp(-0.2 * np.sqrt((1 / d) * (x ** 2).sum(axis=1))) - - np.exp((1 / float(d)) * np.cos(2 * np.pi * x).sum(axis=1)) - + 20.0 - + np.exp(1) + x_ = x[:, 0] + y_ = x[:, 1] + j = (x_ + 2 * y_ - 7) ** 2.0 + (2 * x_ + y_ - 5) ** 2.0 + + return j + + +def bukin6_func(x): + """Bukin N. 6 Objective Function + + Only takes two dimensions and has a global minimum of `0` at + :code:`f([-10,1])`. Its coordinates are bounded by: + * x[:,0] must be within [-15, -5] + * x[:,1] must be within [-3, 3] + + Parameters + ---------- + x : numpy.ndarray + set of inputs of shape :code:`(n_particles, dimensions)` + + Returns + ------- + numpy.ndarray + computed cost of size :code:`(n_particles, )` + + Raises + ------ + IndexError + When the input dimensions is greater than what the function + allows + ValueError + When the input is out of bounds with respect to the function + domain + """ + if not x.shape[1] == 2: + raise IndexError( + "Bukin N. 6 function only takes two-dimensional " "input." + ) + if not np.logical_and(x[:, 0] >= -15, x[:, 0] <= -5).all(): + raise ValueError( + "x-coord for Bukin N. 6 function must be within " "[-15, -5]." + ) + if not np.logical_and(x[:, 1] >= -3, x[:, 1] <= 3).all(): + raise ValueError( + "y-coord for Bukin N. 6 function must be within " "[-3, 3]." + ) + + x_ = x[:, 0] + y_ = x[:, 1] + j = 100 * np.sqrt(np.absolute(y_ - 0.01 * x_ ** 2.0)) + 0.01 * np.absolute( + x_ + 10 ) return j -def rosenbrock_func(x): - """Rosenbrock objective function. +def crossintray_func(x): + """Cross-in-tray objective function. - Also known as the Rosenbrock's valley or Rosenbrock's banana - function. Has a global minimum of :code:`np.ones(dimensions)` where - :code:`dimensions` is :code:`x.shape[1]`. The search domain is - :code:`[-inf, inf]`. + Only takes two dimensions and has a four equal global minimums + of `-2.06261` at :code:`f([1.34941, -1.34941])`, :code:`f([1.34941, 1.34941])`, + :code:`f([-1.34941, 1.34941])`, and :code:`f([-1.34941, -1.34941])`. + Its coordinates are bounded within :code:`[-10,10]`. + + Best visualized in the full domain and a range of :code:`[-2.0, -0.5]`. Parameters ---------- @@ -133,20 +229,49 @@ def rosenbrock_func(x): ------- numpy.ndarray computed cost of size :code:`(n_particles, )` + + Raises + ------ + IndexError + When the input dimensions is greater than what the function + allows + ValueError + When the input is out of bounds with respect to the function + domain """ + if not x.shape[1] == 2: + raise IndexError( + "Cross-in-tray function only takes two-dimensional input." + ) + if not np.logical_and(x >= -10, x <= 10).all(): + raise ValueError( + "Input for cross-in-tray function must be within [-10, 10]." + ) - r = np.sum( - 100 * (x.T[1:] - x.T[:-1] ** 2.0) ** 2 + (1 - x.T[:-1]) ** 2.0, axis=0 + x_ = x[:, 0] + y_ = x[:, 1] + + j = -0.0001 * np.power( + np.abs( + np.sin(x_) + * np.sin(y_) + * np.exp(np.abs(100 - (np.sqrt(x_ ** 2 + y_ ** 2) / np.pi))) + ) + + 1, + 0.1, ) - return r + return j -def beale_func(x): - """Beale objective function. +def easom_func(x): + """Easom objective function. - Only takes two dimensions and has a global minimum at - :code:`f([3,0.5])` Its domain is bounded between :code:`[-4.5, 4.5]` + Only takes two dimensions and has a global minimum of + `-1` at :code:`f([pi, pi])`. + Its coordinates are bounded within :code:`[-100,100]`. + + Best visualized in the domain of :code:`[-5, 5]` and a range of :code:`[-1, 0.2]`. Parameters ---------- @@ -168,30 +293,80 @@ def beale_func(x): domain """ if not x.shape[1] == 2: - raise IndexError("Beale function only takes two-dimensional input.") - if not np.logical_and(x >= -4.5, x <= 4.5).all(): + raise IndexError("Easom function only takes two-dimensional input.") + if not np.logical_and(x >= -100, x <= 100).all(): raise ValueError( - "Input for Beale function must be within " "[-4.5, 4.5]." + "Input for Easom function must be within [-100, 100]." ) x_ = x[:, 0] y_ = x[:, 1] + j = ( - (1.5 - x_ + x_ * y_) ** 2.0 - + (2.25 - x_ + x_ * y_ ** 2.0) ** 2.0 - + (2.625 - x_ + x_ * y_ ** 3.0) ** 2.0 + -1 + * np.cos(x_) + * np.cos(y_) + * np.exp(-1 * ((x_ - np.pi) ** 2 + (y_ - np.pi) ** 2)) + ) + + return j + + +def eggholder_func(x): + """Eggholder objective function. + + Only takes two dimensions and has a global minimum of + `-959.6407` at :code:`f([512, 404.3219])`. + Its coordinates are bounded within :code:`[-512, 512]`. + + Best visualized in the full domain and a range of :code:`[-1000, 1000]`. + + Parameters + ---------- + x : numpy.ndarray + set of inputs of shape :code:`(n_particles, dimensions)` + + Returns + ------- + numpy.ndarray + computed cost of size :code:`(n_particles, )` + + Raises + ------ + IndexError + When the input dimensions is greater than what the function + allows + ValueError + When the input is out of bounds with respect to the function + domain + """ + if not x.shape[1] == 2: + raise IndexError( + "Eggholder function only takes two-dimensional input." + ) + if not np.logical_and(x >= -512, x <= 512).all(): + raise ValueError( + "Input for Eggholder function must be within [-512, 512]." + ) + + x_ = x[:, 0] + y_ = x[:, 1] + + j = -(y_ + 47) * np.sin(np.sqrt(np.abs((x_ / 2) + y_ + 47))) - x_ * np.sin( + np.sqrt(np.abs(x_ - (y_ + 47))) ) return j -# TODO: Implement Goldstein-Price's Function def goldstein_func(x): """Goldstein-Price's objective function. Only takes two dimensions and has a global minimum at :code:`f([0,-1])`. Its domain is bounded between :code:`[-2, 2]` + Best visualized in the domain of :code:`[-1.3,1.3]` and range :code:`[-1,8000]` + Parameters ---------- x : numpy.ndarray @@ -249,12 +424,15 @@ def goldstein_func(x): return j -# TODO: Implement Booth's Function -def booth_func(x): - """Booth's objective function. +def himmelblau_func(x): + """Himmelblau's objective function - Only takes two dimensions and has a global minimum at - :code:`f([1,3])`. Its domain is bounded between :code:`[-10, 10]` + Only takes two dimensions and has a four equal global minimums + of zero at :code:`f([3.0,2.0])`, :code:`f([-2.805118,3.131312])`, + :code:`f([-3.779310,-3.283186])`, and :code:`f([3.584428,-1.848126])`. + Its coordinates are bounded within :code:`[-5,5]`. + + Best visualized with the full domain and a range of :code:`[0,1000]` Parameters ---------- @@ -276,25 +454,31 @@ def booth_func(x): domain """ if not x.shape[1] == 2: - raise IndexError("Booth function only takes two-dimensional input.") - if not np.logical_and(x >= -10, x <= 10).all(): - raise ValueError("Input for Booth function must be within [-10, 10].") + raise IndexError( + "Himmelblau function only takes two-dimensional input." + ) + if not np.logical_and(x >= -5, x <= 5).all(): + raise ValueError( + "Input for Himmelblau function must be within [-5,5]." + ) x_ = x[:, 0] y_ = x[:, 1] - j = (x_ + 2 * y_ - 7) ** 2.0 + (2 * x_ + y_ - 5) ** 2.0 + + j = (x_ ** 2 + y_ - 11) ** 2 + (x_ + y_ ** 2 - 7) ** 2 return j -# TODO: Implement Bukin Function no. 6 -def bukin6_func(x): - """Bukin N. 6 Objective Function +def holdertable_func(x): + """Holder Table objective function - Only takes two dimensions and has a global minimum at - :code:`f([-10,1])`. Its coordinates are bounded by: - * x[:,0] must be within [-15, -5] - * x[:,1] must be within [-3, 3] + Only takes two dimensions and has a four equal global minimums + of `-19.2085` at :code:`f([8.05502, 9.66459])`, :code:`f([-8.05502, 9.66459])`, + :code:`f([8.05502, -9.66459])`, and :code:`f([-8.05502, -9.66459])`. + Its coordinates are bounded within :code:`[-10, 10]`. + + Best visualized with the full domain and a range of :code:`[-20, 0]` Parameters ---------- @@ -317,22 +501,70 @@ def bukin6_func(x): """ if not x.shape[1] == 2: raise IndexError( - "Bukin N. 6 function only takes two-dimensional " "input." + "Holder Table function only takes two-dimensional input." ) - if not np.logical_and(x[:, 0] >= -15, x[:, 0] <= -5).all(): - raise ValueError( - "x-coord for Bukin N. 6 function must be within " "[-15, -5]." - ) - if not np.logical_and(x[:, 1] >= -3, x[:, 1] <= 3).all(): + if not np.logical_and(x >= -10, x <= 10).all(): raise ValueError( - "y-coord for Bukin N. 6 function must be within " "[-3, 3]." + "Input for Holder Table function must be within [-10,10]." ) x_ = x[:, 0] y_ = x[:, 1] - j = 100 * np.sqrt( - np.absolute(y_ ** 2.0 - 0.01 * x_ ** 2.0) - ) + 0.01 * np.absolute(x_ + 10) + + j = -np.abs( + np.sin(x_) + * np.cos(y_) + * np.exp(np.abs(1 - np.sqrt(x_ ** 2 + y_ ** 2) / np.pi)) + ) + + return j + + +def levi_func(x): + """Levi objective function + + Only takes two dimensions and has a global minimum at + :code:`f([1,1])`. Its coordinates are bounded within + :code:`[-10,10]`. + + Parameters + ---------- + x : numpy.ndarray + set of inputs of shape :code:`(n_particles, dimensions)` + + Returns + ------- + numpy.ndarray + computed cost of size :code:`(n_particles, )` + + Raises + ------ + IndexError + When the input dimensions is greater than what the function + allows + ValueError + When the input is out of bounds with respect to the function + domain + """ + if not x.shape[1] == 2: + raise IndexError("Levi function only takes two-dimensional input.") + if not np.logical_and(x >= -10, x <= 10).all(): + raise ValueError("Input for Levi function must be within [-10, 10].") + + mask = np.full(x.shape, False) + mask[:, -1] = True + masked_x = np.ma.array(x, mask=mask) + + w_ = 1 + (x - 1) / 4 + masked_w_ = np.ma.array(w_, mask=mask) + d_ = x.shape[1] - 1 + + j = ( + np.sin(np.pi * w_[:, 0]) ** 2.0 + + ((masked_x - 1) ** 2.0).sum(axis=1) + * (1 + 10 * np.sin(np.pi * (masked_w_).sum(axis=1) + 1) ** 2.0) + + (w_[:, d_] - 1) ** 2.0 * (1 + np.sin(2 * np.pi * w_[:, d_]) ** 2.0) + ) return j @@ -367,12 +599,11 @@ def matyas_func(x): return j -def levi_func(x): - """Levi objective function +def rastrigin_func(x): + """Rastrigin objective function. - Only takes two dimensions and has a global minimum at - :code:`f([1,1])`. Its coordinates are bounded within - :code:`[-10,10]`. + Has a global minimum at :code:`f(0,0,...,0)` with a search + domain of :code:`[-5.12, 5.12]` Parameters ---------- @@ -386,34 +617,45 @@ def levi_func(x): Raises ------ - IndexError - When the input dimensions is greater than what the function - allows ValueError When the input is out of bounds with respect to the function domain """ - if not x.shape[1] == 2: - raise IndexError("Levi function only takes two-dimensional input.") - if not np.logical_and(x >= -10, x <= 10).all(): - raise ValueError("Input for Levi function must be within [-10, 10].") + if not np.logical_and(x >= -5.12, x <= 5.12).all(): + raise ValueError( + "Input for Rastrigin function must be within " "[-5.12, 5.12]." + ) - mask = np.full(x.shape, False) - mask[:, -1] = True - masked_x = np.ma.array(x, mask=mask) + d = x.shape[1] + j = 10.0 * d + (x ** 2.0 - 10.0 * np.cos(2.0 * np.pi * x)).sum(axis=1) - w_ = 1 + (x - 1) / 4 - masked_w_ = np.ma.array(w_, mask=mask) - d_ = x.shape[1] - 1 + return j - j = ( - np.sin(np.pi * w_[:, 0]) ** 2.0 - + ((masked_x - 1) ** 2.0).sum(axis=1) - * (1 + 10 * np.sin(np.pi * (masked_w_).sum(axis=1) + 1) ** 2.0) - + (w_[:, d_] - 1) ** 2.0 * (1 + np.sin(2 * np.pi * w_[:, d_]) ** 2.0) + +def rosenbrock_func(x): + """Rosenbrock objective function. + + Also known as the Rosenbrock's valley or Rosenbrock's banana + function. Has a global minimum of :code:`np.ones(dimensions)` where + :code:`dimensions` is :code:`x.shape[1]`. The search domain is + :code:`[-inf, inf]`. + + Parameters + ---------- + x : numpy.ndarray + set of inputs of shape :code:`(n_particles, dimensions)` + + Returns + ------- + numpy.ndarray + computed cost of size :code:`(n_particles, )` + """ + + r = np.sum( + 100 * (x.T[1:] - x.T[:-1] ** 2.0) ** 2 + (1 - x.T[:-1]) ** 2.0, axis=0 ) - return j + return r def schaffer2_func(x): @@ -459,3 +701,69 @@ def schaffer2_func(x): ) return j + + +def sphere_func(x): + """Sphere objective function. + + Has a global minimum at :code:`0` and with a search domain of + :code:`[-inf, inf]` + + Parameters + ---------- + x : numpy.ndarray + set of inputs of shape :code:`(n_particles, dimensions)` + + Returns + ------- + numpy.ndarray + computed cost of size :code:`(n_particles, )` + """ + j = (x ** 2.0).sum(axis=1) + + return j + + +def threehump_func(x): + """Three-hump camel objective function + + Only takes two dimensions and has a global minimum of `0` at + :code:`f([0, 0])`. Its coordinates are bounded within + :code:`[-5, 5]`. + + Best visualized in the full domin and a range of :code:`[0, 2000]`. + + Parameters + ---------- + x : numpy.ndarray + set of inputs of shape :code:`(n_particles, dimensions)` + + Returns + ------- + numpy.ndarray + computed cost of size :code:`(n_particles, )` + + Raises + ------ + IndexError + When the input dimensions is greater than what the function + allows + ValueError + When the input is out of bounds with respect to the function + domain + """ + if not x.shape[1] == 2: + raise IndexError( + "Three-hump camel function only takes two-dimensional input." + ) + if not np.logical_and(x >= -5, x <= 5).all(): + raise ValueError( + "Input for Three-hump camel function must be within [-5, 5]." + ) + + x_ = x[:, 0] + y_ = x[:, 1] + + j = 2 * x_ ** 2 - 1.05 * (x_ ** 4) + (x_ ** 6) / 6 + x_ * y_ + y_ ** 2 + + return j diff --git a/tests/utils/functions/conftest.py b/tests/utils/functions/conftest.py index ec3e3b16..855f3845 100644 --- a/tests/utils/functions/conftest.py +++ b/tests/utils/functions/conftest.py @@ -13,7 +13,7 @@ def outbound(): """Returns a function that generates a matrix out of bounds a given range""" - def _outbound(low, high, size, tol=100, nums=100): + def _outbound(low, high, size, tol=1000, nums=100): """Generates a matrix that is out of bounds""" low_end = -np.random.uniform(tol, low, (nums,)) high_end = np.random.uniform(tol, high, (nums,)) diff --git a/tests/utils/functions/test_singleobj_bounds.py b/tests/utils/functions/test_singleobj_bounds.py index d89c5535..dd950106 100644 --- a/tests/utils/functions/test_singleobj_bounds.py +++ b/tests/utils/functions/test_singleobj_bounds.py @@ -14,23 +14,25 @@ Bounds = namedtuple("Bounds", "low high") b = { # Define all bounds here - "rastrigin": Bounds(low=-5.12, high=5.12), "ackley": Bounds(low=-32, high=32), "beale": Bounds(low=-4.5, high=4.5), - "goldstein": Bounds(low=-2, high=-2), "booth": Bounds(low=-10, high=10), - "matyas": Bounds(low=-10, high=10), + # bukin_6 is not symmetrical + "crossintray": Bounds(low=-1, high=10), + "easom": Bounds(low=-100, high=100), + "eggholder": Bounds(low=-512, high=512), + "goldstein": Bounds(low=-2, high=-2), + "himmelblau": Bounds(low=-5, high=5), + "holdertable": Bounds(low=-10, high=10), "levi": Bounds(low=-10, high=10), + "matyas": Bounds(low=-10, high=10), + "rastrigin": Bounds(low=-5.12, high=5.12), + # rosenbrock has an infinite domain "schaffer2": Bounds(low=-100, high=100), + "threehump": Bounds(low=-5, high=5), } -def test_rastrigin_bound_fail(outbound): - """Test rastrigin bound exception""" - with pytest.raises(ValueError): - x = outbound(b["rastrigin"].low, b["rastrigin"].high, size=(3, 2)) - fx.rastrigin_func(x) - def test_ackley_bound_fail(outbound): """Test ackley bound exception""" @@ -46,13 +48,6 @@ def test_beale_bound_fail(outbound): fx.beale_func(x) -def test_goldstein_bound_fail(outbound): - """Test goldstein bound exception""" - with pytest.raises(ValueError): - x = outbound(b["goldstein"].low, b["goldstein"].high, size=(3, 2)) - fx.goldstein_func(x) - - def test_booth_bound_fail(outbound): """Test booth bound exception""" with pytest.raises(ValueError): @@ -74,11 +69,46 @@ def test_bukin6_bound_fail(x): fx.bukin6_func(x) -def test_matyas_bound_fail(outbound): - """Test matyas bound exception""" +def test_crossintray_bound_fail(outbound): + """Test crossintray bound exception""" with pytest.raises(ValueError): - x = outbound(b["matyas"].low, b["matyas"].high, size=(3, 2)) - fx.matyas_func(x) + x = outbound(b["crossintray"].low, b["crossintray"].high, size=(3, 2)) + fx.crossintray_func(x) + + +def test_easom_bound_fail(outbound): + """Test easom bound exception""" + with pytest.raises(ValueError): + x = outbound(b["easom"].low, b["easom"].high, size=(3, 2)) + fx.easom_func(x) + + +def test_eggholder_bound_fail(outbound): + """Test eggholder bound exception""" + with pytest.raises(ValueError): + x = outbound(b["eggholder"].low, b["eggholder"].high, size=(3, 2)) + fx.eggholder_func(x) + + +def test_goldstein_bound_fail(outbound): + """Test goldstein bound exception""" + with pytest.raises(ValueError): + x = outbound(b["goldstein"].low, b["goldstein"].high, size=(3, 2)) + fx.goldstein_func(x) + + +def test_himmelblau_bound_fail(outbound): + """Test himmelblau bound exception""" + with pytest.raises(ValueError): + x = outbound(b["himmelblau"].low, b["himmelblau"].high, size=(3, 2)) + fx.himmelblau_func(x) + + +def test_holdertable_bound_fail(outbound): + """Test holdertable bound exception""" + with pytest.raises(ValueError): + x = outbound(b["holdertable"].low, b["holdertable"].high, size=(3, 2)) + fx.holdertable_func(x) def test_levi_bound_fail(outbound): @@ -88,6 +118,20 @@ def test_levi_bound_fail(outbound): fx.levi_func(x) +def test_matyas_bound_fail(outbound): + """Test matyas bound exception""" + with pytest.raises(ValueError): + x = outbound(b["matyas"].low, b["matyas"].high, size=(3, 2)) + fx.matyas_func(x) + + +def test_rastrigin_bound_fail(outbound): + """Test rastrigin bound exception""" + with pytest.raises(ValueError): + x = outbound(b["rastrigin"].low, b["rastrigin"].high, size=(3, 2)) + fx.rastrigin_func(x) + + def test_schaffer2_bound_fail(outbound): """Test schaffer2 bound exception""" with pytest.raises(ValueError): @@ -95,3 +139,10 @@ def test_schaffer2_bound_fail(outbound): b["schaffer2"].low, b["schaffer2"].high, tol=200, size=(3, 2) ) fx.schaffer2_func(x) + + +def test_threehump_bound_fail(outbound): + """Test threehump bound exception""" + with pytest.raises(ValueError): + x = outbound(b["threehump"].low, b["threehump"].high, size=(3, 2)) + fx.threehump_func(x) diff --git a/tests/utils/functions/test_singleobj_dims.py b/tests/utils/functions/test_singleobj_dims.py index 9a4f1d76..e1a78e55 100644 --- a/tests/utils/functions/test_singleobj_dims.py +++ b/tests/utils/functions/test_singleobj_dims.py @@ -18,12 +18,6 @@ def test_beale_dim_fail(outdim): fx.beale_func(outdim) -def test_goldstein_dim_fail(outdim): - """Test goldstein dim exception""" - with pytest.raises(IndexError): - fx.goldstein_func(outdim) - - def test_booth_dim_fail(outdim): """Test booth dim exception""" with pytest.raises(IndexError): @@ -36,10 +30,39 @@ def test_bukin6_dim_fail(outdim): fx.bukin6_func(outdim) -def test_matyas_dim_fail(outdim): - """Test matyas dim exception""" +def test_crossintray_dim_fail(outdim): + """Test crossintray dim exception""" with pytest.raises(IndexError): - fx.matyas_func(outdim) + fx.crossintray_func(outdim) + + +def test_easom_dim_fail(outdim): + """Test easom dim exception""" + with pytest.raises(IndexError): + fx.easom_func(outdim) + +def test_goldstein_dim_fail(outdim): + """Test goldstein dim exception""" + with pytest.raises(IndexError): + fx.goldstein_func(outdim) + + +def test_eggholder_dim_fail(outdim): + """Test eggholder dim exception""" + with pytest.raises(IndexError): + fx.eggholder_func(outdim) + + +def test_himmelblau_dim_fail(outdim): + """Test himmelblau dim exception""" + with pytest.raises(IndexError): + fx.himmelblau_func(outdim) + + +def test_holdertable_dim_fail(outdim): + """Test holdertable dim exception""" + with pytest.raises(IndexError): + fx.holdertable_func(outdim) def test_levi_dim_fail(outdim): @@ -48,7 +71,19 @@ def test_levi_dim_fail(outdim): fx.levi_func(outdim) +def test_matyas_dim_fail(outdim): + """Test matyas dim exception""" + with pytest.raises(IndexError): + fx.matyas_func(outdim) + + def test_schaffer2_dim_fail(outdim): """Test schaffer2 dim exception""" with pytest.raises(IndexError): fx.schaffer2_func(outdim) + + +def test_threehump_dim_fail(outdim): + """Test threehump dim exception""" + with pytest.raises(IndexError): + fx.threehump_func(outdim) diff --git a/tests/utils/functions/test_singleobj_return.py b/tests/utils/functions/test_singleobj_return.py index c4e3f28b..5c6fbb36 100644 --- a/tests/utils/functions/test_singleobj_return.py +++ b/tests/utils/functions/test_singleobj_return.py @@ -10,28 +10,14 @@ from pyswarms.utils.functions import single_obj as fx -def test_sphere_output(common_minima): - """Tests sphere function output.""" - assert np.array_equal(fx.sphere_func(common_minima), np.zeros((3,))) -def test_rastrigin_output(common_minima): - """Tests rastrigin function output.""" - assert np.array_equal(fx.rastrigin_func(common_minima), np.zeros(3)) - def test_ackley_output(common_minima): """Tests ackley function output.""" assert np.isclose(fx.ackley_func(common_minima), np.zeros(3)).all() -def test_rosenbrock_output(common_minima2): - """Tests rosenbrock function output.""" - assert np.array_equal( - fx.rosenbrock_func(common_minima2).all(), np.zeros(3).all() - ) - - def test_beale_output(common_minima2): """Tests beale function output.""" assert np.isclose( @@ -39,13 +25,6 @@ def test_beale_output(common_minima2): ).all() -def test_goldstein_output(common_minima2): - """Tests goldstein-price function output.""" - assert np.isclose( - fx.goldstein_func([0, -1] * common_minima2), (3 * np.ones(3)) - ).all() - - def test_booth_output(common_minima2): """Test booth function output.""" assert np.isclose( @@ -60,9 +39,85 @@ def test_bukin6_output(common_minima2): ).all() -def test_bukin6_output(common_minima): - """Test bukin function output.""" - assert np.isclose(fx.matyas_func(common_minima), 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]]) + ], +) +@pytest.mark.parametrize( + "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_func(x), minima + ).all() + + +def test_easom_output(common_minima2): + """Tests easom function output.""" + assert np.isclose( + fx.easom_func([np.pi, np.pi] * common_minima2), (-1 * np.ones(3)) + ).all() + + +def test_eggholder_output(common_minima2): + """Tests eggholder function output.""" + assert np.isclose( + fx.eggholder_func([512, 404.3219] * common_minima2), (-959.6407 * np.ones(3)) + ).all() + + +def test_goldstein_output(common_minima2): + """Tests goldstein-price function output.""" + assert np.isclose( + fx.goldstein_func([0, -1] * common_minima2), (3 * np.ones(3)) + ).all() + + +@pytest.mark.parametrize( + "x", + [ + 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_func(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]]) + ], +) +@pytest.mark.parametrize( + "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_func(x), minima + ).all() def test_levi_output(common_minima2): @@ -70,6 +125,33 @@ def test_levi_output(common_minima2): assert np.isclose(fx.levi_func(common_minima2), np.zeros(3)).all() +def test_matyas_output(common_minima): + """Test matyas function output.""" + assert np.isclose(fx.matyas_func(common_minima), np.zeros(3)).all() + + +def test_rastrigin_output(common_minima): + """Tests rastrigin function output.""" + assert np.array_equal(fx.rastrigin_func(common_minima), np.zeros(3)) + + +def test_rosenbrock_output(common_minima2): + """Tests rosenbrock function output.""" + assert np.array_equal( + fx.rosenbrock_func(common_minima2).all(), np.zeros(3).all() + ) + + def test_schaffer2_output(common_minima): """Test schaffer2 function output.""" assert np.isclose(fx.schaffer2_func(common_minima), np.zeros(3)).all() + + +def test_sphere_output(common_minima): + """Tests sphere function output.""" + assert np.array_equal(fx.sphere_func(common_minima), np.zeros((3,))) + + +def test_threehump_output(common_minima): + """Tests threehump function output.""" + assert np.array_equal(fx.threehump_func(common_minima), np.zeros(3)) diff --git a/tests/utils/functions/test_singleobj_returndims.py b/tests/utils/functions/test_singleobj_returndims.py index 0051c4d5..2e40a2e4 100644 --- a/tests/utils/functions/test_singleobj_returndims.py +++ b/tests/utils/functions/test_singleobj_returndims.py @@ -10,36 +10,16 @@ from pyswarms.utils.functions import single_obj as fx -def test_sphere_output_size(common_minima, targetdim): - """Tests sphere output size.""" - assert fx.sphere_func(common_minima).shape == targetdim - - -def test_rastrigin_output_size(common_minima, targetdim): - """Tests rastrigin output size.""" - assert fx.rastrigin_func(common_minima).shape == targetdim - - def test_ackley_output_size(common_minima, targetdim): """Tests ackley output size.""" assert fx.ackley_func(common_minima).shape == targetdim -def test_rosenbrock_output_size(common_minima, targetdim): - """Tests rosenbrock output size.""" - assert fx.rosenbrock_func(common_minima).shape == targetdim - - def test_beale_output_size(common_minima, targetdim): """Tests beale output size.""" assert fx.beale_func(common_minima).shape == targetdim -def test_goldstein_output_size(common_minima, targetdim): - """Test goldstein output size.""" - assert fx.goldstein_func(common_minima).shape == targetdim - - def test_booth_output_size(common_minima, targetdim): """Test booth output size.""" assert fx.booth_func(common_minima).shape == targetdim @@ -50,11 +30,61 @@ def test_bukin6_output_size(common_minima2, targetdim): assert fx.bukin6_func([-10, 0] * common_minima2).shape == targetdim +def test_crossintray_output_size(common_minima2, targetdim): + """Test crossintray output size.""" + assert fx.crossintray_func([-10, 0] * common_minima2).shape == targetdim + + +def test_easom_output_size(common_minima2, targetdim): + """Test easom output size.""" + assert fx.easom_func([-10, 0] * common_minima2).shape == targetdim + + +def test_eggholder_output_size(common_minima2, targetdim): + """Test eggholder output size.""" + assert fx.eggholder_func([-10, 0] * common_minima2).shape == targetdim + + +def test_goldstein_output_size(common_minima, targetdim): + """Test goldstein output size.""" + assert fx.goldstein_func(common_minima).shape == targetdim + + +def test_himmelblau_output_size(common_minima, targetdim): + """Test himmelblau output size.""" + assert fx.himmelblau_func(common_minima).shape == targetdim + + +def test_holdertable_output_size(common_minima, targetdim): + """Test holdertable output size.""" + assert fx.holdertable_func(common_minima).shape == targetdim + + def test_levi_output_size(common_minima, targetdim): """Test levi output size.""" assert fx.levi_func(common_minima).shape == targetdim +def test_rastrigin_output_size(common_minima, targetdim): + """Tests rastrigin output size.""" + assert fx.rastrigin_func(common_minima).shape == targetdim + + +def test_rosenbrock_output_size(common_minima, targetdim): + """Tests rosenbrock output size.""" + assert fx.rosenbrock_func(common_minima).shape == targetdim + + def test_schaffer2_output_size(common_minima, targetdim): """Test schaffer2 output size.""" assert fx.schaffer2_func(common_minima).shape == targetdim + + +def test_sphere_output_size(common_minima, targetdim): + """Tests sphere output size.""" + assert fx.sphere_func(common_minima).shape == targetdim + + +def test_threehump_output_size(common_minima, targetdim): + """Test threehump output size.""" + assert fx.threehump_func(common_minima).shape == targetdim From a44443d4a33bb379d3e56a252c30c3ee1454c0df Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Thu, 19 Jul 2018 08:17:29 +0200 Subject: [PATCH 28/38] Fix bug in setup.py (#175) Resolves #174 Fixed a bug in the setup.py file where it would throw an error because the encoding format was not right. This is done by adding the encoding parameter to the open() function. Signed-off-by: Lester James V. Miranda Committed-with: Aaron (@whzup ) --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1c8db6cc..0267c4a5 100644 --- a/setup.py +++ b/setup.py @@ -5,10 +5,10 @@ from setuptools import setup, find_packages -with open('README.md') as readme_file: +with open('README.md', encoding="utf8") as readme_file: readme = readme_file.read() -with open('HISTORY.rst') as history_file: +with open('HISTORY.rst', encoding="utf8") as history_file: history = history_file.read() requirements = [ From d8c39ef105551fe00b327af14285296d7ee05413 Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Mon, 23 Jul 2018 14:44:52 +0200 Subject: [PATCH 29/38] Fix bug in topology classes (#176) Resolves #170 Fixed a bug in the topology class where not the best values were used as result when optimizing with the LocalBest class. Made the tests for the topologies less trivial by changing the index of the best cost and best position in the fixture swarm. Added a bigger fixture swarm to test the topology classes. Additionally, I fixed a little bug in the Random topology where the elements were compared to 0 instead of np.inf. Deleted the fixture for the neighbour number and replaced it with a pytest.mark.parametrize(). Signed-off-by: Lester James V. Miranda Committed-with: Aaron (@whzup) --- pyswarms/backend/topology/pyramid.py | 2 +- pyswarms/backend/topology/random.py | 7 +--- pyswarms/backend/topology/ring.py | 4 +- tests/backend/topology/conftest.py | 52 ++++++++++++++++++-------- tests/backend/topology/test_pyramid.py | 8 ++-- tests/backend/topology/test_random.py | 21 ++++++++--- tests/backend/topology/test_ring.py | 10 ++--- tests/backend/topology/test_star.py | 8 ++-- 8 files changed, 71 insertions(+), 41 deletions(-) diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 3d618e82..86c2fee8 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -77,7 +77,7 @@ def compute_gbest(self, swarm): # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) best_pos = swarm.pbest_pos[ - np.argmin(swarm.pbest_cost[best_neighbor]) + best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])] ] except AttributeError: msg = "Please pass a Swarm class. You passed {}".format( diff --git a/pyswarms/backend/topology/random.py b/pyswarms/backend/topology/random.py index d53cdd97..f03f6ed4 100644 --- a/pyswarms/backend/topology/random.py +++ b/pyswarms/backend/topology/random.py @@ -74,7 +74,7 @@ def compute_gbest(self, swarm, k): # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) best_pos = swarm.pbest_pos[ - np.argmin(swarm.pbest_cost[best_neighbor]) + best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])] ] except AttributeError: @@ -169,9 +169,6 @@ def __compute_neighbors(self, swarm, k): graph needs edges to change it to a connected graph. - .. note:: If the graph isn't connected, it is possible that the - PSO algorithm does not find the best position within - the swarm. Parameters ---------- @@ -206,7 +203,7 @@ def __compute_neighbors(self, swarm, k): # Generate connected graph. 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] == 0: + 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 3f1ed440..3c6e2018 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -72,7 +72,7 @@ def compute_gbest(self, swarm, p, k): if k == 1: # The minimum index is itself, no mapping needed. best_neighbor = swarm.pbest_cost[self.neighbor_idx][:, np.newaxis].argmin( - axis=1 + axis=0 ) else: idx_min = swarm.pbest_cost[self.neighbor_idx].argmin(axis=1) @@ -80,7 +80,7 @@ def compute_gbest(self, swarm, p, k): # Obtain best cost and position best_cost = np.min(swarm.pbest_cost[best_neighbor]) best_pos = swarm.pbest_pos[ - np.argmin(swarm.pbest_cost[best_neighbor]) + best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])] ] except AttributeError: msg = "Please pass a Swarm class. You passed {}".format( diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index d4e5e199..4aade1b1 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -15,20 +15,42 @@ def swarm(): """A contrived instance of the Swarm class at a certain timestep""" attrs_at_t = { - "position": np.array([[5, 5, 5], [3, 3, 3], [1, 1, 1]]), - "velocity": np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]]), - "current_cost": np.array([2, 2, 2]), - "pbest_cost": np.array([1, 2, 3]), - "pbest_pos": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), - "best_cost": 1, - "best_pos": np.array([1, 1, 1]), - "options": {"c1": 0.5, "c2": 1, "w": 2}, + "position": np.array([[9.95838686e-01, 5.87433429e-04, 6.49113772e-03], + [1.00559609e+00, 3.96477697e-02, 7.67205397e-01], + [2.87990950e-01, -3.64932609e-02, 1.89750725e-02], + [1.11646877e+00, 3.12037361e-03, 1.97885369e-01], + [8.96117216e-01, -9.79602053e-03, -1.66139336e-01], + [9.90423669e-01, 1.99307974e-03, -1.23386797e-02], + [2.06800701e-01, -1.67869387e-02, 1.14268810e-01], + [4.21786494e-01, 2.58755510e-02, 6.62254843e-01], + [9.90350831e-01, 3.81575154e-03, 8.80833545e-01], + [9.94353749e-01, -4.85086205e-02, 9.85313500e-03]]), + "velocity": np.array([[2.09076818e-02, 2.04936403e-03, 1.06761248e-02], + [1.64940497e-03, 5.67924469e-03, 9.74902301e-02], + [1.50445516e-01, 9.11699158e-03, 1.51474794e-02], + [2.94238740e-01, 5.71545680e-04, 1.54122294e-02], + [4.10430034e-02, 6.51847479e-04, 6.25109226e-02], + [6.71076116e-06, 1.89615516e-04, 4.65023770e-03], + [4.76081378e-02, 4.24416089e-03, 7.11856172e-02], + [1.33832808e-01, 1.81818698e-02, 1.16947941e-01], + [1.22849955e-03, 1.55685312e-03, 1.67819003e-02], + [5.60617396e-03, 4.31819608e-02, 2.52217220e-02]]), + "current_cost": np.array([1.07818462, 5.5647911, 19.6046078, 14.05300016, 3.72597614, 1.01169386, + 16.51846203, 32.72262829, 3.80274901, 1.05237138]), + "pbest_cost": np.array([1.00362006, 2.39151041, 2.55208424, 5.00176207, 1.04510827, 1.00025284, + 6.31216654, 2.53873121, 2.00530884, 1.05237138]), + "pbest_pos": np.array([[9.98033031e-01, 4.97392619e-03, 3.07726256e-03], + [1.00665809e+00, 4.22504014e-02, 9.84334657e-01], + [1.12159389e-02, 1.11429739e-01, 2.86388193e-02], + [1.64059236e-01, 6.85791237e-03, -2.32137604e-02], + [9.93740665e-01, -6.16501403e-03, -1.46096578e-02], + [9.90438476e-01, 2.50379538e-03, 1.87405987e-05], + [1.12301876e-01, 1.77099784e-03, 1.45382457e-01], + [4.41204876e-02, 4.84059652e-02, 1.05454822e+00], + [9.89348409e-01, -1.31692358e-03, 9.88291764e-01], + [9.99959923e-01, -5.32665972e-03, -1.53685870e-02]]), + "best_cost": 1.0002528364353296, + "best_pos": np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]), + "options": {'c1': 0.5, 'c2': 0.3, 'w': 0.9}, } return Swarm(**attrs_at_t) - - -@pytest.fixture(scope="module") -def k(): - """Default neighbor number""" - _k = 1 - return _k diff --git a/tests/backend/topology/test_pyramid.py b/tests/backend/topology/test_pyramid.py index bd8bf359..dc0b1ced 100644 --- a/tests/backend/topology/test_pyramid.py +++ b/tests/backend/topology/test_pyramid.py @@ -13,11 +13,11 @@ def test_compute_gbest_return_values(swarm, static): """Test if compute_gbest() gives the expected return values""" topology = Pyramid(static=static) - expected_cost = 1 - expected_pos = np.array([1, 2, 3]) + expected_cost = 1.0002528364353296 + expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) pos, cost = topology.compute_gbest(swarm) - assert cost == expected_cost - assert (pos == expected_pos).all() + assert cost == pytest.approx(expected_cost) + 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 d1ba10c4..15872897 100644 --- a/tests/backend/topology/test_random.py +++ b/tests/backend/topology/test_random.py @@ -15,10 +15,10 @@ def test_update_gbest_neighborhood(swarm, k, static): """Test if update_gbest_neighborhood gives the expected return values""" topology = Random(static=static) pos, cost = topology.compute_gbest(swarm, k=k) - expected_pos = np.array([1, 2, 3]) - expected_cost = 1 - assert (pos == expected_pos).all() - assert cost == expected_cost + expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) + expected_cost = 1.0002528364353296 + assert cost == pytest.approx(expected_cost) + assert (pos == pytest.approx(expected_pos)) @pytest.mark.parametrize("static", [True, False]) @@ -57,16 +57,27 @@ def test_compute_neighbors_return_values(swarm, k, static): @pytest.mark.parametrize("static", [True, False]) +@pytest.mark.parametrize("k", [1]) def test_compute_neighbors_adjacency_matrix(swarm, k, static): """Test if __compute_neighbors() gives the expected matrix""" np.random.seed(1) topology = Random(static=static) adj_matrix = topology._Random__compute_neighbors(swarm, k) - comparison_matrix = np.array([[1, 1, 0], [1, 1, 1], [0, 1, 1]]) + comparison_matrix = np.array([[1, 1, 1, 0, 1, 0, 0, 0, 0, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 1, 1, 1, 1, 0, 0, 0, 1, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 1, 1, 0, 1, 1, 0, 1, 0, 1], + [0, 1, 1, 0, 1, 0, 1, 0, 1, 1], + [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], + [0, 1, 1, 1, 1, 0, 1, 1, 1, 0], + [1, 1, 1, 0, 1, 1, 1, 0, 0, 1]]) assert np.allclose(adj_matrix, comparison_matrix, atol=1e-8) @pytest.mark.parametrize("static", [True, False]) +@pytest.mark.parametrize("k", [1]) def test_neighbor_idx(swarm, k, static): """Test if the neighbor_idx attribute is assigned""" topology = Random(static=static) diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index eb776a7a..8f3366fc 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -16,10 +16,10 @@ def test_update_gbest_neighborhood(swarm, p, k, static): """Test if update_gbest_neighborhood gives the expected return values""" topology = Ring(static=static) pos, cost = topology.compute_gbest(swarm, p=p, k=k) - expected_pos = np.array([1, 2, 3]) - expected_cost = 1 - assert (pos == expected_pos).all() - assert cost == expected_cost + expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) + expected_cost = 1.0002528364353296 + assert cost == pytest.approx(expected_cost) + assert (pos == pytest.approx(expected_pos)) @pytest.mark.parametrize("static", [True, False]) @@ -50,7 +50,7 @@ def test_compute_position_return_values(swarm, bounds, static): @pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize("k", [1, 2, 3]) @pytest.mark.parametrize("p", [1, 2]) -def test_neighbor_idx(swarm, static, p , k): +def test_neighbor_idx(swarm, static, p, k): """Test if the neighbor_idx attribute is assigned""" topology = Ring(static=static) p = topology.compute_gbest(swarm, p=p, k=k) diff --git a/tests/backend/topology/test_star.py b/tests/backend/topology/test_star.py index d47dd5d1..aab347a3 100644 --- a/tests/backend/topology/test_star.py +++ b/tests/backend/topology/test_star.py @@ -12,11 +12,11 @@ def test_compute_gbest_return_values(swarm): """Test if compute_gbest() gives the expected return values""" topology = Star() - expected_cost = 1 - expected_pos = np.array([1, 2, 3]) + expected_cost = 1.0002528364353296 + expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) pos, cost = topology.compute_gbest(swarm) - assert cost == expected_cost - assert (pos == expected_pos).all() + assert cost == pytest.approx(expected_cost) + assert (pos == pytest.approx(expected_pos)) @pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) From 6306812a7f50f1f48207de28ef58f7d9f07b4966 Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Mon, 23 Jul 2018 14:55:13 +0200 Subject: [PATCH 30/38] Add Von Neumann topology (#177) Reference: #129 Added a Von Neumann topology by inheriting from the Ring class and computing Delannoy numbers for the neighbours. Added tests and documentation and expanded the tests for the Ring topology. Signed-off-by: Lester James V. Miranda Committed-with: Aaron (@whzup ) --- docs/api/pyswarms.topology.rst | 9 +++ pyswarms/backend/topology/__init__.py | 3 +- pyswarms/backend/topology/von_neumann.py | 77 ++++++++++++++++++++++ tests/backend/topology/conftest.py | 2 +- tests/backend/topology/test_ring.py | 2 +- tests/backend/topology/test_von_neumann.py | 66 +++++++++++++++++++ 6 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 pyswarms/backend/topology/von_neumann.py create mode 100644 tests/backend/topology/test_von_neumann.py diff --git a/docs/api/pyswarms.topology.rst b/docs/api/pyswarms.topology.rst index 8a10782b..fa8cf608 100644 --- a/docs/api/pyswarms.topology.rst +++ b/docs/api/pyswarms.topology.rst @@ -36,6 +36,15 @@ pyswarms.backend.topology.ring module :show-inheritance: :special-members: __init__ +pyswarms.backend.topology.von_neumann module +-------------------------------------- + +.. automodule:: pyswarms.backend.topology.von_neumann + :members: + :undoc-members: + :show-inheritance: + :special-members: __init__ + pyswarms.backend.topology.pyramid module -------------------------------------- diff --git a/pyswarms/backend/topology/__init__.py b/pyswarms/backend/topology/__init__.py index 9764b39d..2039a974 100644 --- a/pyswarms/backend/topology/__init__.py +++ b/pyswarms/backend/topology/__init__.py @@ -11,6 +11,7 @@ from .ring import Ring from .pyramid import Pyramid from .random import Random +from .von_neumann import VonNeumann -__all__ = ["Topology", "Star", "Ring", "Pyramid", "Random"] +__all__ = ["Topology", "Star", "Ring", "Pyramid", "Random", "VonNeumann"] diff --git a/pyswarms/backend/topology/von_neumann.py b/pyswarms/backend/topology/von_neumann.py new file mode 100644 index 00000000..1ad99762 --- /dev/null +++ b/pyswarms/backend/topology/von_neumann.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +""" +A Von Neumann Network Topology + +This class implements a Von Neumann topology. +""" + +# Import from stdlib +import logging + +from .ring import Ring + +# Create a logger +logger = logging.getLogger(__name__) + + +class VonNeumann(Ring): + def __init__(self): + super(VonNeumann, self).__init__(static=True) + + def compute_gbest(self, swarm, p=1, r=1): + """Updates the global best using a neighborhood approach + + The Von Neumann topology inherits from the Ring topology and uses + the same approach to calculate the global best. The number of + neighbors is determined by the dimension and the range. This + topology is always a :code:`static` topology. + + Parameters + ---------- + swarm : pyswarms.backend.swarms.Swarm + a Swarm instance + r : int + range of the Von Neumann topology + p: int {1,2} + the Minkowski p-norm to use. 1 is the + sum-of-absolute values (or L1 distance) while 2 is + the Euclidean (or L2) distance. + + Returns + ------- + numpy.ndarray + Best position of shape :code:`(n_dimensions, )` + float + Best cost + """ + neighbors = VonNeumann.delannoy(swarm.dimensions, r) + return super(VonNeumann, self).compute_gbest(swarm, p, neighbors) + + @staticmethod + def delannoy(d, r): + """Static helper method to compute Delannoy numbers + + This method computes the number of neighbours of a Von Neumann + topology, i.e. a Delannoy number, dependent on the range and the + dimension of the search space. The Delannoy numbers are computed + recursively. + + Parameters + ---------- + d : int + dimension of the search space + r : int + range of the Von Neumann topology + + Returns + ------- + int + Delannoy number""" + 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) + return del_number diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index 4aade1b1..906323cb 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -53,4 +53,4 @@ 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}, } - return Swarm(**attrs_at_t) + return Swarm(**attrs_at_t) \ No newline at end of file diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index 8f3366fc..eaf54861 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -10,7 +10,7 @@ @pytest.mark.parametrize("static", [True, False]) -@pytest.mark.parametrize("k", [1, 2, 3]) +@pytest.mark.parametrize("k", [i for i in range(1, 10)]) @pytest.mark.parametrize("p", [1, 2]) def test_update_gbest_neighborhood(swarm, p, k, static): """Test if update_gbest_neighborhood gives the expected return values""" diff --git a/tests/backend/topology/test_von_neumann.py b/tests/backend/topology/test_von_neumann.py new file mode 100644 index 00000000..23056425 --- /dev/null +++ b/tests/backend/topology/test_von_neumann.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Import modules +import pytest +import numpy as np + +# Import from package +from pyswarms.backend.topology import VonNeumann + + +@pytest.mark.parametrize("r", [0, 1]) +@pytest.mark.parametrize("p", [1, 2]) +def test_update_gbest_neighborhood(swarm, p, r): + """Test if update_gbest_neighborhood gives the expected return values""" + topology = VonNeumann() + pos, cost = topology.compute_gbest(swarm, p=p, r=r) + expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]) + expected_cost = 1.0002528364353296 + assert cost == pytest.approx(expected_cost) + assert (pos == pytest.approx(expected_pos)) + + +@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)]) +def test_compute_velocity_return_values(swarm, clamp): + """Test if compute_velocity() gives the expected shape and range""" + topology = VonNeumann() + v = topology.compute_velocity(swarm, clamp) + assert v.shape == swarm.position.shape + if clamp is not None: + assert (clamp[0] <= v).all() and (clamp[1] >= v).all() + + +@pytest.mark.parametrize( + "bounds", + [None, ([-5, -5, -5], [5, 5, 5]), ([-10, -10, -10], [10, 10, 10])], +) +def test_compute_position_return_values(swarm, bounds): + """Test if compute_position() gives the expected shape and range""" + topology = VonNeumann() + p = topology.compute_position(swarm, bounds) + assert p.shape == swarm.velocity.shape + if bounds is not None: + assert (bounds[0] <= p).all() and (bounds[1] >= p).all() + + +@pytest.mark.parametrize("r", [0, 1]) +@pytest.mark.parametrize("p", [1, 2]) +def test_neighbor_idx(swarm, p, r): + """Test if the neighbor_idx attribute is assigned""" + topology = VonNeumann() + p = topology.compute_gbest(swarm, p=p, r=r) + assert topology.neighbor_idx is not None + + +@pytest.mark.parametrize("m", [i for i in range(9)]) +@pytest.mark.parametrize("n", [i for i in range(10)]) +def test_delannoy_numbers(m, n): + 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)) + assert VonNeumann.delannoy(m, n) in expected_values From c8541e3ff80329799362cb7de2105bccc2093aad Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Wed, 25 Jul 2018 02:15:56 +0200 Subject: [PATCH 31/38] Update docstrings and examples (#157) Resolves #154 This commit updates the PySwarms documentation from method docstrings, examples, and guides. Please see https://github.com/ljvmiranda921/pyswarms/issues/154 for the whole changelist. Signed-off-by: Lester James V. Miranda Committed-with: Aaron (@whzup) --- docs/examples/basic_optimization.rst | 263 +++++++++++--- docs/examples/inverse_kinematics.rst | 322 ++++++++++++++++++ examples/basic_optimization.ipynb | 206 ++++++++++- .../basic_optimization_with_arguments.ipynb | 236 ------------- examples/inverse_kinematics.ipynb | 12 +- pyswarms/backend/generators.py | 8 +- pyswarms/backend/operators.py | 6 +- pyswarms/backend/swarms.py | 2 +- pyswarms/backend/topology/__init__.py | 4 +- pyswarms/backend/topology/base.py | 6 +- pyswarms/backend/topology/pyramid.py | 16 +- pyswarms/backend/topology/random.py | 1 - pyswarms/backend/topology/ring.py | 13 +- pyswarms/backend/topology/star.py | 11 +- pyswarms/base/base_discrete.py | 12 +- pyswarms/base/base_single.py | 12 +- pyswarms/discrete/binary.py | 12 +- pyswarms/single/__init__.py | 10 + pyswarms/single/general_optimizer.py | 6 +- pyswarms/single/global_best.py | 4 +- pyswarms/single/local_best.py | 6 +- .../utils/environments/plot_environment.py | 14 +- pyswarms/utils/plotters/formatters.py | 2 +- pyswarms/utils/plotters/plotters.py | 8 +- pyswarms/utils/search/base_search.py | 10 +- pyswarms/utils/search/grid_search.py | 4 +- pyswarms/utils/search/random_search.py | 6 +- tests/backend/topology/conftest.py | 2 +- tests/optimizers/test_general_optimizer.py | 59 +++- 29 files changed, 904 insertions(+), 369 deletions(-) create mode 100644 docs/examples/inverse_kinematics.rst delete mode 100644 examples/basic_optimization_with_arguments.ipynb diff --git a/docs/examples/basic_optimization.rst b/docs/examples/basic_optimization.rst index 2ff04523..6de5d997 100644 --- a/docs/examples/basic_optimization.rst +++ b/docs/examples/basic_optimization.rst @@ -2,12 +2,29 @@ Basic Optimization ================== -In this example, we'll be performing a simple optimization of +In this example, we’ll be performing a simple optimization of single-objective functions using the global-best optimizer in ``pyswarms.single.GBestPSO`` and the local-best optimizer in ``pyswarms.single.LBestPSO``. This aims to demonstrate the basic capabilities of the library when applied to benchmark problems. +.. code-block:: python + + import sys + # Change directory to access the pyswarms module + sys.path.append('../') + +.. code-block:: python + + print('Running on Python version: {}'.format(sys.version)) + + +.. parsed-literal:: + + Running on Python version: 3.6.3 |Anaconda custom (64-bit)| (default, Oct 13 2017, 12:02:49) + [GCC 7.2.0] + + .. code-block:: python # Import modules @@ -25,12 +42,12 @@ capabilities of the library when applied to benchmark problems. Optimizing a function --------------------- -First, let's start by optimizing the sphere function. Recall that the +First, let’s start by optimizing the sphere function. Recall that the minima of this function can be located at ``f(0,0..,0)`` with a value of -``0``. In case you don't remember the characteristics of a given +``0``. In case you don’t remember the characteristics of a given function, simply call ``help()``. -For now let's just set some arbitrary parameters in our optimizers. +For now let’s just set some arbitrary parameters in our optimizers. There are, at minimum, three steps to perform optimization: 1. Set the hyperparameters to configure the swarm as a ``dict``. @@ -46,6 +63,7 @@ several variables at once. .. code-block:: python + %%time # Set-up hyperparameters options = {'c1': 0.5, 'c2': 0.3, 'w':0.9} @@ -58,21 +76,27 @@ several variables at once. .. parsed-literal:: - Iteration 1/1000, cost: 0.215476174296 - Iteration 101/1000, cost: 5.26998280059e-07 - Iteration 201/1000, cost: 1.31313801471e-11 - Iteration 301/1000, cost: 1.63948780036e-15 - Iteration 401/1000, cost: 2.72294062778e-19 - Iteration 501/1000, cost: 3.69002488955e-22 - Iteration 601/1000, cost: 3.13387805277e-27 - Iteration 701/1000, cost: 1.65106278625e-30 - Iteration 801/1000, cost: 6.95403958989e-35 - Iteration 901/1000, cost: 1.33520105208e-41 - ================================ + INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 0.11075768527574707 + INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 7.521863508083004e-08 + INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 2.8159915186067273e-11 + INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 8.794923638889175e-17 + INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 1.4699516547190895e-21 + INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 5.111264897313781e-23 + INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 8.329697430155943e-27 + INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 1.662161785541961e-30 + INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 6.140424420222279e-34 + INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 2.0523902169204634e-39 + INFO:pyswarms.single.global_best:================================ Optimization finished! Final cost: 0.0000 - Best value: [9.4634973546019334e-23, 1.7011045174312443e-22] + Best value: [-2.431421462417008e-22, -9.502018378214418e-23] + + +.. parsed-literal:: + + CPU times: user 144 ms, sys: 14.8 ms, total: 159 ms + Wall time: 151 ms We can see that the optimizer was able to find a good minima as shown @@ -80,10 +104,11 @@ above. You can control the verbosity of the output using the ``verbose`` argument, and the number of steps to be printed out using the ``print_step`` argument. -Now, let's try this one using local-best PSO: +Now, let’s try this one using local-best PSO: .. code-block:: python + %%time # Set-up hyperparameters options = {'c1': 0.5, 'c2': 0.3, 'w':0.9, 'k': 2, 'p': 2} @@ -96,36 +121,42 @@ Now, let's try this one using local-best PSO: .. parsed-literal:: - Iteration 1/1000, cost: 0.0573032190292 - Iteration 101/1000, cost: 8.92699853837e-07 - Iteration 201/1000, cost: 4.56513550671e-10 - Iteration 301/1000, cost: 2.35083665314e-16 - Iteration 401/1000, cost: 8.09981989467e-20 - Iteration 501/1000, cost: 2.58846774519e-22 - Iteration 601/1000, cost: 3.33919326611e-26 - Iteration 701/1000, cost: 2.15052800954e-30 - Iteration 801/1000, cost: 1.09638832057e-33 - Iteration 901/1000, cost: 3.92671836329e-38 - ================================ + INFO:pyswarms.single.local_best:Iteration 1/1000, cost: 0.01379181672220725 + INFO:pyswarms.single.local_best:Iteration 101/1000, cost: 2.084056061999154e-07 + INFO:pyswarms.single.local_best:Iteration 201/1000, cost: 9.44588224259351e-10 + INFO:pyswarms.single.local_best:Iteration 301/1000, cost: 1.5414149511766008e-13 + INFO:pyswarms.single.local_best:Iteration 401/1000, cost: 3.283944854760787e-16 + INFO:pyswarms.single.local_best:Iteration 501/1000, cost: 2.093366830537641e-20 + INFO:pyswarms.single.local_best:Iteration 601/1000, cost: 5.0279508047072096e-24 + INFO:pyswarms.single.local_best:Iteration 701/1000, cost: 1.0492646748670006e-27 + INFO:pyswarms.single.local_best:Iteration 801/1000, cost: 2.2616819643931453e-29 + INFO:pyswarms.single.local_best:Iteration 901/1000, cost: 8.48269618909152e-35 + INFO:pyswarms.single.local_best:================================ Optimization finished! Final cost: 0.0000 - Best value: [1.4149803165668767e-21, -9.9189063589743749e-24] + Best value: [2.122881378865588e-18, -5.35447408455737e-19] +.. parsed-literal:: + + CPU times: user 355 ms, sys: 4.36 ms, total: 359 ms + Wall time: 353 ms + + Optimizing a function with bounds --------------------------------- Another thing that we can do is to set some bounds into our solution, so as to contain our candidate solutions within a specific range. We can do this simply by passing a ``bounds`` parameter, of type ``tuple``, when -creating an instance of our swarm. Let's try this using the global-best +creating an instance of our swarm. Let’s try this using the global-best PSO with the Rastrigin function (``rastrigin_func`` in ``pyswarms.utils.functions.single_obj``). Recall that the Rastrigin function is bounded within ``[-5.12, 5.12]``. If we pass an unbounded swarm into this function, then a ``ValueError`` -might be raised. So what we'll do is to create a bound within the +might be raised. So what we’ll do is to create a bound within the specified range. There are some things to remember when specifying a bound: @@ -136,7 +167,7 @@ bound: than the ``min_bound``. Their shapes should match the dimensions of the swarm. -What we'll do now is to create a 10-particle, 2-dimensional swarm. This +What we’ll do now is to create a 10-particle, 2-dimensional swarm. This means that we have to set our maximum and minimum boundaries with the shape of 2. In case we want to initialize an n-dimensional swarm, we then have to set our bounds with the same shape n. A fast workaround for @@ -152,6 +183,7 @@ constant. .. code-block:: python + %%time # Initialize swarm options = {'c1': 0.5, 'c2': 0.3, 'w':0.9} @@ -164,19 +196,162 @@ constant. .. parsed-literal:: - Iteration 1/1000, cost: 6.93571097813 - Iteration 101/1000, cost: 0.00614705911661 - Iteration 201/1000, cost: 7.22876336567e-09 - Iteration 301/1000, cost: 5.89750470681e-13 - Iteration 401/1000, cost: 0.0 - Iteration 501/1000, cost: 0.0 - Iteration 601/1000, cost: 0.0 - Iteration 701/1000, cost: 0.0 - Iteration 801/1000, cost: 0.0 - Iteration 901/1000, cost: 0.0 - ================================ + INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 12.243865048066269 + INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 1.1759164022634394 + INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 0.9949603350768896 + INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 0.9949590581556009 + INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.9949590570934177 + INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.9949590570932898 + INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.9949590570932898 + INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.9949590570932898 + INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.9949590570932898 + INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.9949590570932898 + INFO:pyswarms.single.global_best:================================ + Optimization finished! + Final cost: 0.9950 + Best value: [3.5850411183743393e-09, -0.9949586379966202] + + + +.. parsed-literal:: + + CPU times: user 213 ms, sys: 7.55 ms, total: 221 ms + Wall time: 210 ms + + +Basic Optimization with Arguments +--------------------------------- + +Here, we will run a basic optimization using an objective function that +needs parameterization. We will use the ``single.GBestPSO`` and a +version of the rosenbrock function to demonstrate + +.. code-block:: python + + import sys + # change directory to access pyswarms + sys.path.append('../') + + print("Running Python {}".format(sys.version)) + + +.. parsed-literal:: + + Running Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 2 2016, 17:53:06) + [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] + + +.. code-block:: python + + # import modules + import numpy as np + + # create a parameterized version of the classic Rosenbrock unconstrained optimzation function + def rosenbrock_with_args(x, a, b, c=0): + + f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 + c + return f + +Using Arguments +~~~~~~~~~~~~~~~ + +Arguments can either be passed in using a tuple or a dictionary, using +the ``kwargs={}`` paradigm. First lets optimize the Rosenbrock function +using keyword arguments. Note in the definition of the Rosenbrock +function above, there were two arguments that need to be passed other +than the design variables, and one optional keyword argument, ``a``, +``b``, and ``c``, respectively + +.. code-block:: python + + from pyswarms.single.global_best import GlobalBestPSO + + # instatiate the optimizer + x_max = 10 * np.ones(2) + x_min = -1 * x_max + bounds = (x_min, x_max) + options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9} + optimizer = GlobalBestPSO(n_particles=10, dimensions=2, options=options, bounds=bounds) + + # now run the optimization, pass a=1 and b=100 as a tuple assigned to args + + cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100, c=0) + + +.. parsed-literal:: + + INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100, 'a': 1} + INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1022.9667801907804 + INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0011172801146408992 + INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 7.845605970774126e-07 + INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 1.313503109901238e-09 + INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 5.187079604907219e-10 + INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 1.0115283486088853e-10 + INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 2.329870757208421e-13 + INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 4.826176894160183e-15 + INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 3.125715456651088e-17 + INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 1.4236768129666014e-19 + INFO:pyswarms.single.global_best:================================ + Optimization finished! + Final cost: 0.0000 + Best value: [0.99999999996210465, 0.9999999999218413] + + + +It is also possible to pass a dictionary of key word arguments by using +``**`` decorator when passing the dict + +.. code-block:: python + + kwargs={"a": 1.0, "b": 100.0, 'c':0} + cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, **kwargs) + + +.. parsed-literal:: + + INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100.0, 'a': 1.0} + INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1.996797703363527e-21 + INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 1.0061676299213387e-24 + INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 4.8140236741112245e-28 + INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 2.879342304056693e-29 + INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0 + INFO:pyswarms.single.global_best:================================ Optimization finished! Final cost: 0.0000 - Best value: [-6.763954278218746e-11, 2.4565912679296225e-09] + Best value: [1.0, 1.0] + + +Any key word arguments in the objective function can be left out as they +will be passed the default as defined in the prototype. Note here, ``c`` +is not passed into the function. +.. code-block:: python + + cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100) + + +.. parsed-literal:: + + INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'b': 100, 'a': 1} + INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0 + INFO:pyswarms.single.global_best:================================ + Optimization finished! + Final cost: 0.0000 + Best value: [1.0, 1.0] + + diff --git a/docs/examples/inverse_kinematics.rst b/docs/examples/inverse_kinematics.rst new file mode 100644 index 00000000..f5bea9dc --- /dev/null +++ b/docs/examples/inverse_kinematics.rst @@ -0,0 +1,322 @@ +Solving the Inverse Kinematics problem using Particle Swarm Optimization +======================================================================== + +In this example, we are going to use the ``pyswarms`` library to solve a +6-DOF (Degrees of Freedom) Inverse Kinematics (IK) problem by treating +it as an optimization problem. We will use the ``pyswarms`` library to +find an *optimal* solution from a set of candidate solutions. + +.. code:: python + + import sys + # Change directory to access the pyswarms module + sys.path.append('../') + +.. code:: python + + print('Running on Python version: {}'.format(sys.version)) + + +.. parsed-literal:: + + Running on Python version: 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)] + + +.. code:: python + + # Import modules + import numpy as np + + # Import PySwarms + import pyswarms as ps + + # Some more magic so that the notebook will reload external python modules; + # see http://stackoverflow.com/questions/1907993/autoreload-of-modules-in-ipython + %load_ext autoreload + %autoreload 2 + +.. code:: python + + %%html + + # Styling for the text below + + + +.. raw:: html + + + # Styling for the text below + + +Introduction +============ + +Inverse Kinematics is one of the most challenging problems in robotics. +The problem involves finding an optimal *pose* for a manipulator given +the position of the end-tip effector as opposed to forward kinematics, +where the end-tip position is sought given the pose or joint +configuration. Normally, this position is expressed as a point in a +coordinate system (e.g., in a Cartesian system with :math:`x`, :math:`y` +and :math:`z` coordinates). However, the pose of the manipulator can +also be expressed as the collection of joint variables that describe the +angle of bending or twist (in revolute joints) or length of extension +(in prismatic joints). + +IK is particularly difficult because an abundance of solutions can +arise. Intuitively, one can imagine that a robotic arm can have multiple +ways of reaching through a certain point. It’s the same when you touch +the table and move your arm without moving the point you’re touching the +table at. Moreover, the calculation of these positions can be very +difficult. Simple solutions can be found for 3-DOF manipulators but +trying to solve the problem for 6 or even more DOF can lead to +challenging algebraic problems. + +IK as an Optimization Problem +============================= + +In this implementation, we are going to use a *6-DOF Stanford +Manipulator* with 5 revolute joints and 1 prismatic joint. Furthermore, +the constraints of the joints are going to be as follows: + ++------------------+--------------------------+-------------------------+ +| Parameters | Lower Boundary | Upper Boundary | ++==================+==========================+=========================+ +| :math:`\theta_1` | :math:`-\pi` | :math:`\pi` | ++------------------+--------------------------+-------------------------+ +| :math:`\theta_2` | :math:`-\frac{\pi}{2}` | :math:`\frac{\pi}{2}` | ++------------------+--------------------------+-------------------------+ +| :math:`d_3` | :math:`1` | :math:`3` | ++------------------+--------------------------+-------------------------+ +| :math:`\theta_4` | :math:`-\pi` | :math:`\pi` | ++------------------+--------------------------+-------------------------+ +| :math:`\theta_5` | :math:`-\frac{5\pi}{36}` | :math:`\frac{5\pi}{36}` | ++------------------+--------------------------+-------------------------+ +| :math:`\theta_6` | :math:`-\pi` | :math:`\pi` | ++------------------+--------------------------+-------------------------+ + +**Table 1**: *Physical constraints for the joint variables* + +Now, if we are given an *end-tip position* (in this case a :math:`xyz` +coordinate) we need to find the optimal parameters with the constraints +imposed in **Table 1**. These conditions are then sufficient in order to +treat this problem as an optimization problem. We define our parameter +vector :math:`\mathbf{X}` as follows: + +.. math:: \mathbf{X}\,:=\, [ \, \theta_1 \quad \theta_2 \quad d_3\ \quad \theta_4 \quad \theta_5 \, ] + +And for our end-tip position we define the target vector +:math:`\mathbf{T}` as: + +.. math:: \mathbf{T}\,:=\, [\, T_x \quad T_y \quad T_z \,] + +We can then start implementing our optimization algorithm. + +Initializing the Swarm +====================== + +The main idea for PSO is that we set a swarm :math:`\mathbf{S}` composed +of particles :math:`\mathbf{P}_n` into a search space in order to find +the optimal solution. The movement of the swarm depends on the cognitive +(:math:`c_1`) and social (:math:`c_2`) of all the particles. The +cognitive component speaks of the particle’s bias towards its personal +best from its past experience (i.e., how attracted it is to its own best +position). The social component controls how the particles are attracted +to the best score found by the swarm (i.e., the global best). High +:math:`c_1` paired with low :math:`c_2` values can often cause the swarm +to stagnate. The inverse can cause the swarm to converge too fast, +resulting in suboptimal solutions. + +We define our particle :math:`\mathbf{P}` as: + +.. math:: \mathbf{P}\,:=\,\mathbf{X} + +And the swarm as being composed of :math:`N` particles with certain +positions at a timestep :math:`t`: + +.. math:: \mathbf{S}_t\,:=\,[\,\mathbf{P}_1\quad\mathbf{P}_2\quad ... \quad\mathbf{P}_N\,] + +In this implementation, we designate :math:`\mathbf{P}_1` as the initial +configuration of the manipulator at the zero-position. This means that +the angles are equal to 0 and the link offset is also zero. We then +generate the :math:`N-1` particles using a uniform distribution which is +controlled by the hyperparameter :math:`\epsilon`. + +Finding the global optimum +========================== + +In order to find the global optimum, the swarm must be moved. This +movement is then translated by an update of the current position given +the swarm’s velocity :math:`\mathbf{V}`. That is: + +.. math:: \mathbf{S}_{t+1} = \mathbf{S}_t + \mathbf{V}_{t+1} + +The velocity is then computed as follows: + +.. math:: \mathbf{V}_{t+1} = w\mathbf{V}_t + c_1 r_1 (\mathbf{p}_{best} - \mathbf{p}) + c_2 r_2(\mathbf{g}_{best} - \mathbf{p}) + +Where :math:`r_1` and :math:`r_2` denote random values in the intervall +:math:`[0,1]`, :math:`\mathbf{p}_{best}` is the best and +:math:`\mathbf{p}` is the current personal position and +:math:`\mathbf{g}_{best}` is the best position of all the particles. +Moreover, :math:`w` is the inertia weight that controls the “memory” of +the swarm’s previous position. + +Preparations +------------ + +Let us now see how this works with the ``pyswarms`` library. We use the +point :math:`[-2,2,3]` as our target for which we want to find an +optimal pose of the manipulator. We start by defining a function to get +the distance from the current position to the target position: + +.. code:: python + + def distance(query, target): + x_dist = (target[0] - query[0])**2 + y_dist = (target[1] - query[1])**2 + z_dist = (target[2] - query[2])**2 + dist = np.sqrt(x_dist + y_dist + z_dist) + return dist + +We are going to use the distance function to compute the cost, the +further away the more costly the position is. + +The optimization algorithm needs some parameters (the swarm size, +:math:`c_1`, :math:`c_2` and :math:`\epsilon`). For the *options* +(:math:`c_1`,\ :math:`c_2` and :math:`w`) we have to create a dictionary +and for the constraints a tuple with a list of the respective minimal +values and a list of the respective maximal values. The rest can be +handled with variables. Additionally, we define the joint lengths to be +3 units long: + +.. code:: python + + swarm_size = 20 + dim = 6 # Dimension of X + epsilon = 1.0 + options = {'c1': 1.5, 'c2':1.5, 'w':0.5} + + constraints = (np.array([-np.pi , -np.pi/2 , 1 , -np.pi , -5*np.pi/36 , -np.pi]), + np.array([np.pi , np.pi/2 , 3 , np.pi , 5*np.pi/36 , np.pi])) + + d1 = d2 = d3 = d4 = d5 = d6 = 3 + +In order to obtain the current position, we need to calculate the +matrices of rotation and translation for every joint. Here we use the +`Denvait-Hartenberg +parameters `__ +for that. So we define a function that calculates these. The function +uses the rotation angle and the extension :math:`d` of a prismatic joint +as input: + +.. code:: python + + def getTransformMatrix(theta, d, a, alpha): + T = np.array([[np.cos(theta) , -np.sin(theta)*np.cos(alpha) , np.sin(theta)*np.sin(alpha) , a*np.cos(theta)], + [np.sin(theta) , np.cos(theta)*np.cos(alpha) , -np.cos(theta)*np.sin(alpha) , a*np.sin(theta)], + [0 , np.sin(alpha) , np.cos(alpha) , d ], + [0 , 0 , 0 , 1 ] + ]) + return T + +Now we can calculate the transformation matrix to obtain the end tip +position. For this we create another function that takes our vector +:math:`\mathbf{X}` with the joint variables as input: + +.. code:: python + + def get_end_tip_position(params): + # Create the transformation matrices for the respective joints + t_00 = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]) + t_01 = getTransformMatrix(params[0] , d2 , 0 , -np.pi/2) + t_12 = getTransformMatrix(params[1] , d2 , 0 , -np.pi/2) + t_23 = getTransformMatrix(0 , params[2] , 0 , -np.pi/2) + t_34 = getTransformMatrix(params[3] , d4 , 0 , -np.pi/2) + t_45 = getTransformMatrix(params[4] , 0 , 0 , np.pi/2) + t_56 = getTransformMatrix(params[5] , d6 ,0 , 0) + + # Get the overall transformation matrix + end_tip_m = t_00.dot(t_01).dot(t_12).dot(t_23).dot(t_34).dot(t_45).dot(t_56) + + # The coordinates of the end tip are the 3 upper entries in the 4th column + pos = np.array([end_tip_m[0,3],end_tip_m[1,3],end_tip_m[2,3]]) + return pos + +The last thing we need to prepare in order to run the algorithm is the +actual function that we want to optimize. We just need to calculate the +distance between the position of each swarm particle and the target +point: + +.. code:: python + + def opt_func(X): + n_particles = X.shape[0] # number of particles + target = np.array([-2,2,3]) + dist = [distance(get_end_tip_position(X[i]), target) for i in range(n_particles)] + return np.array(dist) + +Running the algorithm +--------------------- + +Braced with these preparations we can finally start using the algorithm: + +.. code:: python + + %%time + # Call an instance of PSO + optimizer = ps.single.GlobalBestPSO(n_particles=swarm_size, + dimensions=dim, + options=options, + bounds=constraints) + + # Perform optimization + cost, joint_vars = optimizer.optimize(opt_func, print_step=100, iters=1000, verbose=3) + + +.. parsed-literal:: + + INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 0.9638223076369133 + INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 2.5258875519324167e-07 + INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 4.7236564972673785e-14 + INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0 + INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0 + INFO:pyswarms.single.global_best:================================ + Optimization finished! + Final cost: 0.0000 + Best value: [ -2.182725 1.323111 1.579636 ...] + + + +.. parsed-literal:: + + Wall time: 13.6 s + + +Now let’s see if the algorithm really worked and test the output for +``joint_vars``: + +.. code:: python + + print(get_end_tip_position(joint_vars)) + + +.. parsed-literal:: + + [-2. 2. 3.] + + +Hooray! That’s exactly the position we wanted the tip to be in. Of +course this example is quite primitive. Some extensions of this idea +could involve the consideration of the current position of the +manipulator and the amount of rotation and extension in the optimization +function such that the result is the path with the least movement. diff --git a/examples/basic_optimization.ipynb b/examples/basic_optimization.ipynb index a6b41e04..43136d3c 100644 --- a/examples/basic_optimization.ipynb +++ b/examples/basic_optimization.ipynb @@ -278,9 +278,211 @@ "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic Optimization with Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, we will run a basic optimization using an objective function that needs parameterization. We will use the ``single.GBestPSO`` and a version of the rosenbrock function to demonstrate" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 2 2016, 17:53:06) \n", + "[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\n" + ] + } + ], + "source": [ + "import sys\n", + "# change directory to access pyswarms\n", + "sys.path.append('../')\n", + "\n", + "print(\"Running Python {}\".format(sys.version))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# import modules\n", + "import numpy as np\n", + "\n", + "# create a parameterized version of the classic Rosenbrock unconstrained optimzation function\n", + "def rosenbrock_with_args(x, a, b, c=0):\n", + "\n", + " f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 + c\n", + " return f" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Arguments can either be passed in using a tuple or a dictionary, using the ``kwargs={}`` paradigm. First lets optimize the Rosenbrock function using keyword arguments. Note in the definition of the Rosenbrock function above, there were two arguments that need to be passed other than the design variables, and one optional keyword argument, ``a``, ``b``, and ``c``, respectively" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100, 'a': 1}\n", + "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1022.9667801907804\n", + "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0011172801146408992\n", + "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 7.845605970774126e-07\n", + "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 1.313503109901238e-09\n", + "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 5.187079604907219e-10\n", + "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 1.0115283486088853e-10\n", + "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 2.329870757208421e-13\n", + "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 4.826176894160183e-15\n", + "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 3.125715456651088e-17\n", + "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 1.4236768129666014e-19\n", + "INFO:pyswarms.single.global_best:================================\n", + "Optimization finished!\n", + "Final cost: 0.0000\n", + "Best value: [0.99999999996210465, 0.9999999999218413]\n", + "\n" + ] + } + ], + "source": [ + "from pyswarms.single.global_best import GlobalBestPSO\n", + "\n", + "# instatiate the optimizer\n", + "x_max = 10 * np.ones(2)\n", + "x_min = -1 * x_max\n", + "bounds = (x_min, x_max)\n", + "options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n", + "optimizer = GlobalBestPSO(n_particles=10, dimensions=2, options=options, bounds=bounds)\n", + "\n", + "# now run the optimization, pass a=1 and b=100 as a tuple assigned to args\n", + "\n", + "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100, c=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is also possible to pass a dictionary of key word arguments by using ``**`` decorator when passing the dict" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100.0, 'a': 1.0}\n", + "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1.996797703363527e-21\n", + "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 1.0061676299213387e-24\n", + "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 4.8140236741112245e-28\n", + "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 2.879342304056693e-29\n", + "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:================================\n", + "Optimization finished!\n", + "Final cost: 0.0000\n", + "Best value: [1.0, 1.0]\n", + "\n" + ] + } + ], + "source": [ + "kwargs={\"a\": 1.0, \"b\": 100.0, 'c':0}\n", + "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, **kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "Any key word arguments in the objective function can be left out as they will be passed the default as defined in the prototype. Note here, ``c`` is not passed into the function." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'b': 100, 'a': 1}\n", + "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0\n", + "INFO:pyswarms.single.global_best:================================\n", + "Optimization finished!\n", + "Final cost: 0.0000\n", + "Best value: [1.0, 1.0]\n", + "\n" + ] + } + ], + "source": [ + "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { + "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", @@ -296,9 +498,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.3" + "version": "3.6.0" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 1 } diff --git a/examples/basic_optimization_with_arguments.ipynb b/examples/basic_optimization_with_arguments.ipynb deleted file mode 100644 index 0398a008..00000000 --- a/examples/basic_optimization_with_arguments.ipynb +++ /dev/null @@ -1,236 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Basic Optimization with Arguments" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we will run a basic optimization using an objective function that needs parameterization. We will use the ``single.GBestPSO`` and a version of the rosenbrock function to demonstrate" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 2 2016, 17:53:06) \n", - "[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\n" - ] - } - ], - "source": [ - "import sys\n", - "# change directory to access pyswarms\n", - "sys.path.append('../')\n", - "\n", - "print(\"Running Python {}\".format(sys.version))" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": false, - "scrolled": true - }, - "outputs": [], - "source": [ - "# import modules\n", - "import numpy as np\n", - "\n", - "# create a parameterized version of the classic Rosenbrock unconstrained optimzation function\n", - "def rosenbrock_with_args(x, a, b, c=0):\n", - "\n", - " f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 + c\n", - " return f" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using Arguments" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Arguments can either be passed in using a tuple or a dictionary, using the ``kwargs={}`` paradigm. First lets optimize the Rosenbrock function using keyword arguments. Note in the definition of the Rosenbrock function above, there were two arguments that need to be passed other than the design variables, and one optional keyword argument, ``a``, ``b``, and ``c``, respectively" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100, 'a': 1}\n", - "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1022.9667801907804\n", - "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0011172801146408992\n", - "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 7.845605970774126e-07\n", - "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 1.313503109901238e-09\n", - "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 5.187079604907219e-10\n", - "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 1.0115283486088853e-10\n", - "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 2.329870757208421e-13\n", - "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 4.826176894160183e-15\n", - "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 3.125715456651088e-17\n", - "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 1.4236768129666014e-19\n", - "INFO:pyswarms.single.global_best:================================\n", - "Optimization finished!\n", - "Final cost: 0.0000\n", - "Best value: [0.99999999996210465, 0.9999999999218413]\n", - "\n" - ] - } - ], - "source": [ - "from pyswarms.single.global_best import GlobalBestPSO\n", - "\n", - "# instatiate the optimizer\n", - "x_max = 10 * np.ones(2)\n", - "x_min = -1 * x_max\n", - "bounds = (x_min, x_max)\n", - "options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n", - "optimizer = GlobalBestPSO(n_particles=10, dimensions=2, options=options, bounds=bounds)\n", - "\n", - "# now run the optimization, pass a=1 and b=100 as a tuple assigned to args\n", - "\n", - "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100, c=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is also possible to pass a dictionary of key word arguments by using ``**`` decorator when passing the dict" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'c': 0, 'b': 100.0, 'a': 1.0}\n", - "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 1.996797703363527e-21\n", - "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 1.0061676299213387e-24\n", - "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 4.8140236741112245e-28\n", - "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 2.879342304056693e-29\n", - "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:================================\n", - "Optimization finished!\n", - "Final cost: 0.0000\n", - "Best value: [1.0, 1.0]\n", - "\n" - ] - } - ], - "source": [ - "kwargs={\"a\": 1.0, \"b\": 100.0, 'c':0}\n", - "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, **kwargs)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "Any key word arguments in the objective function can be left out as they will be passed the default as defined in the prototype. Note here, ``c`` is not passed into the function." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pyswarms.single.global_best:Arguments Passed to Objective Function: {'b': 100, 'a': 1}\n", - "INFO:pyswarms.single.global_best:Iteration 1/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 101/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 201/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 301/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 401/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 501/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 601/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 701/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 801/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:Iteration 901/1000, cost: 0.0\n", - "INFO:pyswarms.single.global_best:================================\n", - "Optimization finished!\n", - "Final cost: 0.0000\n", - "Best value: [1.0, 1.0]\n", - "\n" - ] - } - ], - "source": [ - "cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, print_step=100, verbose=3, a=1, b=100)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python [conda env:anaconda3]", - "language": "python", - "name": "conda-env-anaconda3-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/examples/inverse_kinematics.ipynb b/examples/inverse_kinematics.ipynb index 0e295b17..f46cd5ea 100644 --- a/examples/inverse_kinematics.ipynb +++ b/examples/inverse_kinematics.ipynb @@ -7,7 +7,7 @@ "Solving the Inverse Kinematics problem using Particle Swarm Optimization\n", "====================================\n", "\n", - "In this example we are going to use the `pyswarms` library to solve a 6-DOF (Degrees of Freedom) Inverse Kinematics (IK) problem by treating it as a optimization problem. We will use the `pyswarms` library to find an *optimal* solution from a set of candidate solutions." + "In this example, we are going to use the `pyswarms` library to solve a 6-DOF (Degrees of Freedom) Inverse Kinematics (IK) problem by treating it as an optimization problem. We will use the `pyswarms` library to find an *optimal* solution from a set of candidate solutions." ] }, { @@ -92,9 +92,9 @@ "Introduction\n", "======\n", "\n", - "Inverse Kinematics is one of the most challenging problems in robotics. The problem involves finding an optimal *pose* for a manipulator given the position of the end-tip effector as opposed to forward kinematics , where the end-tip position is sought given the pose or joint configuration. Normally, this position is expressed as a point in a coordinate system (e.g., in a Cartesian system with $x$, $y$ and $z$ coordinates). However, the pose of the manipulator can also be expressed as the collection of joint variables that describe the angle of bending or twist (in revolute joints) or length of extension (in prismatic joints).\n", + "Inverse Kinematics is one of the most challenging problems in robotics. The problem involves finding an optimal *pose* for a manipulator given the position of the end-tip effector as opposed to forward kinematics, where the end-tip position is sought given the pose or joint configuration. Normally, this position is expressed as a point in a coordinate system (e.g., in a Cartesian system with $x$, $y$ and $z$ coordinates). However, the pose of the manipulator can also be expressed as the collection of joint variables that describe the angle of bending or twist (in revolute joints) or length of extension (in prismatic joints).\n", "\n", - "IK is particularly difficult because an abundance of solutions can arise. Intuitively, one can imagine that a robotic arm can have multiple ways of reaching through a certain point. It's the same when you touch the table and move your arm without moving the point you're touching the table at. Moreover, the calculation of of these positions can be very difficult. Simple solutions can be found for 3-DOF manipulators but trying to solve the problem for 6 or even more DOF can lead to challenging algebraic problems.\n", + "IK is particularly difficult because an abundance of solutions can arise. Intuitively, one can imagine that a robotic arm can have multiple ways of reaching through a certain point. It's the same when you touch the table and move your arm without moving the point you're touching the table at. Moreover, the calculation of these positions can be very difficult. Simple solutions can be found for 3-DOF manipulators but trying to solve the problem for 6 or even more DOF can lead to challenging algebraic problems.\n", "\n", "IK as an Optimization Problem\n", "===============\n", @@ -140,7 +140,7 @@ "Finding the global optimum\n", "=============\n", "\n", - "In order to find the global optimum the swarm must be moved. This movement is then translated by an update of the current position given the swarm's velocity $\\mathbf{V}$. That is:\n", + "In order to find the global optimum, the swarm must be moved. This movement is then translated by an update of the current position given the swarm's velocity $\\mathbf{V}$. That is:\n", "\n", "$$\\mathbf{S}_{t+1} = \\mathbf{S}_t + \\mathbf{V}_{t+1}$$\n", "\n", @@ -176,7 +176,7 @@ "source": [ "We are going to use the distance function to compute the cost, the further away the more costly the position is. \n", "\n", - "The optimization algorithm needs some paramters (the swarm size, $c_1$, $c_2$ and $\\epsilon$). For the *options* ($c_1$,$c_2$ and $w$) we have to create a dictionary and for the constraints a tuple with a list of the respective minimal values and a list of the respective maximal values. The rest can be handled with variables. Additionally, we define the joint lengths to be 3 units long:" + "The optimization algorithm needs some parameters (the swarm size, $c_1$, $c_2$ and $\\epsilon$). For the *options* ($c_1$,$c_2$ and $w$) we have to create a dictionary and for the constraints a tuple with a list of the respective minimal values and a list of the respective maximal values. The rest can be handled with variables. Additionally, we define the joint lengths to be 3 units long:" ] }, { @@ -353,7 +353,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Hooray! That's exactly the position we wanted the tip to be in. Of course this example is quite primitive. Some extensions of this idea could involve the consideration of the current position of the manipulator and the amout of rotation and extension in the optimization function such that the result is the path with the least movement." + "Hooray! That's exactly the position we wanted the tip to be in. Of course this example is quite primitive. Some extensions of this idea could involve the consideration of the current position of the manipulator and the amount of rotation and extension in the optimization function such that the result is the path with the least movement." ] } ], diff --git a/pyswarms/backend/generators.py b/pyswarms/backend/generators.py index 2ef6f5a8..d475fbab 100644 --- a/pyswarms/backend/generators.py +++ b/pyswarms/backend/generators.py @@ -19,7 +19,7 @@ def generate_swarm( n_particles, dimensions, bounds=None, center=1.00, init_pos=None ): - """Generates a swarm + """Generate a swarm Parameters ---------- @@ -75,7 +75,7 @@ def generate_swarm( def generate_discrete_swarm( n_particles, dimensions, binary=False, init_pos=None ): - """Generates a discrete swarm + """Generate a discrete swarm Parameters ---------- @@ -109,7 +109,7 @@ def generate_discrete_swarm( def generate_velocity(n_particles, dimensions, clamp=None): - """Initializes a velocity vector + """Initialize a velocity vector Parameters ---------- @@ -149,7 +149,7 @@ def create_swarm( init_pos=None, clamp=None, ): - """Abstracts the generate_swarm() and generate_velocity() methods + """Abstract the generate_swarm() and generate_velocity() methods Parameters ---------- diff --git a/pyswarms/backend/operators.py b/pyswarms/backend/operators.py index f91b7675..fb42ecd2 100644 --- a/pyswarms/backend/operators.py +++ b/pyswarms/backend/operators.py @@ -19,7 +19,7 @@ def compute_pbest(swarm): - """Takes a swarm instance and updates the personal best scores + """Update the personal best score of a swarm instance You can use this method to update your personal best positions. @@ -75,7 +75,7 @@ def compute_pbest(swarm): def compute_velocity(swarm, clamp): - """Updates the velocity matrix + """Update the velocity matrix This method updates the velocity matrix using the best and current positions of the swarm. The velocity matrix is computed using the @@ -149,7 +149,7 @@ def compute_velocity(swarm, clamp): def compute_position(swarm, bounds): - """Updates the position matrix + """Update the position matrix This method updates the position matrix given the current position and the velocity. If bounded, it waives updating the position. diff --git a/pyswarms/backend/swarms.py b/pyswarms/backend/swarms.py index 8cc3d2d9..a6d4db06 100644 --- a/pyswarms/backend/swarms.py +++ b/pyswarms/backend/swarms.py @@ -5,7 +5,7 @@ This module implements a Swarm class that holds various attributes in the swarm such as position, velocity, options, etc. You can use this -as input to most backend cases +as input to most backend cases. """ # Import modules diff --git a/pyswarms/backend/topology/__init__.py b/pyswarms/backend/topology/__init__.py index 2039a974..c5c96039 100644 --- a/pyswarms/backend/topology/__init__.py +++ b/pyswarms/backend/topology/__init__.py @@ -1,6 +1,6 @@ """ -The :code:`pyswarms.backend.topology` contains various topologies that dictate -particle behavior. These topologies implement three methods: +The :code:`pyswarms.backend.topology` contains various topologies. They dictate +the behavior of the particles and implement three methods: - compute_best_particle(): gets the position and cost of the best particle in the swarm - update_velocity(): updates the velocity-matrix depending on the topology. - update_position(): updates the position-matrix depending on the topology. diff --git a/pyswarms/backend/topology/base.py b/pyswarms/backend/topology/base.py index d5a368ff..760d038e 100644 --- a/pyswarms/backend/topology/base.py +++ b/pyswarms/backend/topology/base.py @@ -38,14 +38,14 @@ def __init__(self, static, **kwargs): self.logger) def compute_gbest(self, swarm): - """Computes the best particle of the swarm and returns the cost and + """Compute the best particle of the swarm and return the cost and position""" raise NotImplementedError("Topology::compute_gbest()") def compute_position(self, swarm): - """Updates the swarm's position-matrix""" + """Update the swarm's position-matrix""" raise NotImplementedError("Topology::compute_position()") def compute_velocity(self, swarm): - """Updates the swarm's velocity-matrix""" + """Update the swarm's velocity-matrix""" raise NotImplementedError("Topology::compute_velocity()") diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 86c2fee8..2e3cb751 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -3,7 +3,7 @@ """ A Pyramid Network Topology -This class implements a pyramid topology where all particles are connected in a N-dimensional simplex fashion. +This class implements a pyramid topology. In this topology, the particles are connected by N-dimensional simplices. """ # Import from stdlib @@ -34,10 +34,14 @@ def __init__(self, static=False): super(Pyramid, self).__init__(static) def compute_gbest(self, swarm): - """Updates the global best using a pyramid neighborhood approach + """Update the global best using a pyramid neighborhood approach - This uses the Delaunay method from :code:`scipy` to triangulate N-dimensional space - with simplices consisting of swarm particles + This topology uses the Delaunay method from :code:`scipy`. This method is used to triangulate + N-dimensional space into simplices. The vertices of the simplicies consist of swarm particles. [SIS2008] + + [SIS2008] J. Lane, A. Engelbrecht and J. Gain, "Particle swarm optimization with spatially + meaningful neighbours," 2008 IEEE Swarm Intelligence Symposium, St. Louis, MO, 2008, + pp. 1-8. doi: 10.1109/SIS.2008.4668281 Parameters ---------- @@ -89,7 +93,7 @@ def compute_gbest(self, swarm): return (best_pos, best_cost) def compute_velocity(self, swarm, clamp=None): - """Computes the velocity matrix + """Compute the velocity matrix This method updates the velocity matrix using the best and current positions of the swarm. The velocity matrix is computed using the @@ -127,7 +131,7 @@ def compute_velocity(self, swarm, clamp=None): return ops.compute_velocity(swarm, clamp) def compute_position(self, swarm, bounds=None): - """Updates the position matrix + """Update the position matrix This method updates the position matrix given the current position and the velocity. If bounded, it waives updating the position. diff --git a/pyswarms/backend/topology/random.py b/pyswarms/backend/topology/random.py index f03f6ed4..17439621 100644 --- a/pyswarms/backend/topology/random.py +++ b/pyswarms/backend/topology/random.py @@ -169,7 +169,6 @@ def __compute_neighbors(self, swarm, k): graph needs edges to change it to a connected graph. - Parameters ---------- swarm : pyswarms.backend.swarms.Swarm diff --git a/pyswarms/backend/topology/ring.py b/pyswarms/backend/topology/ring.py index 3c6e2018..bef98219 100644 --- a/pyswarms/backend/topology/ring.py +++ b/pyswarms/backend/topology/ring.py @@ -3,8 +3,9 @@ """ A Ring Network Topology -This class implements a star topology where all particles are connected in a -ring-like fashion. This social behavior is often found in LocalBest PSO +This class implements a ring topology. In this topology, +the particles are connected with their k nearest neighbors. +This social behavior is often found in LocalBest PSO optimizers. """ @@ -35,10 +36,10 @@ def __init__(self, static=False): super(Ring, self).__init__(static) def compute_gbest(self, swarm, p, k): - """Updates the global best using a neighborhood approach + """Update the global best using a ring-like neighborhood approach This uses the cKDTree method from :code:`scipy` to obtain the nearest - neighbours + neighbors. Parameters ---------- @@ -92,7 +93,7 @@ def compute_gbest(self, swarm, p, k): return (best_pos, best_cost) def compute_velocity(self, swarm, clamp=None): - """Computes the velocity matrix + """Compute the velocity matrix This method updates the velocity matrix using the best and current positions of the swarm. The velocity matrix is computed using the @@ -130,7 +131,7 @@ def compute_velocity(self, swarm, clamp=None): return ops.compute_velocity(swarm, clamp) def compute_position(self, swarm, bounds=None): - """Updates the position matrix + """Update the position matrix This method updates the position matrix given the current position and the velocity. If bounded, it waives updating the position. diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index 9ddff269..5ed23f93 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -3,8 +3,9 @@ """ A Star Network Topology -This class implements a star topology where all particles are connected to -one another. This social behavior is often found in GlobalBest PSO +This class implements a star topology. In this topology, +all particles are connected to one another. This social +behavior is often found in GlobalBest PSO optimizers. """ @@ -27,7 +28,7 @@ def __init__(self): super(Star, self).__init__(static=False) def compute_gbest(self, swarm): - """Obtains the global best cost and position based on a star topology + """Update the global best using a star topology This method takes the current pbest_pos and pbest_cost, then returns the minimum cost and position from the matrix. It should be used in @@ -73,7 +74,7 @@ def compute_gbest(self, swarm): return (best_pos, best_cost) def compute_velocity(self, swarm, clamp=None): - """Computes the velocity matrix + """Compute the velocity matrix This method updates the velocity matrix using the best and current positions of the swarm. The velocity matrix is computed using the @@ -111,7 +112,7 @@ def compute_velocity(self, swarm, clamp=None): return ops.compute_velocity(swarm, clamp) def compute_position(self, swarm, bounds=None): - """Updates the position matrix + """Update the position matrix This method updates the position matrix given the current position and the velocity. If bounded, it waives updating the position. diff --git a/pyswarms/base/base_discrete.py b/pyswarms/base/base_discrete.py index a52aced9..382e972f 100644 --- a/pyswarms/base/base_discrete.py +++ b/pyswarms/base/base_discrete.py @@ -41,7 +41,7 @@ class DiscreteSwarmOptimizer(object): def assertions(self): - """Assertion method to check various inputs. + """Check inputs and throw assertions Raises ------ @@ -112,7 +112,7 @@ def __init__( init_pos=None, ftol=-np.inf, ): - """Initializes the swarm. + """Initialize the swarm. Creates a :code:`numpy.ndarray` of positions depending on the number of particles needed and the number of dimensions. @@ -174,7 +174,7 @@ def __init__( self.reset() def _populate_history(self, hist): - """Populates all history lists + """Populate all history lists The :code:`cost_history`, :code:`mean_pbest_history`, and :code:`neighborhood_best` is expected to have a shape of @@ -194,11 +194,11 @@ def _populate_history(self, hist): self.velocity_history.append(hist.velocity) def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): - """Optimizes the swarm for a number of iterations. + """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective function :code:`objective_func` for a number of iterations - :code:`iter.` + :code:`iter.` Parameters ---------- @@ -221,7 +221,7 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): raise NotImplementedError("SwarmBase::optimize()") def reset(self): - """Resets the attributes of the optimizer. + """Reset the attributes of the optimizer All variables/atributes that will be re-initialized when this method is defined here. Note that this method diff --git a/pyswarms/base/base_single.py b/pyswarms/base/base_single.py index ef633387..ade84259 100644 --- a/pyswarms/base/base_single.py +++ b/pyswarms/base/base_single.py @@ -4,7 +4,7 @@ Base class for single-objective Particle Swarm Optimization implementations. -All methods here are abstract and raises a :code:`NotImplementedError` +All methods here are abstract and raise a :code:`NotImplementedError` when not used. When defining your own swarm implementation, create another class, @@ -42,7 +42,7 @@ class SwarmOptimizer(object): def assertions(self): - """Assertion method to check various inputs. + """Check inputs and throw assertions Raises ------ @@ -145,7 +145,7 @@ def __init__( ftol=-np.inf, init_pos=None, ): - """Initializes the swarm. + """Initialize the swarm Creates a Swarm class depending on the values initialized @@ -205,7 +205,7 @@ def __init__( self.reset() def _populate_history(self, hist): - """Populates all history lists + """Populate all history lists The :code:`cost_history`, :code:`mean_pbest_history`, and :code:`neighborhood_best` is expected to have a shape of @@ -225,7 +225,7 @@ def _populate_history(self, hist): self.velocity_history.append(hist.velocity) def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): - """Optimizes the swarm for a number of iterations. + """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective function :code:`objective_func` for a number of iterations @@ -252,7 +252,7 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): raise NotImplementedError("SwarmOptimizer::optimize()") def reset(self): - """Resets the attributes of the optimizer. + """Reset the attributes of the optimizer All variables/atributes that will be re-initialized when this method is defined here. Note that this method diff --git a/pyswarms/discrete/binary.py b/pyswarms/discrete/binary.py index ba2cc2c6..b57e2839 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -66,7 +66,7 @@ class BinaryPSO(DiscreteSwarmOptimizer): def assertions(self): - """Assertion method to check various inputs. + """Check inputs and throw assertions Raises ------ @@ -99,7 +99,7 @@ def __init__( velocity_clamp=None, ftol=-np.inf, ): - """Initializes the swarm. + """Initialize the swarm Attributes ---------- @@ -149,8 +149,8 @@ def __init__( # Initialize the topology self.top = Ring(static=False) - def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): - """Optimizes the swarm for a number of iterations. + 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 function :code:`f` for a number of iterations :code:`iter.` @@ -227,7 +227,7 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): return (final_best_cost, final_best_pos) def _compute_position(self, swarm): - """Updates the position matrix of the swarm. + """Update the position matrix of the swarm This computes the next position in a binary swarm. It compares the sigmoid output of the velocity-matrix and compares it with a randomly @@ -244,7 +244,7 @@ def _compute_position(self, swarm): ) * 1 def _sigmoid(self, x): - """Helper method for the sigmoid function. + """Helper method for the sigmoid function Parameters ---------- diff --git a/pyswarms/single/__init__.py b/pyswarms/single/__init__.py index 34aafcc5..8666dcca 100644 --- a/pyswarms/single/__init__.py +++ b/pyswarms/single/__init__.py @@ -2,6 +2,16 @@ The :mod:`pyswarms.single` module implements various techniques in continuous single-objective optimization. These require only one objective function that can be optimized in a continuous space. + +.. note:: + PSO algorithms scale with the search space. This means that, by + using larger boundaries, the final results are getting larger + as well. + +.. note:: + Please keep in mind that Python has a biggest float number. + So using large boundaries in combination with exponentiation or + multiplication can lead to an :code:`OverflowError`. """ from .global_best import GlobalBestPSO diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py index fa22da76..43a89312 100644 --- a/pyswarms/single/general_optimizer.py +++ b/pyswarms/single/general_optimizer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -""" +r""" A general Particle Swarm Optimization (general PSO) algorithm. It takes a set of candidate solutions, and tries to find the best @@ -81,7 +81,7 @@ def __init__( ftol=-np.inf, init_pos=None, ): - """Initializes the swarm. + """Initialize the swarm Attributes ---------- @@ -191,7 +191,7 @@ def __init__( raise KeyError("Missing k in options") def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): - """Optimizes the swarm for a number of iterations. + """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective function :code:`f` for a number of iterations :code:`iter.` diff --git a/pyswarms/single/global_best.py b/pyswarms/single/global_best.py index 4ff47fb9..28f06439 100644 --- a/pyswarms/single/global_best.py +++ b/pyswarms/single/global_best.py @@ -80,7 +80,7 @@ def __init__( ftol=-np.inf, init_pos=None, ): - """Initializes the swarm. + """Initialize the swarm Attributes ---------- @@ -132,7 +132,7 @@ def __init__( self.top = Star() def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): - """Optimizes the swarm for a number of iterations. + """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective function :code:`f` for a number of iterations :code:`iter.` diff --git a/pyswarms/single/local_best.py b/pyswarms/single/local_best.py index a2999f65..aada1609 100644 --- a/pyswarms/single/local_best.py +++ b/pyswarms/single/local_best.py @@ -79,7 +79,7 @@ class LocalBestPSO(SwarmOptimizer): def assertions(self): - """Assertion method to check various inputs. + """Check inputs and throw assertions Raises ------ @@ -115,7 +115,7 @@ def __init__( init_pos=None, static=False ): - """Initializes the swarm. + """Initialize the swarm Attributes ---------- @@ -179,7 +179,7 @@ def __init__( self.top = Ring(static=static) def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): - """Optimizes the swarm for a number of iterations. + """Optimize the swarm for a number of iterations Performs the optimization to evaluate the objective function :code:`f` for a number of iterations :code:`iter.` diff --git a/pyswarms/utils/environments/plot_environment.py b/pyswarms/utils/environments/plot_environment.py index fc77d60e..6e9321d3 100644 --- a/pyswarms/utils/environments/plot_environment.py +++ b/pyswarms/utils/environments/plot_environment.py @@ -80,7 +80,7 @@ class PlotEnvironment(object): def assertions(self): - """Assertion check""" + """Check inputs and throw assertions""" # Check if the objective_func is a callable if not callable(self.objective_func): raise TypeError("Must pass a callable") @@ -105,7 +105,7 @@ def assertions(self): ) def __init__(self, optimizer, objective_func, iters): - """Runs the optimizer against an objective function for a number + """Run the optimizer against an objective function for a number of iterations Upon initialization, the :code:`optimize` method of the optimizer @@ -150,7 +150,7 @@ def plot_cost( text_fontsize="medium", **kwargs ): - """Creates a simple line plot with the cost in the y-axis and + """Create a simple line plot with the cost in the y-axis and the iteration at the x-axis Parameters @@ -230,7 +230,7 @@ def plot_particles2D( title_fontsize="large", text_fontsize="medium", ): - """Creates an animation of particle movement in 2D-space + """Create an animation of particle movement in 2D-space Parameters ---------- @@ -326,7 +326,7 @@ def plot_particles3D( title_fontsize="large", text_fontsize="medium", ): - """Creates an animation of particle movement in 2D-space + """Create an animation of particle movement in 3D-space Parameters ---------- @@ -414,7 +414,7 @@ def plot_particles3D( return anim def _animate2D(self, i, data, plot, idx): - """Helper animation function that is called seqentially + """Helper animation function that is called sequentially :class:`matplotlib.animation.FuncAnimation` Parameters @@ -441,7 +441,7 @@ def _animate2D(self, i, data, plot, idx): return (plot,) def _animate3D(self, i, data, plot, idx): - """Helper animation function that is called seqentially + """Helper animation function that is called sequentially :class:`matplotlib.animation.FuncAnimation` Parameters diff --git a/pyswarms/utils/plotters/formatters.py b/pyswarms/utils/plotters/formatters.py index 94802d65..b986ecca 100644 --- a/pyswarms/utils/plotters/formatters.py +++ b/pyswarms/utils/plotters/formatters.py @@ -149,7 +149,7 @@ class Mesher(object): alpha = attrib(type=float, validator=instance_of(float), default=0.3) def compute_history_3d(self, pos_history): - """Computes a 3D position matrix + """Compute a 3D position matrix The first two columns are the 2D position in the x and y axes respectively, while the third column is the fitness on that given diff --git a/pyswarms/utils/plotters/plotters.py b/pyswarms/utils/plotters/plotters.py index f2ce0e0a..28516a65 100644 --- a/pyswarms/utils/plotters/plotters.py +++ b/pyswarms/utils/plotters/plotters.py @@ -83,7 +83,7 @@ def plot_cost_history( cost_history, ax=None, title="Cost History", designer=None, **kwargs ): - """Creates a simple line plot with the cost in the y-axis and + """Create a simple line plot with the cost in the y-axis and the iteration at the x-axis Parameters @@ -147,9 +147,9 @@ def plot_contour( animator=None, **kwargs ): - """Draws a 2D contour map for particle trajectories + """Draw a 2D contour map for particle trajectories - Here, the space is represented as flat plane. The contours indicate the + Here, the space is represented as a flat plane. The contours indicate the elevation with respect to the objective function. This works best with 2-dimensional swarms with their fitness in z-space. @@ -249,7 +249,7 @@ def plot_surface( mark=None, **kwargs ): - """Plots a swarm's trajectory in 3D + """Plot a swarm's trajectory in 3D This is useful for plotting the swarm's 2-dimensional position with respect to the objective function. The value in the z-axis is the fitness diff --git a/pyswarms/utils/search/base_search.py b/pyswarms/utils/search/base_search.py index 4aa3fc9b..9bb2cbae 100644 --- a/pyswarms/utils/search/base_search.py +++ b/pyswarms/utils/search/base_search.py @@ -12,7 +12,7 @@ class SearchBase(object): def assertions(self): - """Assertion method to check :code:`optimizer` input. + """Assertion method to check :code:`optimizer` input Raises ------ @@ -36,7 +36,7 @@ def __init__( bounds=None, velocity_clamp=(0, 1), ): - """Initializes the Search. + """Initialize the Search Attributes ---------- @@ -89,7 +89,7 @@ def __init__( self.assertions() def generate_score(self, options): - """Generates score for optimizer's performance on objective function. + """Generate score for optimizer's performance on objective function Parameters ---------- @@ -107,8 +107,8 @@ def generate_score(self, options): return f.optimize(self.objective_func, self.iters)[0] def search(self, maximum=False): - """Compares optimizer's objective function performance scores - for all combinations of provided parameters. + """Compare optimizer's objective function performance scores + for all combinations of provided parameters Parameters ---------- diff --git a/pyswarms/utils/search/grid_search.py b/pyswarms/utils/search/grid_search.py index 4d518550..46e2fba4 100644 --- a/pyswarms/utils/search/grid_search.py +++ b/pyswarms/utils/search/grid_search.py @@ -55,7 +55,7 @@ def __init__( bounds=None, velocity_clamp=(0, 1), ): - """Initializes the paramsearch.""" + """Initialize the Search""" # Assign attributes super(GridSearch, self).__init__( @@ -72,7 +72,7 @@ def __init__( self.assertions() def generate_grid(self): - """Generates the grid of all hyperparameter value combinations.""" + """Generate the grid of all hyperparameter value combinations""" # Extract keys and values from options dictionary params = self.options.keys() diff --git a/pyswarms/utils/search/random_search.py b/pyswarms/utils/search/random_search.py index 43215f17..b5dd5df6 100644 --- a/pyswarms/utils/search/random_search.py +++ b/pyswarms/utils/search/random_search.py @@ -49,7 +49,7 @@ class RandomSearch(SearchBase): within specified bounds for specified number of selection iterations.""" def assertions(self): - """Assertion method to check :code:`n_selection_iters` input. + """Assertion method to check :code:`n_selection_iters` input Raises ------ @@ -76,7 +76,7 @@ def __init__( bounds=None, velocity_clamp=(0, 1), ): - """Initializes the paramsearch. + """Initialize the Search Attributes ---------- @@ -101,7 +101,7 @@ def __init__( self.assertions() def generate_grid(self): - """Generates the grid of hyperparameter value combinations.""" + """Generate the grid of hyperparameter value combinations""" options = dict(self.options) params = {} diff --git a/tests/backend/topology/conftest.py b/tests/backend/topology/conftest.py index 906323cb..4aade1b1 100644 --- a/tests/backend/topology/conftest.py +++ b/tests/backend/topology/conftest.py @@ -53,4 +53,4 @@ 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}, } - return Swarm(**attrs_at_t) \ No newline at end of file + return Swarm(**attrs_at_t) diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py index 5d396dfb..a5cccd8c 100644 --- a/tests/optimizers/test_general_optimizer.py +++ b/tests/optimizers/test_general_optimizer.py @@ -52,6 +52,21 @@ def test_keyword_exception_random(options, static): GeneralOptimizerPSO(5, 2, options, Random(static=static)) +@pytest.mark.parametrize( + "options", + [ + {"c2": 0.7, "w": 0.5, "k": 2}, + {"c1": 0.5, "w": 0.5, "k": 2}, + {"c1": 0.5, "c2": 0.7, "k": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5}, + ], +) +def test_keyword_exception_random(options): + """Tests if exceptions are thrown when keywords are missing and a Random topology is chosen""" + with pytest.raises(KeyError): + GeneralOptimizerPSO(5, 2, options, Random()) + + @pytest.mark.parametrize( "options", [ @@ -82,6 +97,21 @@ def test_invalid_k_value(options, static): GeneralOptimizerPSO(5, 2, options, Random(static=static)) +@pytest.mark.parametrize( + "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} + ], +) +def test_invalid_k_value(options): + """Tests if exception is thrown when passing + an invalid value for k when using a Random topology""" + with pytest.raises(ValueError): + GeneralOptimizerPSO(5, 2, options, Random()) + + @pytest.mark.parametrize( "topology", [object(), int(), dict()] @@ -92,6 +122,10 @@ def test_topology_type_exception(options, topology): GeneralOptimizerPSO(5, 2, options, topology) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize( "bounds", [ @@ -106,6 +140,10 @@ def test_bounds_size_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize( "bounds", [ @@ -119,6 +157,10 @@ def test_bounds_maxmin_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize( "bounds", [ @@ -132,6 +174,10 @@ def test_bound_type_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) def test_vclamp_shape_exception(velocity_clamp, options, topology): """Tests if exception is raised when velocity_clamp's size is not equal @@ -140,6 +186,10 @@ def test_vclamp_shape_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) def test_vclamp_maxmin_exception(velocity_clamp, options, topology): """Tests if the max velocity_clamp is less than min velocity_clamp and @@ -148,6 +198,10 @@ def test_vclamp_maxmin_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) @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""" @@ -177,7 +231,10 @@ def test_training_history_shape(gbest_history, history, expected_shape): pso = vars(gbest_history) assert np.array(pso[history]).shape == expected_shape - +@pytest.mark.parametrize( + "topology", + [Star(), Ring(), Pyramid(), Random()] +) def test_ftol_effect(options, topology): """Test if setting the ftol breaks the optimization process accodingly""" pso = GeneralOptimizerPSO(10, 2, options=options, topology=topology, ftol=1e-1) From 3a4c8c0200957efb16e6845040aabe1684e3c1ea Mon Sep 17 00:00:00 2001 From: Lj Miranda <12949683+ljvmiranda921@users.noreply.github.com> Date: Sun, 29 Jul 2018 19:38:31 +0900 Subject: [PATCH 32/38] Update authors list (#181) This commit updates the authors list in both README.md and AUTHORS.rst Signed-off-by: Lester James V. Miranda --- AUTHORS.rst | 12 +++++++++--- README.md | 13 ++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 168deb4a..9cf73d1d 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -9,20 +9,23 @@ The package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypacka .. _Cookiecutter: https://github.com/audreyr/cookiecutter .. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage -Development Lead ----------------- +Maintainers +----------- * Lester James V. Miranda (`@ljvmiranda921`_) +* Aaron Moser (`@whzup`_) +* Siobhán K. Cronin (`@SioKCronin`_) Contributors ------------ * Carl-K (`@Carl-K`_) -* Siobhán K Cronin (`@SioKCronin`_) * Andrew Jarcho (`@jazcap53`_) * Charalampos Papadimitriou (`@CPapadim`_) * Mamady Nabé (`@mamadyonline`_) * Erik (`@slek120`_) +* Jay Speidell (`@jayspeidell`_) +* Bradahoward (`@bradahoward`_) * Thomas (`@ThomasCES`_) .. _`@ljvmiranda921`: https://github.com/ljvmiranda921 @@ -32,4 +35,7 @@ Contributors .. _`@CPapadim`: https://github.com/CPapadim .. _`@mamadyonline`: https://github.com/mamadyonline .. _`@slek120`: https://github.com/slek120 +.. _`@whzup`: https://github.com/whzup +.. _`@jayspeidell`: https://github.com/jayspeidell +.. _`@bradahoward`: https://github.com/bradahoward .. _`@ThomasCES`: https://github.com/ThomasCES diff --git a/README.md b/README.md index f0bd6235..7fb1ba30 100644 --- a/README.md +++ b/README.md @@ -232,15 +232,22 @@ animation3d = plot_surface(pos_history=pos_history_3d, ## Contributing -PySwarms is currently maintained by a single person (me!) with the aid of a -few but very helpful contributors. We would appreciate it if you can lend a -hand with the following: +PySwarms is currently maintained by a small yet dedicated team: +- Lester James V. Miranda ([@ljvmiranda921](https://github.com/ljvmiranda921)) +- Siobhán K. Cronin ([@SioKCronin](https://github.com/SioKCronin)) +- Aaron Moser ([@whzup](https://github.com/whzup)) + +And we would appreciate it if you can lend a hand with the following: * Find bugs and fix them * Update documentation in docstrings * Implement new optimizers to our collection * Make utility functions more robust. +We would also like to acknowledge [all our +contributors](http://pyswarms.readthedocs.io/en/latest/authors.html), past and +present, for making this project successful! + If you wish to contribute, check out our [contributing guide]. Moreover, you can also see the list of features that need some help in our [Issues] page. From 7428b94ff8da8d872036ed37a8a7d469cdc1a19b Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Mon, 30 Jul 2018 06:42:46 +0200 Subject: [PATCH 33/38] Clean up changes for v.0.3.0 (#189) Resolves #183 Set the Star topology to static and only assign the neighbor_idx when it has not already been assigned. Added the VonNeumann topology to the GeneralOptimizer with the necessary changes to the documentation and new exceptions for missing attributes. Added the VonNeumann topology to the GeneralOptimizer tests. The exceptions are tested and the topology fixture now also uses the VonNeumann class. Cleaned up the tests and added a static parametrization. Fixed a bug in the Pyramid topology where the optimize function of the GeneralOptimizer would output a ValueError because for some particles the neighbours were not computed. This is achieved by passing custom qhull_options to the Delaunay class. For these qhull_options the necessary documentation and a link to the qhull website was added. Additionally, changed the boundary for the connectivity of the particles in the Pyramid topology to be swarm.dimensions + 1 instead of 5. The reason or this change is that a n-dimensional simplex has n+1 vertices. Signed-off-by: Lester James V. Miranda Committed-by: Aaron Moser (@whzup ) --- pyswarms/backend/topology/pyramid.py | 13 ++-- pyswarms/backend/topology/star.py | 5 +- pyswarms/backend/topology/von_neumann.py | 9 +-- pyswarms/discrete/binary.py | 3 +- pyswarms/single/general_optimizer.py | 71 +++++++++++++++------ tests/backend/topology/test_pyramid.py | 2 +- tests/backend/topology/test_random.py | 2 +- tests/backend/topology/test_ring.py | 2 +- tests/backend/topology/test_star.py | 2 +- tests/backend/topology/test_von_neumann.py | 2 +- tests/optimizers/conftest.py | 14 ++--- tests/optimizers/test_general_optimizer.py | 73 ++++++++-------------- 12 files changed, 103 insertions(+), 95 deletions(-) diff --git a/pyswarms/backend/topology/pyramid.py b/pyswarms/backend/topology/pyramid.py index 2e3cb751..1f209502 100644 --- a/pyswarms/backend/topology/pyramid.py +++ b/pyswarms/backend/topology/pyramid.py @@ -36,12 +36,15 @@ def __init__(self, static=False): def compute_gbest(self, swarm): """Update the global best using a pyramid neighborhood approach - This topology uses the Delaunay method from :code:`scipy`. This method is used to triangulate - N-dimensional space into simplices. The vertices of the simplicies consist of swarm particles. [SIS2008] + This topology uses the :code:`Delaunay` class from :code:`scipy`. To prevent precision errors in the Delaunay + class, custom :code:`qhull_options` were added. Namely, :code:`QJ0.001 Qbb Qc Qx`. The meaning of those options + is explained in [qhull]. This method is used to triangulate N-dimensional space into simplices. The vertices of + the simplicies consist of swarm particles. This method is adapted from the work of Lane et al.[SIS2008] [SIS2008] J. Lane, A. Engelbrecht and J. Gain, "Particle swarm optimization with spatially meaningful neighbours," 2008 IEEE Swarm Intelligence Symposium, St. Louis, MO, 2008, pp. 1-8. doi: 10.1109/SIS.2008.4668281 + [qhull] http://www.qhull.org/html/qh-optq.htm Parameters ---------- @@ -56,15 +59,15 @@ def compute_gbest(self, swarm): Best cost """ try: - # If there are less than 5 particles they are all connected - if swarm.n_particles < 5: + # 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)) 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) + 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( diff --git a/pyswarms/backend/topology/star.py b/pyswarms/backend/topology/star.py index 5ed23f93..8c666b2d 100644 --- a/pyswarms/backend/topology/star.py +++ b/pyswarms/backend/topology/star.py @@ -25,7 +25,7 @@ class Star(Topology): def __init__(self): - super(Star, self).__init__(static=False) + super(Star, self).__init__(static=True) def compute_gbest(self, swarm): """Update the global best using a star topology @@ -61,7 +61,8 @@ def compute_gbest(self, swarm): Best cost """ try: - self.neighbor_idx = np.tile(np.arange(swarm.n_particles), (swarm.n_particles, 1)) + if self.neighbor_idx is None: + 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 1ad99762..ad44cb85 100644 --- a/pyswarms/backend/topology/von_neumann.py +++ b/pyswarms/backend/topology/von_neumann.py @@ -19,7 +19,7 @@ class VonNeumann(Ring): def __init__(self): super(VonNeumann, self).__init__(static=True) - def compute_gbest(self, swarm, p=1, r=1): + def compute_gbest(self, swarm, p, r): """Updates the global best using a neighborhood approach The Von Neumann topology inherits from the Ring topology and uses @@ -71,7 +71,8 @@ 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 4a84031a..72d8d536 100644 --- a/pyswarms/discrete/binary.py +++ b/pyswarms/discrete/binary.py @@ -149,7 +149,7 @@ 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 @@ -257,4 +257,3 @@ def _sigmoid(self, x): Output sigmoid computation """ return 1 / (1 + np.exp(-x)) - diff --git a/pyswarms/single/general_optimizer.py b/pyswarms/single/general_optimizer.py index 43a89312..33114327 100644 --- a/pyswarms/single/general_optimizer.py +++ b/pyswarms/single/general_optimizer.py @@ -64,7 +64,7 @@ # Import from package from ..base import SwarmOptimizer from ..backend.operators import compute_pbest -from ..backend.topology import Topology, Ring, Random +from ..backend.topology import Topology, Ring, Random, VonNeumann from ..utils.console_utils import cli_print, end_report @@ -98,7 +98,7 @@ def __init__( social parameter * w : float inertia parameter - if used with the :code:`Ring` or :code:`Random` topology the additional + if used with the :code:`Ring`, :code:`VonNeumann` or :code:`Random` topology the additional parameter k must be included * k : int number of neighbors to be considered. Must be a @@ -109,6 +109,12 @@ def __init__( the Minkowski p-norm to use. 1 is the sum-of-absolute values (or L1 distance) while 2 is the Euclidean (or L2) distance. + if used with the :code:`VonNeumann` topology the additional + parameters p and r must be included + * r: int + the range of the VonNeumann topology. + This is used to determine the number of + neighbours in the topology. topology : pyswarms.backend.topology.Topology a :code:`Topology` object that defines the topology to use in the optimization process. The currently available topologies @@ -117,6 +123,8 @@ def __init__( All particles are connected * Ring (static and dynamic) Particles are connected to the k nearest neighbours + * VonNeumann + Particles are connected in a VonNeumann topology * Pyramid (static and dynamic) Particles are connected in N-dimensional simplices * Random (static and dynamic) @@ -162,7 +170,7 @@ def __init__( self.top = topology # Case for the Ring topology - if isinstance(topology, Ring): + if isinstance(topology, (Ring, VonNeumann)): # Assign p-value as attributes self.p = options["p"] # Exceptions for the p value @@ -174,21 +182,38 @@ def __init__( "or 2 (for L2/Euclidean)." ) - # Case for Random and Ring topologies - if isinstance(topology, (Random, Ring)): - # Assign k-neighbors as attribute - self.k = options["k"] - if not isinstance(self.k, int): - raise ValueError( - "No. of neighbors must be an integer between" - "0 and no. of particles." - ) - if not 0 <= self.k <= self.n_particles - 1: - raise ValueError( - "No. of neighbors must be between 0 and no. " "of particles." - ) - if "k" not in self.options: - raise KeyError("Missing k in options") + # Case for Random, VonNeumann and Ring topologies + if isinstance(topology, (Random, Ring, VonNeumann)): + if not isinstance(topology, VonNeumann): + self.k = options["k"] + if not isinstance(self.k, int): + raise ValueError( + "No. of neighbors must be an integer between" + "0 and no. of particles." + ) + if not 0 <= self.k <= self.n_particles - 1: + raise ValueError( + "No. of neighbors must be between 0 and no. " + "of particles." + ) + if "k" not in self.options: + raise KeyError("Missing k in options") + else: + # Assign range r as attribute + self.r = options["r"] + if not isinstance(self.r, int): + raise ValueError("The range must be a positive integer") + if ( + self.r <= 0 + or not 0 + <= VonNeumann.delannoy(self.swarm.dimensions, self.r) + <= self.n_particles - 1 + ): + raise ValueError( + "The range must be set such that the computed" + "Delannoy number (number of neighbours) is" + "between 0 and the no. of particles." + ) def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): """Optimize the swarm for a number of iterations @@ -227,12 +252,18 @@ def optimize(self, objective_func, iters, print_step=1, verbose=1, **kwargs): ) 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): + 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 ) - # If the topology is a random topology pass the neighbor attribute to the compute_gbest() method + # If the topology is a VonNeumann topology pass the neighbour and range attribute to compute_gbest() + if 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.r + ) + # If the topology is a random topology pass the neighbor attribute to compute_gbest() elif isinstance(self.top, Random): # Get minima of pbest and check if it's less than gbest if np.min(self.swarm.pbest_cost) < self.swarm.best_cost: diff --git a/tests/backend/topology/test_pyramid.py b/tests/backend/topology/test_pyramid.py index dc0b1ced..31583428 100644 --- a/tests/backend/topology/test_pyramid.py +++ b/tests/backend/topology/test_pyramid.py @@ -49,5 +49,5 @@ def test_compute_position_return_values(swarm, bounds, static): def test_neighbor_idx(swarm, static): """Test if the neighbor_idx attribute is assigned""" topology = Pyramid(static=static) - p = topology.compute_gbest(swarm) + topology.compute_gbest(swarm) assert topology.neighbor_idx is not None diff --git a/tests/backend/topology/test_random.py b/tests/backend/topology/test_random.py index 15872897..1b12a5dc 100644 --- a/tests/backend/topology/test_random.py +++ b/tests/backend/topology/test_random.py @@ -81,5 +81,5 @@ def test_compute_neighbors_adjacency_matrix(swarm, k, static): def test_neighbor_idx(swarm, k, static): """Test if the neighbor_idx attribute is assigned""" topology = Random(static=static) - p = topology.compute_gbest(swarm, k) + topology.compute_gbest(swarm, k) assert topology.neighbor_idx is not None diff --git a/tests/backend/topology/test_ring.py b/tests/backend/topology/test_ring.py index eaf54861..c7723724 100644 --- a/tests/backend/topology/test_ring.py +++ b/tests/backend/topology/test_ring.py @@ -53,5 +53,5 @@ def test_compute_position_return_values(swarm, bounds, static): def test_neighbor_idx(swarm, static, p, k): """Test if the neighbor_idx attribute is assigned""" topology = Ring(static=static) - p = topology.compute_gbest(swarm, p=p, k=k) + topology.compute_gbest(swarm, p=p, k=k) assert topology.neighbor_idx is not None diff --git a/tests/backend/topology/test_star.py b/tests/backend/topology/test_star.py index aab347a3..1dc72415 100644 --- a/tests/backend/topology/test_star.py +++ b/tests/backend/topology/test_star.py @@ -45,5 +45,5 @@ def test_compute_position_return_values(swarm, bounds): def test_neighbor_idx(swarm): """Test if the neighbor_idx attribute is assigned""" topology = Star() - p = topology.compute_gbest(swarm) + topology.compute_gbest(swarm) assert topology.neighbor_idx is not None diff --git a/tests/backend/topology/test_von_neumann.py b/tests/backend/topology/test_von_neumann.py index 23056425..e34a9000 100644 --- a/tests/backend/topology/test_von_neumann.py +++ b/tests/backend/topology/test_von_neumann.py @@ -49,7 +49,7 @@ def test_compute_position_return_values(swarm, bounds): def test_neighbor_idx(swarm, p, r): """Test if the neighbor_idx attribute is assigned""" topology = VonNeumann() - p = topology.compute_gbest(swarm, p=p, r=r) + topology.compute_gbest(swarm, p=p, r=r) assert topology.neighbor_idx is not None diff --git a/tests/optimizers/conftest.py b/tests/optimizers/conftest.py index ba1f4697..6dfee2c3 100644 --- a/tests/optimizers/conftest.py +++ b/tests/optimizers/conftest.py @@ -11,7 +11,7 @@ from pyswarms.single import GlobalBestPSO, LocalBestPSO, GeneralOptimizerPSO from pyswarms.discrete import BinaryPSO from pyswarms.utils.functions.single_obj import sphere_func -from pyswarms.backend.topology import Star, Ring, Pyramid, Random +from pyswarms.backend.topology import Star, Ring, Pyramid, Random, VonNeumann @pytest.fixture(scope="module") @@ -93,7 +93,7 @@ def binary_reset(): @pytest.fixture def options(): """Default options dictionary for most PSO use-cases""" - options_ = {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2} + options_ = {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 2, "p": 2, "r": 1} return options_ @@ -101,16 +101,10 @@ def options(): Star(), Ring(static=False), Ring(static=True), Pyramid(static=False), Pyramid(static=True), - Random(static=False), Random(static=True) + Random(static=False), Random(static=True), + VonNeumann() ]) def topology(request): """Parametrized topology parameter""" topology_ = request.param return topology_ - - -@pytest.fixture(scope="module", params=[True, False]) -def static(request): - """Parametrized static parameter""" - static_ = request.param - return static_ diff --git a/tests/optimizers/test_general_optimizer.py b/tests/optimizers/test_general_optimizer.py index a5cccd8c..970e9116 100644 --- a/tests/optimizers/test_general_optimizer.py +++ b/tests/optimizers/test_general_optimizer.py @@ -7,7 +7,7 @@ # Import from package from pyswarms.single import GeneralOptimizerPSO -from pyswarms.backend.topology import Star, Ring, Pyramid, Random +from pyswarms.backend.topology import Star, Ring, Pyramid, Random, VonNeumann from pyswarms.utils.functions.single_obj import sphere_func @@ -21,6 +21,7 @@ def test_keyword_exception(options, topology): GeneralOptimizerPSO(5, 2, options, topology) +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "options", [ @@ -37,21 +38,24 @@ def test_keyword_exception_ring(options, static): GeneralOptimizerPSO(5, 2, options, Ring(static=static)) +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "options", [ - {"c2": 0.7, "w": 0.5, "k": 2}, - {"c1": 0.5, "w": 0.5, "k": 2}, - {"c1": 0.5, "c2": 0.7, "k": 2}, - {"c1": 0.5, "c2": 0.7, "w": 0.5}, + {"c2": 0.7, "w": 0.5, "r": 2, "p": 2}, + {"c1": 0.5, "w": 0.5, "r": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "r": 2, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "r": 2}, ], ) -def test_keyword_exception_random(options, static): - """Tests if exceptions are thrown when keywords are missing and a Random topology is chosen""" +def test_keyword_exception_vonneumann(options, static): + """Tests if exceptions are thrown when keywords are missing and a VonNeumann topology is chosen""" with pytest.raises(KeyError): - GeneralOptimizerPSO(5, 2, options, Random(static=static)) + GeneralOptimizerPSO(5, 2, options, VonNeumann()) +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "options", [ @@ -61,12 +65,13 @@ def test_keyword_exception_random(options, static): {"c1": 0.5, "c2": 0.7, "w": 0.5}, ], ) -def test_keyword_exception_random(options): +def test_keyword_exception_random(options, static): """Tests if exceptions are thrown when keywords are missing and a Random topology is chosen""" with pytest.raises(KeyError): - GeneralOptimizerPSO(5, 2, options, Random()) + GeneralOptimizerPSO(5, 2, options, Random(static=static)) +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "options", [ @@ -85,18 +90,19 @@ def test_invalid_k_or_p_values(options, static): @pytest.mark.parametrize( "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, "r": -1, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "r": 6, "p": 2}, + {"c1": 0.5, "c2": 0.7, "w": 0.5, "r": 2, "p": 5}, ], ) -def test_invalid_k_value(options, static): +def test_invalid_r_or_p_values(options): """Tests if exception is thrown when passing - an invalid value for k when using a Random topology""" + an invalid value for r or p when using a Von Neumann topology""" with pytest.raises(ValueError): - GeneralOptimizerPSO(5, 2, options, Random(static=static)) + GeneralOptimizerPSO(5, 2, options, VonNeumann()) +@pytest.mark.parametrize("static", [True, False]) @pytest.mark.parametrize( "options", [ @@ -105,11 +111,11 @@ def test_invalid_k_value(options, static): {"c1": 0.5, "c2": 0.7, "w": 0.5, "k": 0.5} ], ) -def test_invalid_k_value(options): +def test_invalid_k_value(options, static): """Tests if exception is thrown when passing an invalid value for k when using a Random topology""" with pytest.raises(ValueError): - GeneralOptimizerPSO(5, 2, options, Random()) + GeneralOptimizerPSO(5, 2, options, Random(static=static)) @pytest.mark.parametrize( @@ -122,10 +128,6 @@ def test_topology_type_exception(options, topology): GeneralOptimizerPSO(5, 2, options, topology) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize( "bounds", [ @@ -140,10 +142,6 @@ def test_bounds_size_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize( "bounds", [ @@ -157,10 +155,6 @@ def test_bounds_maxmin_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize( "bounds", [ @@ -174,10 +168,6 @@ def test_bound_type_exception(bounds, options, topology): GeneralOptimizerPSO(5, 2, options=options, topology=topology, bounds=bounds) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize("velocity_clamp", [(1, 1, 1), (2, 3, 1)]) def test_vclamp_shape_exception(velocity_clamp, options, topology): """Tests if exception is raised when velocity_clamp's size is not equal @@ -186,10 +176,6 @@ def test_vclamp_shape_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @pytest.mark.parametrize("velocity_clamp", [(3, 2), (10, 8)]) def test_vclamp_maxmin_exception(velocity_clamp, options, topology): """Tests if the max velocity_clamp is less than min velocity_clamp and @@ -198,10 +184,6 @@ def test_vclamp_maxmin_exception(velocity_clamp, options, topology): GeneralOptimizerPSO(5, 2, velocity_clamp=velocity_clamp, options=options, topology=topology) -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) @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""" @@ -231,12 +213,9 @@ def test_training_history_shape(gbest_history, history, expected_shape): pso = vars(gbest_history) assert np.array(pso[history]).shape == expected_shape -@pytest.mark.parametrize( - "topology", - [Star(), Ring(), Pyramid(), Random()] -) + def test_ftol_effect(options, topology): - """Test if setting the ftol breaks the optimization process accodingly""" + """Test if setting the ftol breaks the optimization process accordingly""" pso = GeneralOptimizerPSO(10, 2, options=options, topology=topology, ftol=1e-1) pso.optimize(sphere_func, 2000, verbose=0) assert np.array(pso.cost_history).shape != (2000,) From 63ef1abf87ee69325d81844c6a2a511342b8a7f8 Mon Sep 17 00:00:00 2001 From: Lj Miranda <12949683+ljvmiranda921@users.noreply.github.com> Date: Mon, 30 Jul 2018 13:48:37 +0900 Subject: [PATCH 34/38] Update dependencies in requirements_dev.txt (#188) The following updates were done by pyup-bot: - Update pytest from 3.6.3 to 3.6.4 (#184) - Update pip from 10.0.1 to 18.0 (#178) - Update cryptography from 2.2.2 to 2.3 (#173) - Update sphinx from 1.7.5 to 1.7.6 (#166) - Update tox from 3.0.0 to 3.1.2 (#162) --- requirements_dev.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 1e936161..f1d2e839 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,18 +1,18 @@ -pip==10.0.1 +pip==18.0 bumpversion==0.5.3 wheel==0.31.1 watchdog==0.8.3 flake8==3.5.0 mock==2.0.0 -tox==3.0.0 +tox==3.1.2 coverage==4.5.1 -Sphinx==1.7.5 -cryptography==2.2.2 +Sphinx==1.7.6 +cryptography==2.3 PyYAML==3.13 # pyup: ignore future==0.16.0 scipy>=0.17.0 numpy>=1.13.0 matplotlib>=1.3.1 -pytest==3.6.3 +pytest==3.6.4 attrs==18.1.0 pre-commit \ No newline at end of file From f7ce89dbd10e4cb8d99010f5c0560a4079a41a6f Mon Sep 17 00:00:00 2001 From: Lj Miranda <12949683+ljvmiranda921@users.noreply.github.com> Date: Tue, 31 Jul 2018 17:10:22 +0900 Subject: [PATCH 35/38] Bump version to v.0.3.0 (#191) --- HISTORY.rst | 26 ++++++++++++++-- pyswarms/__init__.py | 4 +-- setup.cfg | 2 +- setup.py | 72 ++++++++++++++++++++++---------------------- 4 files changed, 63 insertions(+), 41 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 332144af..69380922 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -107,8 +107,30 @@ Thanks for all the wonderful Pull Requests, `@mamadyonline `_! +* New GeneralOptimizer algorithm that allows you to switch-out topologies for your optimization needs (PR# 151). Thanks a lot `@whzup `_! +* All topologies now have a static attribute. Neigbors can now be set initially or computed dynamically (PR# 164_). Thanks a lot `@whzup `_! +* New single-objective functions (PR# 168_)! Awesome work, `@jayspeidell `_! +* New tutorial on Inverse Kinematics using Particle Swarm Optimization (PR# 141_) Thanks a lot `@whzup `_! +* New plotters module for visualization. The environment module is now deprecated (PR# 135_). +* Keyword arguments can now be passed in the optimize() method for your custom objective functions (PR# 144_). Great job, `@bradahoward ` + +.. _135: https://github.com/ljvmiranda921/pyswarms/pull/135 +.. _141: https://github.com/ljvmiranda921/pyswarms/pull/141 +.. _142: https://github.com/ljvmiranda921/pyswarms/pull/142 +.. _144: https://github.com/ljvmiranda921/pyswarms/pull/144 +.. _151: https://github.com/ljvmiranda921/pyswarms/pull/151 +.. _155: https://github.com/ljvmiranda921/pyswarms/pull/155 +.. _164: https://github.com/ljvmiranda921/pyswarms/pull/164 +.. _168: https://github.com/ljvmiranda921/pyswarms/pull/168 +.. _176: https://github.com/ljvmiranda921/pyswarms/pull/176 +.. _177: https://github.com/ljvmiranda921/pyswarms/pull/177 \ No newline at end of file diff --git a/pyswarms/__init__.py b/pyswarms/__init__.py index c9d32098..98b853bb 100644 --- a/pyswarms/__init__.py +++ b/pyswarms/__init__.py @@ -11,8 +11,8 @@ """ __author__ = """Lester James V. Miranda""" -__email__ = 'ljvmiranda@gmail.com' -__version__ = '0.2.1' +__email__ = "ljvmiranda@gmail.com" +__version__ = "0.3.0" from .single import global_best, local_best, general_optimizer from .discrete import binary diff --git a/setup.cfg b/setup.cfg index 2c88f882..1f58d7c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.2.1 +current_version = 0.3.0 commit = True tag = True diff --git a/setup.py b/setup.py index f43e38a2..acb3c98d 100644 --- a/setup.py +++ b/setup.py @@ -5,19 +5,19 @@ from setuptools import setup, find_packages -with open('README.md', encoding="utf8") as readme_file: +with open("README.md", encoding="utf8") as readme_file: readme = readme_file.read() requirements = [ - 'PyYAML==3.12', - 'future==0.16.0', - 'scipy>=0.17.0', - 'numpy>=1.13.0', - 'matplotlib>=1.3.1', - 'mock==2.0.0', - 'pytest==3.2.1', - 'attrs==18.1.0', - 'pre-commit' + "PyYAML==3.13", + "future==0.16.0", + "scipy>=0.17.0", + "numpy>=1.13.0", + "matplotlib>=1.3.1", + "mock==2.0.0", + "pytest==3.6.4", + "attrs==18.1.0", + "pre-commit", ] setup_requirements = [ @@ -25,45 +25,45 @@ ] test_requirements = [ - 'PyYAML==3.12', - 'future==0.16.0', - 'scipy>=0.17.0', - 'numpy>=1.13.0', - 'matplotlib>=1.3.1', - 'mock==2.0.0', - 'pytest==3.2.1', - 'attrs==18.1.0', - 'pre-commit' + "PyYAML==3.13", + "future==0.16.0", + "scipy>=0.17.0", + "numpy>=1.13.0", + "matplotlib>=1.3.1", + "mock==2.0.0", + "pytest==3.6.4", + "attrs==18.1.0", + "pre-commit", ] setup( - name='pyswarms', - version='0.2.1', + name="pyswarms", + version="0.3.0", description="A Python-based Particle Swarm Optimization (PSO) library.", long_description=readme, long_description_content_type="text/markdown", author="Lester James V. Miranda", - author_email='ljvmiranda@gmail.com', - url='https://github.com/ljvmiranda921/pyswarms', - packages=find_packages(exclude=['docs', 'tests']), + author_email="ljvmiranda@gmail.com", + url="https://github.com/ljvmiranda921/pyswarms", + packages=find_packages(exclude=["docs", "tests"]), include_package_data=True, install_requires=requirements, license="MIT license", zip_safe=False, - keywords='pyswarms', + keywords="pyswarms", classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'Intended Audience :: Education', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Topic :: Scientific/Engineering', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Topic :: Scientific/Engineering", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", ], - test_suite='tests', + test_suite="tests", tests_require=test_requirements, setup_requires=setup_requirements, ) From c29e2605021546e6fac0196d6a06afdf3be2abb0 Mon Sep 17 00:00:00 2001 From: Lj Miranda <12949683+ljvmiranda921@users.noreply.github.com> Date: Tue, 31 Jul 2018 17:52:58 +0900 Subject: [PATCH 36/38] Update image sources in README (#192) This commit updates all image source from local to Imgur. Turns out that whenever we create a setup file, the docs directory is excluded, thus, the images are nowhere to be found. By sourcing them from Imgur, we can resolve this problem. Signed-off-by: Lester James V. Miranda --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7fb1ba30..aa2cdc69 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -![PySwarms Logo](docs/pyswarms-header.png) - +![PySwarms Logo](https://i.imgur.com/eX8oqPQ.png) --- @@ -175,14 +174,12 @@ hyperparameter options that enables it. 9.504769054771 ``` -### Plotting environments +### Swarm visualization It is also possible to plot optimizer performance for the sake of formatting. -The plotting environment is built on top of `matplotlib`, making it +The plotters moule is built on top of `matplotlib`, making it highly-customizable. -The environment takes in the optimizer and its parameters, then performs a -fresh run to plot the cost and create animation. ```python import pyswarms as ps @@ -197,7 +194,7 @@ plot_cost_history(optimizer.cost_history) plt.show() ``` - +![CostHistory](https://i.imgur.com/19Iuz4B.png) We can also plot the animation... From 63abfb0d42547177218e9a48ce9a61174ba82c8b Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Sat, 4 Aug 2018 04:33:27 +0200 Subject: [PATCH 37/38] Update documentation for v.0.3.0 (#196) Updated the documentation of the base class to include the GeneralOptimizer. Additionally, changed the chapter "Writing unit test" in the dev.optimizer.rst because we don't use the unittest module anymore. I replaced the "unittest" module with the "pytest" module and rewrote some parts of the paragraph so it reads a bit more easily. Also, added a link to the pytest docs for the running of tests. --- docs/api/_pyswarms.backend.rst | 2 +- docs/dev.optimizer.rst | 17 +++++++++++------ pyswarms/base/base_single.py | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/api/_pyswarms.backend.rst b/docs/api/_pyswarms.backend.rst index 93c01ad3..ddae95c1 100644 --- a/docs/api/_pyswarms.backend.rst +++ b/docs/api/_pyswarms.backend.rst @@ -4,7 +4,7 @@ Backend The main workhorse of PySwarms is the backend module. It contains various primitive methods and classes to help you create your own custom swarm implementation. The high-level PSO implementations in this library such -as GlobalBestPSO and LocalBestPSo were built using the backend module. +as GlobalBestPSO and LocalBestPSO were built using the backend module. .. toctree:: diff --git a/docs/dev.optimizer.rst b/docs/dev.optimizer.rst index fa8f609a..47365c59 100644 --- a/docs/dev.optimizer.rst +++ b/docs/dev.optimizer.rst @@ -94,14 +94,19 @@ This ensures that it will be automatically initialized when the whole library is Writing unit tests ------------------ -Testing is an important element of developing PySwarms, and we wanted -everything to be as smooth as possible, especially when doing the build and -integrating. In this case, we provide the :code:`tests` module in the -package. In case you add a test for your optimizer, simply name them with the -same convention as in those tests. +Testing is an important element of developing PySwarms and we want +everything to be as smooth as possible. Especially, when working on +the build and integrating new features. In this case, we provide the +:code:`tests` module in the package. For writing the test, we use the +:code:`pytest` module. In case you add a test for your optimizer, +use the same naming conventions that were used in the existing ones. You can perform separate checks by .. code-block:: shell - $ python -m unittest tests.optimizers. + $ python -m pytest tests.optimizers. + +For more details on running the tests `see here`_. + +.. _see here: https://docs.pytest.org/en/latest/usage.html diff --git a/pyswarms/base/base_single.py b/pyswarms/base/base_single.py index ade84259..ce1cbacc 100644 --- a/pyswarms/base/base_single.py +++ b/pyswarms/base/base_single.py @@ -27,6 +27,7 @@ -------- :mod:`pyswarms.single.global_best`: global-best PSO implementation :mod:`pyswarms.single.local_best`: local-best PSO implementation +:mod:`pyswarms.single.general_optimizer`: a more general PSO implementation with a custom topology """ import os From b96a08dc298057b7ea3cdfe5302cab698359492f Mon Sep 17 00:00:00 2001 From: Aaron <39431903+whzup@users.noreply.github.com> Date: Thu, 9 Aug 2018 01:55:29 +0200 Subject: [PATCH 38/38] Update features.rst and HISTORY.rst (#199) Added the GeneralOptimizer and the plotters module to the features.rst file and provided a deprecation warning in the environment package. Updated the HISTORY.rst file with the new release note format based on the notes in #180. --- HISTORY.rst | 124 +++++++++++++++++++++++----------------------- docs/features.rst | 15 +++++- 2 files changed, 75 insertions(+), 64 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 69380922..7914592f 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,32 +6,32 @@ History ------------------ * First release on PyPI. -* Includes primary optimization techniques such as global-best PSO and local-best PSO (# 1_) (# 3_). +* Includes primary optimization techniques such as global-best PSO and local-best PSO - `#1`_, `#3`_ -.. _1: https://github.com/ljvmiranda921/pyswarms/issues/1 -.. _3: https://github.com/ljvmiranda921/pyswarmsissues/3 +.. _#1: https://github.com/ljvmiranda921/pyswarms/issues/1 +.. _#3: https://github.com/ljvmiranda921/pyswarmsissues/3 0.1.1 (2017-07-25) ~~~~~~~~~~~~~~~~~~ * Patch on LocalBestPSO implementation. It seems that it's not returning the best value of the neighbors, this fixes the problem . -* **New feature:** Test functions for single-objective problems (# 6_) (# 10_) (PR# 14_). Contributed by `@Carl-K `_. Thank you! +* **New feature:** Test functions for single-objective problems - `#6`_, `#10`_, `#14`_. Contributed by `@Carl-K `_. Thank you! -.. _6: https://github.com/ljvmiranda921/pyswarms/issues/6 -.. _10: https://github.com/ljvmiranda921/pyswarms/pull/10 -.. _14: https://github.com/ljvmiranda921/pyswarms/pull/14 +.. _#6: https://github.com/ljvmiranda921/pyswarms/issues/6 +.. _#10: https://github.com/ljvmiranda921/pyswarms/pull/10 +.. _#14: https://github.com/ljvmiranda921/pyswarms/pull/14 0.1.2 (2017-08-02) ~~~~~~~~~~~~~~~~~~ -* **New feature:** Binary Particle Swarm Optimization (# 7_) (# 17_). -* Patch on Ackley function return error (# 22_). -* Improved documentation and unit tests (# 16_). +* **New feature:** Binary Particle Swarm Optimization - `#7`_, `#17`_ +* Patch on Ackley function return error - `#22`_ +* Improved documentation and unit tests - `#16`_ -.. _7: https://github.com/ljvmiranda921/pyswarms/issues/7 -.. _16: https://github.com/ljvmiranda921/pyswarms/issues/16 -.. _17: https://github.com/ljvmiranda921/pyswarms/issues/17 -.. _22: https://github.com/ljvmiranda921/pyswarms/issues/22 +.. _#7: https://github.com/ljvmiranda921/pyswarms/issues/7 +.. _#16: https://github.com/ljvmiranda921/pyswarms/issues/16 +.. _#17: https://github.com/ljvmiranda921/pyswarms/issues/17 +.. _#22: https://github.com/ljvmiranda921/pyswarms/issues/22 0.1.4 (2017-08-03) @@ -42,33 +42,33 @@ History 0.1.5 (2017-08-11) ~~~~~~~~~~~~~~~~~~ -* **New feature:** easy graphics environment. This new plotting environment makes it easier to plot the costs and swarm movement in 2-d or 3-d planes (# 30_) (PR# 31_). +* **New feature:** easy graphics environment. This new plotting environment makes it easier to plot the costs and swarm movement in 2-d or 3-d planes - `#30`_, `#31`_ -.. _30: https://github.com/ljvmiranda921/pyswarms/issues/30 -.. _31: https://github.com/ljvmiranda921/pyswarms/pull/31 +.. _#30: https://github.com/ljvmiranda921/pyswarms/issues/30 +.. _#31: https://github.com/ljvmiranda921/pyswarms/pull/31 0.1.6 (2017-09-24) ~~~~~~~~~~~~~~~~~~ -* **New feature:** Native GridSearch and RandomSearch implementations for finding the best hyperparameters in controlling swarm behaviour (# 4_) (PR# 20_) (PR# 25_). Contributed by `@SioKCronin `_. Thanks a lot! -* Added tests for hyperparameter search techniques (# 27_) (PR# 28_) (PR# 40_). Contributed by `@jazcap53 `_. Thank you so much! +* **New feature:** Native GridSearch and RandomSearch implementations for finding the best hyperparameters in controlling swarm behaviour - `#4`_, `#20`_, `#25`_. Contributed by `@SioKCronin `_. Thanks a lot! +* Added tests for hyperparameter search techniques - `#27`_, `#28`_, `#40`_. Contributed by `@jazcap53 `_. Thank you so much! * Updated structure of Base classes for higher extensibility -.. _4: https://github.com/ljvmiranda921/pyswarms/issues/4 -.. _20: https://github.com/ljvmiranda921/pyswarms/pull/20 -.. _25: https://github.com/ljvmiranda921/pyswarms/pull/25 -.. _27: https://github.com/ljvmiranda921/pyswarms/issues/27 -.. _28: https://github.com/ljvmiranda921/pyswarms/pull/28 -.. _40: https://github.com/ljvmiranda921/pyswarms/pull/40 +.. _#4: https://github.com/ljvmiranda921/pyswarms/issues/4 +.. _#20: https://github.com/ljvmiranda921/pyswarms/pull/20 +.. _#25: https://github.com/ljvmiranda921/pyswarms/pull/25 +.. _#27: https://github.com/ljvmiranda921/pyswarms/issues/27 +.. _#28: https://github.com/ljvmiranda921/pyswarms/pull/28 +.. _#40: https://github.com/ljvmiranda921/pyswarms/pull/40 0.1.7 (2017-09-25) ~~~~~~~~~~~~~~~~~~ -* Fixed patch on :code:`local_best.py` and :code:`binary.py` (# 33_) (PR# 34_). Thanks for the awesome fix, `@CPapadim `_! +* Fixed patch on :code:`local_best.py` and :code:`binary.py` - `#33`_, `#34`_. Thanks for the awesome fix, `@CPapadim `_! * Git now ignores IPython notebook checkpoints -.. _33: https://github.com/ljvmiranda921/pyswarms/issues/33 -.. _34: https://github.com/ljvmiranda921/pyswarms/pull/34 +.. _#33: https://github.com/ljvmiranda921/pyswarms/issues/33 +.. _#34: https://github.com/ljvmiranda921/pyswarms/pull/34 0.1.8 (2018-01-11) ~~~~~~~~~~~~~~~~~~ @@ -81,56 +81,56 @@ History 0.1.9 (2018-04-20) ~~~~~~~~~~~~~~~~~~ -* You can now set the initial position wherever you want (PR# 93_). -* Quick-fix for the rosenbrock function (PR# 98_). -* Tolerance can now be set to break during iteration (PR# 100_). +* You can now set the initial position wherever you want - `#93`_ +* Quick-fix for the Rosenbrock function - `#98`_ +* Tolerance can now be set to break during iteration - `#100`_ Thanks for all the wonderful Pull Requests, `@mamadyonline `_! -.. _93: https://github.com/ljvmiranda921/pyswarms/pull/93 -.. _98: https://github.com/ljvmiranda921/pyswarms/pull/98 -.. _100: https://github.com/ljvmiranda921/pyswarms/pull/100 +.. _#93: https://github.com/ljvmiranda921/pyswarms/pull/93 +.. _#98: https://github.com/ljvmiranda921/pyswarms/pull/98 +.. _#100: https://github.com/ljvmiranda921/pyswarms/pull/100 0.2.0 (2018-06-11) ------------------ -* New PySwarms backend. You can now build native swarm implementations using this module! (PR# 115_) (PR# 116_) (PR# 117_) -* Drop Python 2.7 version support. This package now supports Python 3.4 and up (PR# 114_). -* All tests were ported into pytest (PR# 113_). +* New PySwarms backend. You can now build native swarm implementations using this module! - `#115`_, `#116`_, `#117`_ +* Drop Python 2.7 version support. This package now supports Python 3.4 and up - `#113`_ +* All tests were ported into pytest - `#114`_ -.. _113: https://github.com/ljvmiranda921/pyswarms/pull/113 -.. _114: https://github.com/ljvmiranda921/pyswarms/pull/114 -.. _115: https://github.com/ljvmiranda921/pyswarms/pull/115 -.. _116: https://github.com/ljvmiranda921/pyswarms/pull/116 -.. _117: https://github.com/ljvmiranda921/pyswarms/pull/117 +.. _#113: https://github.com/ljvmiranda921/pyswarms/pull/113 +.. _#114: https://github.com/ljvmiranda921/pyswarms/pull/114 +.. _#115: https://github.com/ljvmiranda921/pyswarms/pull/115 +.. _#116: https://github.com/ljvmiranda921/pyswarms/pull/116 +.. _#117: https://github.com/ljvmiranda921/pyswarms/pull/117 0.2.1 (2018-06-27) ~~~~~~~~~~~~~~~~~~ -* Fix sigmoid function in BinaryPSO. Thanks a lot `@ThomasCES `_! -.. _145: https://github.com/ljvmiranda921/pyswarms/pull/145 +.. _#145: https://github.com/ljvmiranda921/pyswarms/pull/145 0.3.0 (2018-08-10) ------------------ -* New topologies: Pyramid, Random, and Von Neumann. More ways for your particles to interact! (PR# 176_) (PR# 177_) (PR# 155_) (PR# 142_) Thanks a lot `@whzup `_! -* New GeneralOptimizer algorithm that allows you to switch-out topologies for your optimization needs (PR# 151). Thanks a lot `@whzup `_! -* All topologies now have a static attribute. Neigbors can now be set initially or computed dynamically (PR# 164_). Thanks a lot `@whzup `_! -* New single-objective functions (PR# 168_)! Awesome work, `@jayspeidell `_! -* New tutorial on Inverse Kinematics using Particle Swarm Optimization (PR# 141_) Thanks a lot `@whzup `_! -* New plotters module for visualization. The environment module is now deprecated (PR# 135_). -* Keyword arguments can now be passed in the optimize() method for your custom objective functions (PR# 144_). Great job, `@bradahoward ` - -.. _135: https://github.com/ljvmiranda921/pyswarms/pull/135 -.. _141: https://github.com/ljvmiranda921/pyswarms/pull/141 -.. _142: https://github.com/ljvmiranda921/pyswarms/pull/142 -.. _144: https://github.com/ljvmiranda921/pyswarms/pull/144 -.. _151: https://github.com/ljvmiranda921/pyswarms/pull/151 -.. _155: https://github.com/ljvmiranda921/pyswarms/pull/155 -.. _164: https://github.com/ljvmiranda921/pyswarms/pull/164 -.. _168: https://github.com/ljvmiranda921/pyswarms/pull/168 -.. _176: https://github.com/ljvmiranda921/pyswarms/pull/176 -.. _177: https://github.com/ljvmiranda921/pyswarms/pull/177 \ No newline at end of file +* New topologies: Pyramid, Random, and Von Neumann. More ways for your particles to interact! - `#176`_, `#177`_, `#155`_, `#142`_. Thanks a lot `@whzup `_! +* New GeneralOptimizer algorithm that allows you to switch-out topologies for your optimization needs - `#151`_. Thanks a lot `@whzup `_! +* All topologies now have a static attribute. Neigbors can now be set initially or computed dynamically - `#164`_. Thanks a lot `@whzup `_! +* New single-objective functions - `#168`_. Awesome work, `@jayspeidell `_! +* New tutorial on Inverse Kinematics using Particle Swarm Optimization - `#141`_. Thanks a lot `@whzup `_! +* New plotters module for visualization. The environment module is now deprecated - `#135`_ +* Keyword arguments can now be passed in the :code:`optimize()` method for your custom objective functions - `#144`_. Great job, `@bradahoward `_ + +.. _#135: https://github.com/ljvmiranda921/pyswarms/pull/135 +.. _#141: https://github.com/ljvmiranda921/pyswarms/pull/141 +.. _#142: https://github.com/ljvmiranda921/pyswarms/pull/142 +.. _#144: https://github.com/ljvmiranda921/pyswarms/pull/144 +.. _#151: https://github.com/ljvmiranda921/pyswarms/pull/151 +.. _#155: https://github.com/ljvmiranda921/pyswarms/pull/155 +.. _#164: https://github.com/ljvmiranda921/pyswarms/pull/164 +.. _#168: https://github.com/ljvmiranda921/pyswarms/pull/168 +.. _#176: https://github.com/ljvmiranda921/pyswarms/pull/176 +.. _#177: https://github.com/ljvmiranda921/pyswarms/pull/177 diff --git a/docs/features.rst b/docs/features.rst index b3a7af53..bb09f74b 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -19,6 +19,8 @@ for optimizing various common functions. * :mod:`pyswarms.single.local_best` - classic local-best Particle Swarm Optimization algorithm with a ring-topology. Every particle compares itself only with its nearest-neighbours as computed by a distance metric. +* :mod:`pyswarms.single.general_optimizer` - alterable but still classic Particle Swarm Optimization algorithm with a custom topology. Every topology in the :mod:`pyswarms.backend` module can be passed as an argument. + Discrete ~~~~~~~~ @@ -30,7 +32,7 @@ job-scheduling, traveling salesman, or any other sequence-based problems. Utilities --------- -Test Functions +Benchmark Functions ~~~~~~~~~~~~~~ These functions can be used as benchmarks for assessing the performance of @@ -48,11 +50,20 @@ hyperparameter value combinations in reducing a specified objective function. * :mod:`pyswarms.utils.search.random_search` - search for optimal performance on selected objective function over combinations of randomly selected hyperparameter values within specified bounds for specified number of selection iterations +Plotters +~~~~~~~~ + +A quick and easy to use tool for the visualization of optimizations. It allows you to easily create animations and +to visually check your optimization! + +* :mod:`pyswarms.utils.plotters` + Environment ~~~~~~~~~~~~ +.. deprecated:: 0.4.0 + Use :mod:`pyswarms.utils.plotters` instead! Various environments that allow you to analyze your swarm performance and make visualizations! * :mod:`pyswarms.utils.environments.plot_environment` - an environment for plotting the cost history and animating particles in a 2D or 3D space. - \ No newline at end of file