wx.callafter interface freeze

16 views
Skip to first unread message

quant

unread,
Jul 23, 2009, 5:08:10 AM7/23/09
to wx-users
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()

Vadim Zeitlin

unread,
Jul 23, 2009, 7:29:12 AM7/23/09
to wx-u...@googlegroups.com
On Thu, 23 Jul 2009 02:08:10 -0700 (PDT) quant <spie...@th.physik.uni-frankfurt.de> wrote:

q> my problem is, if i start it the interface got freezed, and a got only
q> two graphical updates, one at the beginning and on at the end. So
q> could anyone help me ?

I think you should post this question to wxPython list, you'll find more
people able to read your code there. Personally I also think that your
example is way too long, you should try harder to reduce it to something
really simple and small still showing the problem because, first, it could
allow you to find the bug yourself and, second, it's unlikely that many
people have time to read through 300 lines of code.

Regards,
VZ

--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/

Reply all
Reply to author
Forward
0 new messages