HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Particle Swarm Optimization

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
swarmoptimizationparticle

Problem

I wrote my first python code to conduct PSO. I am wondering about the best practices for Python. While my code works, I want to make sure it's orthodox as well. For example, here is my class I use followed by a function that initializes the list of Particles that I use for the algorithm:

```
########### data representation
pList = []
class Particle:
#value, x_pos, y_pos
gBest = [0.0, 0, 0]
bestIndex = 0

#takes index in pList as constructor argument
def __init__(self, i):
#x,y coords, randomly initialized
self.x = randint(-worldWidth/2,worldWidth/2)
self.y = randint(-worldHeight/2,worldHeight/2)
#x,y velocity
self.velocity_x = 0.0
self.velocity_y = 0.0
#personal best
#[fitness value, x coord, y coord]
self.pBest = [Q(self.x, self.y), self.x, self.y]
self.index = i
#local best
self.lBest = []
self.lBestIndex = 0
#array for neighbor indicies
self.neighbors = []
#for printing particle info
def __str__(self):
if k > 0:
return ' i: '+str(self.index)+'\n x: '+str(self.x)+'\n y: '+str(self.y)+'\nv_x: '+str(self.velocity_x)+'\nv_y: '+str(self.velocity_y)+'\n b: '+str(self.pBest[0])+'\n l: '+str(self.lBest)+'\n'
else:
return ' i: '+str(self.index)+'\n x: '+str(self.x)+'\n y: '+str(self.y)+'\nv_x: '+str(self.velocity_x)+'\nv_y: '+str(self.velocity_y)+'\n b: '+str(self.pBest[0])+'\n'

###########

def createParticles():
global pList
global numParticles
global k
#create particle list
for i in range(0,numParticles):
pList.append(Particle(i))

#fill neighbor lists
if k > 0:
for p in pList:
for x in range(p.index-(k/2),p.index+(k/2)+1):
if x > numParticles:
p.neighbors.append(x%numParticles)
elif x < 0:
p.neighbors.append

Solution

I had a quick look at the github repo. I'd highly recommend splitting your project into multiple files as this will make things easier to manage. You might also want to consider writing some unit tests.

Design comments:
The use of global in createParticles is a bit concerning, it looks like you want to have some sort of ParticleManager class (or similar) that will explicitly manage the particles. This will be much easier to maintain than keeping a list in global scope. Things like gBest and bestIndex really work better when they are in an appropriate management class.

From looking at the Github code it looks like you have a bunch of different functions that manipulate a global list of particles. By creating a class that stores the list of particles plist and has methods that operate on it you will save yourself a lot of headaches when dealing with the data. This encapsulation will help you keep maintaining the code simple. So to answer your question, definitely make createParticles a method of a class. For example if you have a problem with the data in your current design you first have to search through a whole bunch of different functions to see which ones could possibly have side effects that modified your data before you can be sure you have fixed your problem. By having the dedicated particle management class you immediately know exactly which functions could be responsible and you immediately know where they are.

Python specific comments:
You really should get in the habit of using docstrings. For example:

class Particle:
    """This class models a particle in the system, it does a,b,c...."""

def createParticles():
    """This function creates the particle objects used in the system"""


You have some really long string lines, Python will let you concatenate these strings. So instead of:

if k > 0:
      return '  i: '+str(self.index)+'\n  x: '+str(self.x)+'\n  y: '+str(self.y)+'\nv_x: '+str(self.velocity_x)+'\nv_y: '+str(self.velocity_y)+'\n  b: '+str(self.pBest[0])+'\n  l: '+str(self.lBest)+'\n'


you could instead do:

if k > 0:
       return('  i: '+str(self.index)+'\n'
              '  x: '+str(self.x)+'\n'
              '  y: '+str(self.y)+'\n'
              'v_x: '+str(self.velocity_x)+'\n'
              'v_y: '+str(self.velocity_y)+'\n'
              '  b: '+str(self.pBest[0])+'\n'
              '  l: '+str(self.lBest)+'\n')


Which is easier to read already. However you can go further here by using string formatting. Additionally there's duplicated code in building the strings for returning, just make the first string and if K > 0 just append what you need in that case.

So the complete __str__ implementation I might go with looks something like this:

def __str__(self):
    """Creates string representation of particle"""
    ret = """  i: {self.index!s}
  x: {self.x!s}
  y: {self.y!s}
v_x: {self.velocity_x!s}
v_y: {self.velocity_y!s}
  b: {self.pBest[0]!s}""".format(**locals())

    if k > 0:
        return ret+'  l: '+str(self.lBest)+'\n'
    else:
        return ret

Code Snippets

class Particle:
    """This class models a particle in the system, it does a,b,c...."""

def createParticles():
    """This function creates the particle objects used in the system"""
if k > 0:
      return '  i: '+str(self.index)+'\n  x: '+str(self.x)+'\n  y: '+str(self.y)+'\nv_x: '+str(self.velocity_x)+'\nv_y: '+str(self.velocity_y)+'\n  b: '+str(self.pBest[0])+'\n  l: '+str(self.lBest)+'\n'
if k > 0:
       return('  i: '+str(self.index)+'\n'
              '  x: '+str(self.x)+'\n'
              '  y: '+str(self.y)+'\n'
              'v_x: '+str(self.velocity_x)+'\n'
              'v_y: '+str(self.velocity_y)+'\n'
              '  b: '+str(self.pBest[0])+'\n'
              '  l: '+str(self.lBest)+'\n')
def __str__(self):
    """Creates string representation of particle"""
    ret = """  i: {self.index!s}
  x: {self.x!s}
  y: {self.y!s}
v_x: {self.velocity_x!s}
v_y: {self.velocity_y!s}
  b: {self.pBest[0]!s}""".format(**locals())

    if k > 0:
        return ret+'  l: '+str(self.lBest)+'\n'
    else:
        return ret

Context

StackExchange Code Review Q#92524, answer score: 5

Revisions (0)

No revisions yet.