# Options Handler Tutorial

Varying options with time is a well regarded technique in particle swarm optimization for faster convergence and better solutions. This class exposes methods to do the same.

In this example, we will demonstrate some common variation techniques along with some visualisation. - `oh_strategy`: a dictionary containing the strategies for each option - `end_opts`: a dictionary containing the ending options for each option - `plot_cost_history`: for plotting the cost history of a swarm given a matrix - `plot_contour`: for plotting swarm trajectories of a 2D-swarm in two-dimensional space

    [1]:

    # Import modules
    import matplotlib.pyplot as plt
    import numpy as np
    from IPython.display import Image

    # Import PySwarms
    import pyswarms as ps
    from pyswarms.utils.functions import single_obj as fx
    from pyswarms.utils.plotters import (plot_cost_history, plot_contour)

    from pyswarms.backend.handlers import OptionsHandler

Let’s create some optimizers for comparison. Here, we’re going to use Global-best PSO to find the minima of a sphere function. As usual, we simply create an instance of its class `pyswarms.single.GlobalBestPSO` by passing the required parameters that we will use. Then, we’ll call the `optimize()` method for 100 iterations for both and visualise the results

    [2]:

    options = {'c1':0.5, 'c2':0.3, 'w':0.9}  # starting options
    optimizer_without_handle=ps.single.GlobalBestPSO(n_particles=50, dimensions=2, options=options)
    optimizer_with_handle = ps.single.GlobalBestPSO(n_particles=50, dimensions=2, options=options, oh_strategy={"w":'exp_decay', 'c1':'lin_variation'})

    cost, pos = optimizer_without_handle.optimize(fx.sphere, iters=100)
    cost_h, pos_h = optimizer_with_handle.optimize(fx.sphere, iters=100)

    2020-12-11 19:23:18,434 - pyswarms.single.global_best - INFO - Optimize for 100 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}
    pyswarms.single.global_best: 100%|██████████|100/100, best_cost=7.95e-10
    2020-12-11 19:23:18,540 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 7.946552028624106e-10, best pos: [ 2.74938126e-05 -6.22458616e-06]
    2020-12-11 19:23:18,541 - pyswarms.single.global_best - INFO - Optimize for 100 iters with {'c1': 0.5, 'c2': 0.3, 'w': 0.9}
    pyswarms.single.global_best: 100%|██████████|100/100, best_cost=7.99e-28
    2020-12-11 19:23:18,648 - pyswarms.single.global_best - INFO - Optimization finished | best cost: 7.987498732542365e-28, best pos: [ 7.14102116e-15 -2.73451219e-14]

    [ ]:

## Comparing the cost history

To plot the cost history, we simply obtain the `cost_history` from the `optimizer` class and pass it to the `cost_history` function. In addition, we can obtain the following histories from the same class: - mean_neighbor_history: average local best history of all neighbors throughout optimization - mean_pbest_history: average personal best of the particles throughout optimization

    [3]:

    fig, (ax, ax_h) = plt.subplots(ncols=2, nrows=1)
    fig.set_size_inches(15,7)
    plot_cost_history(ax=ax, cost_history=optimizer_without_handle.cost_history)
    plot_cost_history(ax=ax_h, cost_history=optimizer_with_handle.cost_history)
    ax.set_title("Cost history without inertia decay")
    ax_h.set_title("Cost history with exponential intertia decay")
    plt.show()

The rapid decay of the inertia weight contributes significantly to the overall best cost and position. It also converges a lot faster.

The next part shows the explanation for this with an animation

## Comparing animations

The `plotters` module offers two methods to perform animation, `plot_contour()`. This method plot the particles in a 2-D space.

The objective function contours are added using the `Mesher` class.

    [4]:

    from pyswarms.utils.plotters.formatters import Mesher
    # Initialize mesher with sphere function
    m = Mesher(func=fx.sphere)

### Plotting in 2-D space

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”.

    [5]:

    %%capture

    # Make and save animation
    animation = plot_contour(pos_history=optimizer_without_handle.pos_history,
                             mesher=m,
                             mark=(0,0))
    animation_h = plot_contour(pos_history=optimizer_with_handle.pos_history,
                             mesher=m,
                             mark=(0,0))
    # Enables us to view it in a Jupyter notebook
    animation.save('ani.gif', writer='imagemagick', fps=10)

    animation_h.save('ani_h.gif', writer='imagemagick', fps=10)

    2020-12-11 19:23:22,596 - matplotlib.animation - WARNING - MovieWriter imagemagick unavailable; using Pillow instead.
    2020-12-11 19:23:22,597 - matplotlib.animation - INFO - Animation.save using <class 'matplotlib.animation.PillowWriter'>
    2020-12-11 19:23:30,971 - matplotlib.animation - WARNING - MovieWriter imagemagick unavailable; using Pillow instead.
    2020-12-11 19:23:30,973 - matplotlib.animation - INFO - Animation.save using <class 'matplotlib.animation.PillowWriter'>

Compare the two animations. Observe the convergence time and particle overshoot in both figures.

Left: Without handle

Right: With handle

    [ ]:

## Customizing ending options

As of the current version(1.2.0), you’ll need to create your own optimization loop to keep custom ending options. The next block shows a basic implementation of this without logging etc.

    [6]:

    from pyswarms.backend.operators import compute_pbest, compute_objective_function

    [10]:

    def optimize(objective_func, maxiters, oh_strategy,start_opts, end_opts):
        opt = ps.single.GlobalBestPSO(n_particles=50, dimensions=2, options=start_opts, oh_strategy=oh_strategy)

        swarm = opt.swarm
        opt.bh.memory = swarm.position
        opt.vh.memory = swarm.position
        swarm.pbest_cost = np.full(opt.swarm_size[0], np.
        inf)

        for i in range(maxiters):
            # Compute cost for current position and personal best
            swarm.current_cost =  compute_objective_function(swarm, objective_func)
            swarm.pbest_pos, swarm.pbest_cost = compute_pbest(swarm)

            # Set best_cost_yet_found for ftol
            best_cost_yet_found = swarm.best_cost
            swarm.best_pos, swarm.best_cost = opt.top.compute_gbest(swarm)
            # Perform options update
            swarm.options = opt.oh( opt.options, iternow=i, itermax=maxiters, end_opts=end_opts )
            print("Iteration:", i," Options: ", swarm.options)    # print to see variation
            # Perform velocity and position updates
            swarm.velocity = opt.top.compute_velocity(
                swarm, opt.velocity_clamp, opt.vh, opt.bounds
            )
            swarm.position = opt.top.compute_position(
                swarm, opt.bounds, opt.bh
            )
        # Obtain the final best_cost and the final best_position
        final_best_cost = swarm.best_cost.copy()
        final_best_pos = swarm.pbest_pos[
            swarm.pbest_cost.argmin()
        ].copy()
        return final_best_cost, final_best_pos

In the next cell, you can play around with the start and end options, maximum iterations and the function itself to compare the outputs like above.

    [12]:

    function = fx.rosenbrock    # optimum at [1,1]
    maxiters = 100
    start_opts = {'c1':2.5, 'c2':0.5, 'w':0.9}
    end_opts= {'c1':0.5, 'c2':2.5, 'w':0.4}     # Ref:[1]
    oh_strategy={ "w":'exp_decay', "c1":'nonlin_mod',"c2":'lin_variation'}

    cost, pos=optimize(function, maxiters, oh_strategy, start_opts, end_opts)

    print("Best cost = ", cost)
    print("Best position = ", pos)

    Iteration: 0  Options:  {'c1': 2.5, 'c2': 0.5, 'w': 0.8154845485377135}
    Iteration: 1  Options:  {'c1': 2.476024064289623, 'c2': 0.52, 'w': 0.7638427274927154}
    Iteration: 2  Options:  {'c1': 2.4520965166602724, 'c2': 0.54, 'w': 0.7212425273766464}
    Iteration: 3  Options:  {'c1': 2.428217751727513, 'c2': 0.56, 'w': 0.6855550181382926}
    Iteration: 4  Options:  {'c1': 2.4043881714225273, 'c2': 0.5800000000000001, 'w': 0.6552602432446853}
    Iteration: 5  Options:  {'c1': 2.3806081852052783, 'c2': 0.6000000000000001, 'w': 0.6292465903435571}
    Iteration: 6  Options:  {'c1': 2.3568782102861965, 'c2': 0.6200000000000001, 'w': 0.6066838570323415}
    Iteration: 7  Options:  {'c1': 2.3331986718568167, 'c2': 0.6399999999999999, 'w': 0.5869404779457066}
    Iteration: 8  Options:  {'c1': 2.3095700033298305, 'c2': 0.6599999999999999, 'w': 0.5695280957195421}
    Iteration: 9  Options:  {'c1': 2.285992646589043, 'c2': 0.6799999999999999, 'w': 0.5540635587607113}
    Iteration: 10  Options:  {'c1': 2.2624670522497583, 'c2': 0.7, 'w': 0.5402423141269194}
    Iteration: 11  Options:  {'c1': 2.2389936799301475, 'c2': 0.72, 'w': 0.527819424452714}
    Iteration: 12  Options:  {'c1': 2.215572998534194, 'c2': 0.74, 'w': 0.5165957922398924}
    Iteration: 13  Options:  {'c1': 2.192205486546836, 'c2': 0.76, 'w': 0.5064080078932764}
    Iteration: 14  Options:  {'c1': 2.1688916323420004, 'c2': 0.78, 'w': 0.4971207626134709}
    Iteration: 15  Options:  {'c1': 2.1456319345042183, 'c2': 0.8, 'w': 0.4886211049862233}
    Iteration: 16  Options:  {'c1': 2.1224269021646167, 'c2': 0.8200000000000001, 'w': 0.48081404179727616}
    Iteration: 17  Options:  {'c1': 2.0992770553520845, 'c2': 0.8400000000000001, 'w': 0.4736191317674961}
    Iteration: 18  Options:  {'c1': 2.076182925360504, 'c2': 0.8600000000000001, 'w': 0.46696782158333405}
    Iteration: 19  Options:  {'c1': 2.053145055132976, 'c2': 0.8799999999999999, 'w': 0.4608013430638035}
    Iteration: 20  Options:  {'c1': 2.030163999664059, 'c2': 0.8999999999999999, 'w': 0.455069038916464}
    Iteration: 21  Options:  {'c1': 2.007240326421086, 'c2': 0.9199999999999999, 'w': 0.44972701900111317}
    Iteration: 22  Options:  {'c1': 1.984374615785726, 'c2': 0.94, 'w': 0.4447370737567352}
    Iteration: 23  Options:  {'c1': 1.961567461517037, 'c2': 0.96, 'w': 0.4400657894042114}
    Iteration: 24  Options:  {'c1': 1.938819471237338, 'c2': 0.98, 'w': 0.4356838227118487}
    Iteration: 25  Options:  {'c1': 1.916131266942353, 'c2': 1.0, 'w': 0.43156530287330336}
    Iteration: 26  Options:  {'c1': 1.893503485537166, 'c2': 1.02, 'w': 0.4276873353495389}
    Iteration: 27  Options:  {'c1': 1.87093677939967, 'c2': 1.04, 'w': 0.4240295880363697}
    Iteration: 28  Options:  {'c1': 1.8484318169733072, 'c2': 1.06, 'w': 0.4205739443113132}
    Iteration: 29  Options:  {'c1': 1.8259892833910558, 'c2': 1.08, 'w': 0.4173042107280646}
    Iteration: 30  Options:  {'c1': 1.8036098811327728, 'c2': 1.1, 'w': 0.41420586961014694}
    Iteration: 31  Options:  {'c1': 1.7812943307181757, 'c2': 1.12, 'w': 0.41126586872702486}
    Iteration: 32  Options:  {'c1': 1.759043371437939, 'c2': 1.14, 'w': 0.4084724417486748}
    Iteration: 33  Options:  {'c1': 1.7368577621255956, 'c2': 1.16, 'w': 0.40581495436664605}
    Iteration: 34  Options:  {'c1': 1.7147382819731596, 'c2': 1.18, 'w': 0.4032837719146377}
    Iteration: 35  Options:  {'c1': 1.6926857313936474, 'c2': 1.2, 'w': 0.4008701450750272}
    Iteration: 36  Options:  {'c1': 1.6707009329339555, 'c2': 1.22, 'w': 0.39856611086172655}
    Iteration: 37  Options:  {'c1': 1.6487847322418678, 'c2': 1.24, 'w': 0.39636440655637023}
    Iteration: 38  Options:  {'c1': 1.626937999091311, 'c2': 1.26, 'w': 0.3942583946688905}
    Iteration: 39  Options:  {'c1': 1.6051616284703591, 'c2': 1.28, 'w': 0.3922419973141236}
    Iteration: 40  Options:  {'c1': 1.583456541736921, 'c2': 1.3, 'w': 0.3903096386581006}
    Iteration: 41  Options:  {'c1': 1.5618236878475174, 'c2': 1.32, 'w': 0.3884561943027294}
    Iteration: 42  Options:  {'c1': 1.5402640446650828, 'c2': 1.34, 'w': 0.3866769466548326}
    Iteration: 43  Options:  {'c1': 1.518778620352329, 'c2': 1.36, 'w': 0.3849675454721789}
    Iteration: 44  Options:  {'c1': 1.497368454857856, 'c2': 1.38, 'w': 0.3833239729009879}
    Iteration: 45  Options:  {'c1': 1.4760346215029587, 'c2': 1.4, 'w': 0.38174251242096996}
    Iteration: 46  Options:  {'c1': 1.454778228677894, 'c2': 1.42, 'w': 0.3802197211989471}
    Iteration: 47  Options:  {'c1': 1.4336004216573355, 'c2': 1.44, 'w': 0.3787524054234573}
    Iteration: 48  Options:  {'c1': 1.4125023845457787, 'c2': 1.46, 'w': 0.37733759825283403}
    Iteration: 49  Options:  {'c1': 1.39148534236489, 'c2': 1.48, 'w': 0.3759725400600326}
    Iteration: 50  Options:  {'c1': 1.3705505632961241, 'c2': 1.5, 'w': 0.3746546607005046}
    Iteration: 51  Options:  {'c1': 1.349699361093501, 'c2': 1.52, 'w': 0.3733815635660021}
    Iteration: 52  Options:  {'c1': 1.3289330976831786, 'c2': 1.54, 'w': 0.3721510112183664}
    Iteration: 53  Options:  {'c1': 1.3082531859684736, 'c2': 1.56, 'w': 0.3709609124240106}
    Iteration: 54  Options:  {'c1': 1.2876610928612768, 'c2': 1.58, 'w': 0.3698093104326385}
    Iteration: 55  Options:  {'c1': 1.2671583425634432, 'c2': 1.6, 'w': 0.36869437236336694}
    Iteration: 56  Options:  {'c1': 1.2467465201247816, 'c2': 1.62, 'w': 0.3676143795783189}
    Iteration: 57  Options:  {'c1': 1.226427275307758, 'c2': 1.6400000000000001, 'w': 0.36656771893834633}
    Iteration: 58  Options:  {'c1': 1.2062023267930964, 'c2': 1.6600000000000001, 'w': 0.36555287484817117}
    Iteration: 59  Options:  {'c1': 1.1860734667651598, 'c2': 1.6800000000000002, 'w': 0.3645684220091819}
    Iteration: 60  Options:  {'c1': 1.1660425659214986, 'c2': 1.7, 'w': 0.3636130188076487}
    Iteration: 61  Options:  {'c1': 1.146111578957366, 'c2': 1.72, 'w': 0.36268540127440874}
    Iteration: 62  Options:  {'c1': 1.126282550583548, 'c2': 1.74, 'w': 0.36178437755931375}
    Iteration: 63  Options:  {'c1': 1.1065576221447462, 'c2': 1.76, 'w': 0.3609088228700637}
    Iteration: 64  Options:  {'c1': 1.0869390389162645, 'c2': 1.78, 'w': 0.360057674830598}
    Iteration: 65  Options:  {'c1': 1.0674291581692645, 'c2': 1.8, 'w': 0.35922992921908464}
    Iteration: 66  Options:  {'c1': 1.0480304581097744, 'c2': 1.8199999999999998, 'w': 0.35842463604983676}
    Iteration: 67  Options:  {'c1': 1.0287455478145502, 'c2': 1.8399999999999999, 'w': 0.3576408959672543}
    Iteration: 68  Options:  {'c1': 1.0095771783084766, 'c2': 1.8599999999999999, 'w': 0.3568778569232281}
    Iteration: 69  Options:  {'c1': 0.9905282549543739, 'c2': 1.88, 'w': 0.3561347111123839}
    Iteration: 70  Options:  {'c1': 0.9716018513579736, 'c2': 1.9, 'w': 0.35541069214215554}
    Iteration: 71  Options:  {'c1': 0.9528012250299461, 'c2': 1.92, 'w': 0.35470507241699134}
    Iteration: 72  Options:  {'c1': 0.9341298350951458, 'c2': 1.94, 'w': 0.35401716071805106}
    Iteration: 73  Options:  {'c1': 0.9155913623992082, 'c2': 1.96, 'w': 0.3533462999615839}
    Iteration: 74  Options:  {'c1': 0.8971897324376921, 'c2': 1.98, 'w': 0.35269186512080497}
    Iteration: 75  Options:  {'c1': 0.8789291416275995, 'c2': 2.0, 'w': 0.35205326129754305}
    Iteration: 76  Options:  {'c1': 0.8608140875614461, 'c2': 2.02, 'w': 0.35142992193123407}
    Iteration: 77  Options:  {'c1': 0.8428494040384126, 'c2': 2.04, 'w': 0.350821307133995}
    Iteration: 78  Options:  {'c1': 0.8250403018670246, 'c2': 2.06, 'w': 0.3502269021415575}
    Iteration: 79  Options:  {'c1': 0.8073924166953819, 'c2': 2.08, 'w': 0.3496462158707742}
    Iteration: 80  Options:  {'c1': 0.7899118654710782, 'c2': 2.1, 'w': 0.3490787795752519}
    Iteration: 81  Options:  {'c1': 0.7726053135965205, 'c2': 2.12, 'w': 0.3485241455914203}
    Iteration: 82  Options:  {'c1': 0.7554800554745198, 'c2': 2.14, 'w': 0.3479818861680258}
    Iteration: 83  Options:  {'c1': 0.7385441120054486, 'c2': 2.16, 'w': 0.34745159237265394}
    Iteration: 84  Options:  {'c1': 0.7218063498096469, 'c2': 2.18, 'w': 0.3469328730694381}
    Iteration: 85  Options:  {'c1': 0.7052766286755895, 'c2': 2.2, 'w': 0.3464253539626105}
    Iteration: 86  Options:  {'c1': 0.6889659862428663, 'c2': 2.2199999999999998, 'w': 0.34592867670100746}
    Iteration: 87  Options:  {'c1': 0.6728868726545348, 'c2': 2.24, 'w': 0.34544249803904953}
    Iteration: 88  Options:  {'c1': 0.657053453585897, 'c2': 2.26, 'w': 0.34496648905008775}
    Iteration: 89  Options:  {'c1': 0.6414820089421402, 'c2': 2.28, 'w': 0.3445003343883481}
    Iteration: 90  Options:  {'c1': 0.6261914688960386, 'c2': 2.3, 'w': 0.3440437315960089}
    Iteration: 91  Options:  {'c1': 0.6112041531021342, 'c2': 2.32, 'w': 0.34359639045222895}
    Iteration: 92  Options:  {'c1': 0.5965468213847226, 'c2': 2.34, 'w': 0.34315803236119463}
    Iteration: 93  Options:  {'c1': 0.5822522228837674, 'c2': 2.36, 'w': 0.34272838977648734}
    Iteration: 94  Options:  {'c1': 0.5683614862434021, 'c2': 2.38, 'w': 0.3423072056592845}
    Iteration: 95  Options:  {'c1': 0.5549280271653059, 'c2': 2.4, 'w': 0.3418942329680971}
    Iteration: 96  Options:  {'c1': 0.5420244448704603, 'c2': 2.42, 'w': 0.3414892341779263}
    Iteration: 97  Options:  {'c1': 0.529756065178477, 'c2': 2.44, 'w': 0.34109198082688047}
    Iteration: 98  Options:  {'c1': 0.518292202077093, 'c2': 2.46, 'w': 0.34070225308844143}
    Iteration: 99  Options:  {'c1': 0.5079621434110699, 'c2': 2.48, 'w': 0.3403198393677058}
    Best cost =  3.4261311762230905e-13
    Best position =  [0.99999942 0.99999884]

Changing options clearly shows better convergence. Just 100 iterations on the rosenbrock function are enough to bring the cost to very low orders of e-10 which is comparable to the ones in academia. [2]

References:

[1] Chih, Mingchang, et al. “Particle swarm optimization with time-varying acceleration coefficients for the multidimensional knapsack problem.” Applied Mathematical Modelling 38.4 (2014): 1338-1350.

[2] Satyanarayana Daggubati, “Comparison of particle swarm optimization variants”