Search code examples
pythonoptimizationparticle-swarm

pyswarms toy example - help to understand simple things


trying to understand Particle Swarm Optimization using Python pyswarms package.

https://pyswarms.readthedocs.io/en/latest/intro.html

Need to optimize a function of multiple variables, given as:

# Define the objective function
def objective_function(x):
    return ((x[0] - 1)**2 + (x[1]-2)**2 + (x[2] - 3)**2)

I want to find a global minimum in a bounded region if I have an initial guess near that global min.

Here is what I have done, but it's actually not working. It gives the error:

ValueError: operands could not be broadcast together with shapes (3,) (100,)

Something wrong with the initial guess or bounds? I need to reshape it in some specific way? Please give me a key to resolve that issue..

Can anyone look and explain - what is the problem? Here is the code to try.

import numpy as np
import pyswarms as ps

# Define the objective function
def objective_function(x):
    return  ((x[0] - 1)**2 + (x[1]-2)**2 + (x[2] - 3)**2)

# Define the bounds for each element of x
bounds = ([-5]*3, [5]*3)
print('Bounds:')
print(bounds)

# Define the initial guesses for each element of x
initial_guess_1 = np.array([1.0, 2.0, 2.9])

# Define the number of elements to optimize
dimensions = initial_guess_1.size
print('Dimensions:', dimensions)

# defining the number of particles to use:
n_particles = 100

print('Objective function for initial guess:')
print(objective_function(initial_guess_1))

# reshaping to get all the particles initial guess positions? 
# I don't know if it's necessary to do this?

initial_guess = initial_guess_1.reshape((1, dimensions))
init_pos = np.tile(initial_guess, (n_particles, 1))

print('Initial guess of one particle:')
print(initial_guess_1)

print('Initial positions for all particles: ')
print(init_pos.shape)
print(init_pos)


# Define the options for the optimizer

options = {
    'c1': 0.5,  # cognitive parameter
    'c2': 0.3,  # social parameter
    'w': 0.9   # inertia weight
}

# Create a PSO optimizer
optimizer = ps.single.GlobalBestPSO(n_particles=n_particles, 
                                    dimensions=dimensions, 
                                    options=options, 
                                    bounds=bounds,
                                    init_pos=init_pos
                                    )

# Initialize the particles with the initial guesses
#optimizer.pos = init_pos

# Run the optimization
best_cost, best_position= optimizer.optimize(objective_function, iters=1000, verbose=True)

# Print the results
print("Best position:", best_position)
print("Best cost:", best_cost)

print('Func value at best pos', objective_function(best_cost))

Solution

  • So your problem lies on the fact that you did not consider how the module works. When you define your number of swarms and the dimension of your target, the optimizer will create an array of size (nb_swarms x dimensions) to pass onto your objective function. You can check the shape and what is actually passed to your objective function whith for example the following lines :

    def objective_function(x):
        print(x.shape)
        print(f"x_0 : {x[0]}")
        print(f"x_1 : {x[1]}")
        print(f"x_2 : {x[2]}")
        return  ((x[0] - 1)**2 + (x[1]-2)**2 + (x[2] - 3)**2)
    

    Running this you will see that your x[0], x[1] and x[2] are actually the rows of your input array, not the columns.

    A simple way to fix this is to add something like that in your objective function :

    def objective_function(x):
        x = x.T
        return  ((x[0] - 1)**2 + (x[1]-2)**2 + (x[2] - 3)**2)
    

    This works. But a nicer way would be to test the shape of the input array so it is more robust and nice to read.

    For example :

    def objective_function(x):
        if x.shape[0] != dimensions : 
            x = x.T
        return  ((x[0] - 1)**2 + (x[1]-2)**2 + (x[2] - 3)**2)
    

    I hope this helps you, I bumped into this before !

    P.S. : you should also correct the last line of your code, running the objective function with the argument "best_position" and not "best_cost" :

    print('Func value at best pos', objective_function(best_position))