I'm trying to make a simple PSO (particle-swarm optimization) program.
Below is my current code, and I've been tweaking the weights only to find the "optimization" not working.
import random as ran
import math
# import threading as thr
import pygame as gm
# import pycuda as cuda
# import matplotlib.pyplot as plt
p_max = 10 # Maximum number of particles
rand_max = 100 # Maximum random vector value
# history = 10 # Number of iterations to record as history (including most current)
width = 600
height = 600
func_dict = {
"sphere" : ( lambda x, y: ( (x-(width/2))**2 + (y-(height/2))**2 ) ),
"booth" : ( lambda x, y: (((x-(width/2)) + 2*(y-(height/2)) - 7) ** 2) + ((2*(x-(width/2)) + (y-(height/2)) - 5) ** 2) ),
"matyas" : ( lambda x, y: (0.26 * ((x-(width/2))**2 + (y-(height/2))**2) - 0.48*(x-(width/2))*(y-(height/2))) )
} # (x-(width/2)) and (y-(height/2)) to shift the Zero to the display center
func = "sphere" # Choose function from func_dict
# Weights (0<w<1)
wLocal = 0.4 # Explore weight
wGlobal = 0.8 # Exploit weight
wRandom = 0.02 # Weight of random vector
global_best = [None, None, None] # Initial blank
class particle: # particles
global global_best
def __init__(self):
global global_best
global width, height
global func_dict, func
self.vel_x = 0
self.vel_y = 0
self.pos_x = ran.randint(0, width)
self.pos_y = ran.randint(0, height)
self.pos_z = func_dict[func](self.pos_x, self.pos_y)
self.local_best = [self.pos_x, self.pos_y, self.pos_z]
if (global_best[0] == None) or (global_best[1] == None) or (global_best[2] == None): # Is 1st particle
global_best = self.local_best
def update(self): # Update vectors
global width, height
global rand_max
global wGlobal, wLocal, wRandom
global global_best
self.vel_x = (wGlobal * (global_best[0] - self.pos_x)) + (wLocal * (self.local_best[0] - self.pos_x)) + (wRandom * ran.randint(-rand_max, rand_max))
self.vel_y = (wGlobal * (global_best[1] - self.pos_y)) + (wLocal * (self.local_best[1] - self.pos_y)) + (wRandom * ran.randint(-rand_max, rand_max))
# self.pos_x = (self.pos_x + self.vel_x) % width
# self.pos_y = (self.pos_y + self.vel_y) % height
self.pos_x += self.vel_x
self.pos_y += self.vel_y
if self.pos_x < 0:
self.pos_x = 0
if self.pos_y < 0:
self.pos_y = 0
if self.pos_x > width:
self.pos_x = width
if self.pos_y > height:
self.pos_y = height
self.pos_z = func_dict[func](self.pos_x, self.pos_y)
if self.pos_z < global_best[2]:
global_best = [self.pos_x, self.pos_y, self.pos_z]
particles = [None for _ in range(p_max)]
def initialize():
global_best = [None, None, None]
for foo in range(p_max):
particles[foo] = particle() # create new particles
# def dist(p1, p2): # distance
# return(math.sqrt( ( (p1.pos_x - p2.pos_y)**2) + ((p1.pos_y - p2.pos_y)**2) ) )
# def update(this): # this = particle
# this.vel_x = (wGlobal * (global_best[0] - this.pos_x)) + (wLocal * (this.local_best[0] - this.pos_x)) + (wRandom * ran.randint(0, rand_max))
# this.vel_y = (wGlobal * (global_best[1] - this.pos_y)) + (wLocal * (this.local_best[1] - this.pos_y)) + (wRandom * ran.randint(0, rand_max))
# # this.pos_x = (this.pos_x + this.vel_x) % width
# # this.pos_y = (this.pos_y + this.vel_y) % height
# this.pos_x += this.vel_x
# this.pos_y += this.vel_y
# if this.pos_x < 0:
# this.pos_x = 0
# if this.pos_y < 0:
# this.pos_y = 0
# if this.pos_x > width:
# this.pos_x = width
# if this.pos_y > height:
# this.pos_y = height
# # return this
# def update_multi(things): # things = list() of particles
# these = things
# for item in these:
# item = update(item)
# return these
gm.init()
main = gm.display.set_mode((width, height))
end_program = False
initialize()
main.fill((255, 255, 255))
while end_program == False:
# main.fill((255, 255, 255)) #Comment/Uncomment to leave trace
# plt.plot() # Plot functions
for event in gm.event.get():
if event.type == gm.QUIT:
end_program = True
for foo in range(len(particles)):
particles[foo].update()
gm.draw.circle(main, (0, 0, 0), (int(particles[foo].pos_x), int(particles[foo].pos_y)), 5, 0)
gm.display.flip()
Problem 1: Program only runs when mouse is moving
I'm not sure why but the program only runs fairly quickly for a few iterations but seems to just stop afterwards, but continues when the mouse is moved.
Problem 2: Particles appear to stay local
As I move the mouse around, it kinda runs. What emerges is clumps of traces left when the 2nd # main.fill((255, 255, 255))
is uncommented. The 1st initial traces from before the program stops without the mouse's movement seem more spread out and I'm not sure if that's the global variables or the randoms at work.
Edit: I've fixed the problem where the program only runs when the mouse moves by unindenting:
for foo in range(len(particles)):
particles[foo].update()
gm.draw.circle(main, (0, 0, 0), (int(particles[foo].pos_x), int(particles[foo].pos_y)), 5, 0)
However, the particles still seem to hardly move from their own positions, oscilating locally.
Problem 1 was solved thanks to Marius. I fixed it by simply putting update
outside the event
loop.
Problem 2 was fixed by adding the local update that I forgot.
if self.pos_z < global_best[2]: # This was not forgotten
global_best = [self.pos_x, self.pos_y, self.pos_z]
if self.pos_z < local_best[2]: # This was forgotten
local_best = [self.pos_x, self.pos_y, self.pos_z]