hi
i am trying to code an wx-interface with multithreading and i read
something about the wx.callafter function. so i tryed to solve it with
this nice tool.
the program is a main program with a gui, if i press the start button
the program should run an algorithm and return the different
result-steps in a graphical window. this means i would like to get a
graphical update after each iteration
my problem is, if i start it the interface got freezed, and a got only
two graphical updates, one at the beginning and on at the end. So could
anyone
help me ?
sorry for my bad description, i am a quite new programmer and in python
as well
here is the code i talking about
######## imports ###########
···
#
import matplotlib
matplotlib.use("WXAgg")
matplotlib.interactive(True)
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
from matplotlib.figure import Figure
from matplotlib.axes import Subplot
import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
import math
import wx
from threading import Thread
import threading
import time
#
###########################
wx.SetDefaultPyEncoding("iso-8859-15")
####################################################################################
#
def normal_pdf(mu, sigma, x_array):
first_term = 1 / math.sqrt(2*math.pi * sigma * sigma)
second_term = np.exp(-0.5 * (x_array - mu) * (x_array - mu) / (sigma
* sigma))
return first_term * second_term
####################################################################################
#
class Theta:
def __init__(self, count_of_gaussians):
# create array for each parameter and init by default-value
self.mu = np.zeros(count_of_gaussians)
self.sigma = np.ones(count_of_gaussians)
self.pi = np.zeros(count_of_gaussians)
self.pi[:] = 1.0 / count_of_gaussians
self.count_of_gaussians = count_of_gaussians
####################################################################################
#
########### testing threads #############
#
class ThreadedAction(Thread):
def __init__(self, em_obj, **kwargs):
Thread.__init__(self, **kwargs)
self.em_obj = em_obj
def run(self):
print "Performing expensive calculation in %s..."%self.getName()
while (not self.em_obj._converged and not self.em_obj.stop):
self.em_obj.iterate()
wx.CallAfter(self.em_obj.updateGUI)
print 'done.'
##################################################
# probability-functions with the convention to read:
# for example: p_c_G_x_theta should be read as:
# probability of c Given x (all datapoints n=1 to N) and theta
# note: instead of defining a function for every datapoint xn we define
a function for all datapoints
# conditional probability of data x given class c and parameters theta
(MoG: the gaussian with parameters mu_c, sigma_c)
def p_x_G_c_theta(x, c, theta):
return normal_pdf(theta.mu[c], theta.sigma[c], x)
# marginal probability of class c given parameters theta (MoG: prior =
const. mixture-parameter pi)
def p_c_G_theta(c, theta):
return theta.pi[c]
# joint probability of data x and class c given parameters theta
def p_x_c_G_theta(x, c, theta):
# product-rule: conditional times marginal
return p_x_G_c_theta(x, c, theta) * p_c_G_theta(c, theta)
# marginal probability of data x given parameters theta
def p_x_G_theta(x, theta):
# sum-rule: sum of all joints
sum_of_joints = np.zeros(x.shape[0])
for cs in xrange(theta.count_of_gaussians):
sum_of_joints += p_x_c_G_theta(x, cs, theta)
return sum_of_joints
def p_c_G_x_theta(c, x, theta):
# applying bayes theorem using joint-probability and marginal
joint = p_x_c_G_theta(x, c, theta)
marginal = p_x_G_theta(x, theta)
return joint / marginal
def generate_data(theta, N):
data = np.zeros(N)
for n in xrange(N):
c = c = np.random.uniform(0, 1)
offset = 0
for i in xrange(theta.count_of_gaussians):
if c < theta.pi[i] + offset:
data[n] = np.random.normal(theta.mu[i], theta.sigma[i])
break
else:
offset += theta.pi[i]
return data
def update_mu(x, expectation, theta_new):
for c in xrange(theta_new.count_of_gaussians):
numerator = expectation[c,:] * x
numerator = numerator.sum()
denominator = expectation[c,:].sum()
theta_new.mu[c] = numerator / denominator
def update_sigma(x, expectation, theta_new):
for c in xrange(theta_new.count_of_gaussians):
numerator = expectation[c,:] * (x - theta_new.mu[c])**2
numerator = numerator.sum()
denominator = expectation[c,:].sum()
theta_new.sigma[c] = math.sqrt(numerator / denominator)
def update_pi(x, expectation, theta_new):
N = x.shape[0]
for c in xrange(theta_new.count_of_gaussians):
theta_new.pi[c] = expectation[c,:].sum() / N
####################################################################################
#
class EM_MoG(wx.Frame):
def __init__(self, task):
wx.Frame.__init__(self, None, -1, "Interactive Frame v1.0")
self.fig = Figure((10,10), 75)
self.canvas = FigureCanvasWxAgg(self, -1, self.fig)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.TOP)
self._task = task
# A: generate datapoints with given parameters
self._theta_gen = Theta(2)
self._theta_gen.mu[0] = -2
self._theta_gen.mu[1] = 4
self._theta_gen.sigma[0] = 1
self._theta_gen.sigma[1] = math.sqrt(10)
self._theta_gen.pi[0] = 0.4
self._theta_gen.pi[1] = 1 - self._theta_gen.pi[0]
self._data_count = 200
if self._task == 'c':
self._data_count = 5
self._data = generate_data(self._theta_gen, self._data_count)
# B/D: initialize theta with given initial values
self._x_min = -20
self._x_max = 20
self._theta_old = Theta(2)
if self._task <> 'd':
self._theta_old.mu[0] = - 5
self._theta_old.mu[1] = 5
self._theta_old.sigma[0] = 1
self._theta_old.sigma[1] = math.sqrt(10)
self._theta_old.pi[0] = 0.5
self._theta_old.pi[1] = 1 - self._theta_old.pi[0]
else:
self._theta_old.mu = np.random.uniform(self._x_min,
self._x_max, self._theta_old.count_of_gaussians)
self._theta_old.sigma = np.random.uniform(0, self._x_max/2,
self._theta_old.count_of_gaussians)
self._theta_old.pi[0] = np.random.uniform(0, 1)
self._theta_old.pi[1] = 1 - self._theta_old.pi[0]
###############################################################################
# some preparation for plotting pdfs, plot generating pdf directly
count = 500.0
# create x-array for plotting
self._pdf_x = np.zeros(count)
step = (self._x_max - self._x_min) / count
for x in xrange(count):
self._pdf_x[x] = self._x_min + x * step
# create y-array for generating pdf
self._y_gen = p_x_G_theta(self._pdf_x, self._theta_gen)
################################################################################
self.a = self.fig.add_subplot(221)
self.a.axes.plot(self._pdf_x, self._y_gen)
# initialisiere die plot-frames
self.b = self.fig.add_subplot(223)
self.c = self.fig.add_subplot(222)
self.d = self.fig.add_subplot(224)
#################################################################################
# run EM-algorithm
self._max_iterations = 1000
self._expectation =
np.zeros((self._theta_old.count_of_gaussians, self._data.shape[0]))
self._log_likelihood = np.zeros((self._max_iterations))
self._theta_history = np.zeros((self._max_iterations,
3*self._theta_old.count_of_gaussians))
self._converged = False
self._counter = 0
self._epsilon = 0.001
################### Controller ######################
#
# create start/stop buttons
self.button_start = wx.Button(self,-1, " Start ", size=(-1,-1))
self.button_stop = wx.Button(self,-1, " Stop ", size=(-1,-1))
self.button_exit = wx.Button(self,-1, " Exit ", size=(-1,-1))
self.cb1 = wx.CheckBox(self, -1, "Show title" )
# bind actions to the buttons
self.button_start.Bind(wx.EVT_BUTTON,self.OnStart)
self.button_stop.Bind(wx.EVT_BUTTON, self.OnStop)
self.button_exit.Bind(wx.EVT_BUTTON, self.OnExit)
# pack the buttons in the Sizer
btnsizer = wx.BoxSizer(wx.HORIZONTAL)
btnsizer.Add(self.button_start, 1, wx.LEFT)
btnsizer.Add(self.button_stop, 1, wx.LEFT)
btnsizer.Add(self.button_exit, 1, wx.RIGHT)
btnsizer1 = wx.BoxSizer(wx.VERTICAL)
btnsizer1.Add(self.cb1, 1, wx.DOWN)
sizer.Add(btnsizer, 0, wx.TOP)
sizer.Add(btnsizer1, 0, wx.TOP)
self.SetSizer(sizer)
self.Fit()
#
#####################################################
# create stop-flag for multithreading
self.stop = False
def iterate(self):
time.sleep(0.1)
# store parameters to history-array
for t in xrange(self._theta_old.count_of_gaussians):
self._theta_history[self._counter, t] = self._theta_old.mu[t]
self._theta_history[self._counter, 2+ t] =
self._theta_old.sigma[t]
self._theta_history[self._counter, 4+ t] = self._theta_old.pi[t]
# get data for y-axis of estimated pdf
self._y_em = p_x_G_theta(self._pdf_x, self._theta_old)
# TODO: add legend
#pl.legend()
# E-step
for c in xrange(self._theta_old.count_of_gaussians):
self._expectation[c, :] = p_c_G_x_theta(c, self._data,
self._theta_old)
# M-step
theta_new = Theta(2)
update_mu(self._data, self._expectation, theta_new)
update_sigma(self._data, self._expectation, theta_new)
update_pi(self._data, self._expectation, theta_new)
# compute log-likelihood
# log-likelihood of initial theta is not logged because it is
very low
# and the curve gets ugly
# to try including the initial theta just move this block to
first line of the loop
ll = p_x_G_theta(self._data, theta_new)
ll = np.log(ll)
ll = ll.sum()
self._log_likelihood[self._counter] = ll
# check convergence
if self._counter > 0:
if self._log_likelihood[self._counter] -
self._log_likelihood[self._counter-1] < self._epsilon:
# idea of Gervasio: also check if parameters don't
change anymore:
if np.abs(theta_new.mu - self._theta_old.mu).max() <
self._epsilon and np.abs(theta_new.sigma - self._theta_old.sigma).max()
< self._epsilon and np.abs(theta_new.pi - self._theta_old.pi).max() <
self._epsilon:
self._converged = True
elif self._counter >= self._max_iterations:
self._converged = True
# increment counter
self._counter += 1
# update theta
self._theta_old = theta_new
def updateGUI(self):
# plot pdf obtained by em-step
self.b.axes.plot(self._pdf_x, self._y_em)
self.canvas.draw()
# plot history of parameters (idea of Omid!!)
#self.d.axes.plot(self._theta_history[:self._counter-1, :])
#self.canvas.draw()
# plot history of likelihood
#self.c.axes.plot(self._log_likelihood[:self._counter])
#self.canvas.draw()
pass
def OnStop(self,event=None):
# self.timer.Stop()
# self.stop = True
pass
def OnStart(self,event=None):
# self.iterate()
################################
self.stop = False
action = ThreadedAction(self)
action.start()
################################
def OnExit(self,event=None):
self.Destroy()
def iterate_until_converged(self):
while (not self._converged):
self.iterate()
#
######################################################################################
if __name__ == '__main__':
app = wx.PySimpleApp(0)
frame = EM_MoG('d')
frame.Show(True)
app.MainLoop()