Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the function callback feature at each iteration. #493

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 111 additions & 25 deletions docs/examples/tutorials/basic_optimization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,17 @@
"name": "stderr",
"output_type": "stream",
"text": [
"2019-05-18 15:39:13,096 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=1.09e-41\n",
"2019-05-18 15:39:25,448 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 1.093473857947962e-41, best pos: [3.27682830e-21 4.43998725e-22]\n"
"2022-06-15 20:30:29,083 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=2.11e-44\n",
"2022-06-15 20:30:30,741 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 2.113549046343125e-44, best pos: [ 4.27475005e-24 -1.45317642e-22]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 3.02 s, sys: 774 ms, total: 3.79 s\n",
"Wall time: 12.4 s\n"
"CPU times: user 1.58 s, sys: 690 ms, total: 2.27 s\n",
"Wall time: 1.67 s\n"
]
}
],
Expand Down Expand Up @@ -106,17 +106,17 @@
"name": "stderr",
"output_type": "stream",
"text": [
"2019-05-18 15:39:25,476 - pyswarms.single.local_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2}\n",
"pyswarms.single.local_best: 100%|██████████|1000/1000, best_cost=3.28e-41\n",
"2019-05-18 15:39:37,110 - pyswarms.single.local_best - INFO - Optimization finished | best cost: 3.275639739592901e-41, best pos: [-5.62944989e-21 -1.40094066e-21]\n"
"2022-06-15 20:30:32,269 - pyswarms.single.local_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'k': 2, 'p': 2}\n",
"pyswarms.single.local_best: 100%|██████████|1000/1000, best_cost=7.47e-41\n",
"2022-06-15 20:30:34,533 - pyswarms.single.local_best - INFO - Optimization finished | best cost: 7.468717233493547e-41, best pos: [ 2.48289631e-21 -8.27782569e-21]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 1.93 s, sys: 271 ms, total: 2.2 s\n",
"Wall time: 11.6 s\n"
"CPU times: user 2.36 s, sys: 741 ms, total: 3.1 s\n",
"Wall time: 2.28 s\n"
]
}
],
Expand Down Expand Up @@ -179,17 +179,17 @@
"name": "stderr",
"output_type": "stream",
"text": [
"2019-05-18 15:39:37,279 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"2022-06-15 20:30:38,419 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=0 \n",
"2019-05-18 15:39:48,976 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 0.0, best pos: [-1.11729550e-09 3.10827139e-09]\n"
"2022-06-15 20:30:40,267 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 0.0, best pos: [-2.90016642e-09 -9.57646361e-10]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 1.95 s, sys: 254 ms, total: 2.21 s\n",
"Wall time: 11.7 s\n"
"CPU times: user 1.96 s, sys: 628 ms, total: 2.59 s\n",
"Wall time: 1.89 s\n"
]
}
],
Expand Down Expand Up @@ -257,9 +257,9 @@
"name": "stderr",
"output_type": "stream",
"text": [
"2019-05-18 15:39:49,204 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=7.02e-10\n",
"2019-05-18 15:40:01,463 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 7.019703679797182e-10, best pos: [1.0000264 1.00005302]\n"
"2022-06-15 20:30:43,771 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=1.67e-22\n",
"2022-06-15 20:30:46,380 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 1.6717623626876796e-22, best pos: [1. 1.]\n"
]
}
],
Expand Down Expand Up @@ -294,9 +294,9 @@
"name": "stderr",
"output_type": "stream",
"text": [
"2019-05-18 15:40:01,475 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=0 \n",
"2019-05-18 15:40:13,805 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 0.0, best pos: [1. 1.]\n"
"2022-06-15 20:30:49,187 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=7.89e-31\n",
"2022-06-15 20:30:51,293 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 7.888609052210118e-31, best pos: [1. 1.]\n"
]
}
],
Expand All @@ -321,21 +321,107 @@
"name": "stderr",
"output_type": "stream",
"text": [
"2019-05-18 15:40:13,819 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=0\n",
"2019-05-18 15:40:25,963 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 0.0, best pos: [1. 1.]\n"
"2022-06-15 20:30:57,250 - pyswarms.single.global_best - INFO - Optimize for 1000 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|1000/1000, best_cost=7.89e-31\n",
"2022-06-15 20:30:59,256 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 7.888609052210118e-31, best pos: [1. 1.]\n"
]
}
],
"source": [
"cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, a=1, b=100)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Modifying reporter at iterations"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"def reporter_callback(swarm, reporter):\n",
" \"A custom callback function\"\n",
" reporter.hook(swarm=swarm) #add the default behaviour (postfix to tqdm)\n",
" print('Best position:', swarm.best_pos)\n",
" return\n",
"\n",
"# Create bounds\n",
"max_bound = 5.12 * np.ones(2)\n",
"min_bound = - max_bound\n",
"bounds = (min_bound, max_bound)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2022-06-15 20:32:31,697 - pyswarms.single.global_best - INFO - Optimize for 10 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}\n",
"pyswarms.single.global_best: 100%|██████████|10/10, best_cost=0.424\n",
"2022-06-15 20:32:31,717 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 0.4237618526034055, best pos: [-0.03887785 0.02516484]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Best position: [1.8470209 3.1831133]\n",
"Best position: [0.9647154 0.67466656]\n",
"Best position: [-0.80333013 0.03822154]\n",
"Best position: [-0.80333013 0.03822154]\n",
"Best position: [0.89995086 0.89942345]\n",
"Best position: [0.89995086 0.89942345]\n",
"Best position: [0.89995086 0.89942345]\n",
"Best position: [0.89995086 0.89942345]\n",
"Best position: [-0.03887785 0.02516484]\n",
"Best position: [-0.03887785 0.02516484]\n",
"CPU times: user 24.1 ms, sys: 12 ms, total: 36.2 ms\n",
"Wall time: 26 ms\n"
]
}
],
"source": [
"%%time\n",
"# Initialize swarm\n",
"options = {'c1': 0.5, 'c2': 0.3, 'w':0.9}\n",
"\n",
"# Call instance of PSO with bounds argument\n",
"optimizer = ps.single.GlobalBestPSO(\n",
" n_particles=10, \n",
" dimensions=2, \n",
" options=options, \n",
" bounds=bounds,\n",
" reporter_callback=reporter_callback,\n",
")\n",
"\n",
"# Perform optimization\n",
"cost, pos = optimizer.optimize(fx.rastrigin, iters=10)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"anaconda-cloud": {},
"interpreter": {
"hash": "f513a0bf0d27faed0401583855d8d7225d1dab65dd440ea970d3f25fb025c381"
},
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3.9.7 ('base')",
"language": "python",
"name": "python3"
},
Expand All @@ -349,7 +435,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.8"
"version": "3.9.7"
}
},
"nbformat": 4,
Expand Down
26 changes: 13 additions & 13 deletions pyswarms/backend/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ def __init__(self, strategy):

* exp_decay:
Decreases the parameter exponentially between limits.

* lin_variation:
Decreases/increases the parameter linearly between limits.

Expand Down Expand Up @@ -584,14 +584,14 @@ def __init__(self, strategy):
# more updates using new_options

.. note::
As of pyswarms v1.3.0, you will need to create your own optimization loop to change the default ending
As of pyswarms v1.3.0, you will need to create your own optimization loop to change the default ending
options and other arguments for each strategy in all of the handlers on this page.

A more comprehensive tutorial is also present `here`_ for interested users.

.. _here: https://pyswarms.readthedocs.io/en/latest/examples/tutorials/options_handler.html



Attributes
----------
Expand Down Expand Up @@ -625,12 +625,12 @@ def __call__(self, start_opts, **kwargs):
def exp_decay(self, start_opts, opt, **kwargs):
"""Exponentially decreasing between :math:`w_{start}` and :math:`w_{end}`
The velocity is adjusted such that the following equation holds:

Defaults: :math:`d_{1}=2, d_{2}=7, w^{end} = 0.4, c^{end}_{1} = 0.8 * c^{start}_{1}, c^{end}_{2} = c^{start}_{2}`

.. math::
w = (w^{start}-w^{end}-d_{1})exp(\\frac{1}{1+ \\frac{d_{2}.iter}{iter^{max}}})

Ref: Li, H.-R., & Gao, Y.-L. (2009). Particle Swarm Optimization Algorithm with Exponent
Decreasing Inertia Weight and Stochastic Mutation. 2009 Second International Conference
on Information and Computing Science. doi:10.1109/icic.2009.24
Expand Down Expand Up @@ -670,14 +670,14 @@ def exp_decay(self, start_opts, opt, **kwargs):
def lin_variation(self, start_opts, opt, **kwargs):
"""
Linearly decreasing/increasing between :math:`w_{start}` and :math:`w_{end}`

Defaults: :math:`w^{end} = 0.4, c^{end}_{1} = 0.8 * c^{start}_{1}, c^{end}_{2} = c^{start}_{2}`

.. math::
w = w^{end}+(w^{start}-w^{end}) \\frac{iter^{max}-iter}{iter^{max}}

Ref: Xin, Jianbin, Guimin Chen, and Yubao Hai. "A particle swarm optimizer with
multi-stage linearly-decreasing inertia weight." 2009 International joint conference
Ref: Xin, Jianbin, Guimin Chen, and Yubao Hai. "A particle swarm optimizer with
multi-stage linearly-decreasing inertia weight." 2009 International joint conference
on computational sciences and optimization. Vol. 1. IEEE, 2009.
"""

Expand Down Expand Up @@ -709,11 +709,11 @@ def random(self, start_opts, opt, **kwargs):

.. math::
w = start + (end-start)*rand(0,1)

Ref: R.C. Eberhart, Y.H. Shi, Tracking and optimizing dynamic systems with particle
swarms, in: Congress on Evolutionary Computation, Korea, 2001
"""

start = start_opts[opt]
if opt in kwargs["end_opts"]:
end = kwargs["end_opts"][opt]
Expand Down
12 changes: 11 additions & 1 deletion pyswarms/discrete/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def __init__(
vh_strategy="unmodified",
ftol=-np.inf,
ftol_iter=1,
reporter_callback=None,
):
"""Initialize the swarm

Expand Down Expand Up @@ -120,9 +121,18 @@ def __init__(
number of iterations over which the relative error in
objective_func(best_pos) is acceptable for convergence.
Default is :code:`1`
reporter_callback : callable, optional
callback function to call at end of each iteration. The signature should
be:
:code:`reporter_callback(swarm, reporter)`
:code:`None` will print the best cost as postfix in tqdm.
"""
# Initialize logger
self.rep = Reporter(logger=logging.getLogger(__name__))
if reporter_callback is None:
self.reporter_callback = self.rep.hook
else:
self.reporter_callback = reporter_callback
# Assign k-neighbors and p-value as attributes
self.k, self.p = options["k"], options["p"]
# Initialize parent class
Expand Down Expand Up @@ -205,7 +215,7 @@ def optimize(
)
if verbose:
# Print to console
self.rep.hook(best_cost=self.swarm.best_cost)
self.reporter_callback(swarm=self.swarm, reporter=self.rep)
# Save to history
hist = self.ToHistory(
best_cost=self.swarm.best_cost,
Expand Down
12 changes: 11 additions & 1 deletion pyswarms/single/general_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __init__(
ftol=-np.inf,
ftol_iter=1,
init_pos=None,
reporter_callback=None,
):
"""Initialize the swarm

Expand Down Expand Up @@ -166,6 +167,11 @@ def __init__(
init_pos : numpy.ndarray, optional
option to explicitly set the particles' initial positions. Set to
:code:`None` if you wish to generate the particles randomly.
reporter_callback : callable, optional
callback function to call at end of each iteration. The signature should
be:
:code:`reporter_callback(swarm, reporter)`
:code:`None` will print the best cost as postfix in tqdm.
"""
super(GeneralOptimizerPSO, self).__init__(
n_particles,
Expand All @@ -182,6 +188,10 @@ def __init__(
oh_strategy = {}
# Initialize logger
self.rep = Reporter(logger=logging.getLogger(__name__))
if reporter_callback is None:
self.reporter_callback = self.rep.hook
else:
self.reporter_callback = reporter_callback
# Initialize the resettable attributes
self.reset()
# Initialize the topology and check for type
Expand Down Expand Up @@ -254,7 +264,7 @@ def optimize(
)
# Print to console
if verbose:
self.rep.hook(best_cost=self.swarm.best_cost)
self.reporter_callback(swarm=self.swarm, reporter=self.rep)
hist = self.ToHistory(
best_cost=self.swarm.best_cost,
mean_pbest_cost=np.mean(self.swarm.pbest_cost),
Expand Down
Loading