Archive for February, 2010
One of the best gif ..
Friday, February 26th, 2010gstream – simple internet radio player
Friday, February 26th, 2010Hi folks, today I’m here showing you my last simple creation, an application for those of you, like me, prefer listen to internet radio stations while standing in front of their monitors rather than playing mp3s files.
Gstream is basically a system tray application which let you play/stop one of the radio stations stored inside a configuration file; obviously you’ll also be able to add and remove the radios with a couple of clicks.
It’s written in python w/ gtk (interface) and gstreamer (http audio playback).
I have also decided to give github a try, so you can source the application from here: enjoy!
Update: Genetic Algorithms: exercise #1
Monday, February 15th, 2010Well I decided to fix a couple of things on the code published last night.
First of all, the fitness function: it consists now in a simple arithmetical evaluation of the chromosome.
def fitness(chromosome):
"""Compute the fitness function of the given chromosome.
In our case the fitness consists in the evaluation of mathematical expression
represented by the chromosome itself.
@param chromosome the chromosome to evaluate.
@return the fitness value if everything is ok, None otherwise.
"""
command = ''.join(purge_chromosome(chromosome))
try:
return eval(command)
except SyntaxError:
return None
except ZeroDivisionError:
return None
And now that the fitness function has been changed, we need also to modify the function used for the selection of the chromosomes. We need indeed to calculate the distance between the target and the score of the current chromosome; I decided to implement this with a radial basis function normalized on the minimum and maximum scores achieved by the chromosomes of the current population: it’s easier to look at the code:
def roulette_wheel_selection(self, n):
"""Select one or more chromosomes from the population by using the roulette
wheel proportional selection algorithm.
The distance between the target fitness, and the one of the current
chromosome is calculated making use of a radial basis function normalized
on the minimum and the maximum fitness score of the current population.
@param n the number of chromosomes to extract.
@return an array of selected chromosomes.
"""
# compute the scores of the chromosomes and the minimun and the maximum
# values of them.
(min_s, max_s) = (None, None)
scores = []
for chromosome in self.data:
score = fitness(chromosome)
if score is not None:
if min_s is None or score < min_s:
min_s = score
if max_s is None or score > max_s:
max_s = score
scores.append(score)
if max_s == min_s:
spread = 1e-10
else:
spread = (max_s - min_s)/math.sqrt(POPULATION_SIZE)
# compute the distance between the current score and the one of the TARGET.
distance = []
for s in scores:
if s is None:
distance.append(0)
elif s == TARGET:
distance.append(1)
else:
distance.append(math.exp(-(s - TARGET)**2/(spread)**2))
total_distance = sum(distance)
# select the chromosomes by implementing the roulette wheel selection based
# on the distance from the target solution.
selection = []
for _ in xrange(n):
probability = random.uniform(0, total_distance)
i = 0
while probability > distance[i]:
probability -= distance[i]
i += 1
selection.append(deepcopy(self.data[i]))
return selection
That’s all with the first set of modifications. The following ones are about the evaluation of a chromosome: instead of evaluate the function by hand, I preferred to put parenthesis in the right places, and then call the eval built-in method. Here is the snippet in which we add the parenthesis:
def purge_chromosome(chromosome):
"""Correct the given chromosome from syntactical errors.
A correct chromosome is composed by a sequence of number / operators.
Additional parentesis are placed accordingly.
Suppose the given chromosome is like ['1', '+', '-', '9', '/', '3']. The
result will be as following: ['(', '(', '1', '+', '9', ')', '/', '3', ')'].
"""
correct_chromosome = []
next_num = True
for item in chromosome:
if next_num and item in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
correct_chromosome.append(item)
if len(correct_chromosome) != 1:
correct_chromosome.append(')')
correct_chromosome.insert(0, '(')
next_num = False
elif not next_num and item in ['+', '-', '*', '/']:
correct_chromosome.append(item)
next_num = True
if next_num and correct_chromosome:
correct_chromosome.pop()
return correct_chromosome
Well, here is the new version of the script. I hope you enjoy!
Genetic Algorithms: exercise #1
Monday, February 15th, 2010Here is an exercise I found around looking for some GA tutorials:
Given the digits 0 through 9 and the operators +, -, * and /, find a sequence that will represent a given target number. The operators will be applied sequentially from left to right as you read.
Here is my solution: it seems longer than the necessary but I decided to comment the code the more as possible.
Script Setting
We can modify these parameters to test different solutions.
# This value will be replaced by sys.argv[1]. TARGET = None # The number of chromosomes contained in one population. POPULATION_SIZE = 50 # Because a correct chromosome is composed by pairs of number and operators, # you better choice an odd number of genes. CHROMOSOME_SIZE = 11 # List of possible values a gene can assume. GENES = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/'] # Define the probability used to apply the crossover operator to a chromosome. CROSSOVER_PROBABILITY = 0.7 # Define the probability used to apply the mutation operator to a chromosome. MUTATION_PROBABILITY = 0.7 # How often a message is shown on screen displaying the work in progress. SHOW_EVOLUTION = 50 # the higher number we can represent using a chromosome which is a sequence of # number, operator, number operator an so on. MAX = 9**(math.ceil(CHROMOSOME_SIZE / 2)) # the lower number we can rapresent using a chromosome which is a sequence of # number, operator, number operator and so on. MIN = -MAX / 9 # the spread of the gaussian function used inside the fitness function, is # based on normalization of the maximum distance between two possible # solutions, and the number of chromosomes contained in a population. GAUSSIAN_SPREAD = (MAX - MIN) / math.sqrt(POPULATION_SIZE)
Utility functions
Following, is a collection of functions used all around the script. In details we have a function which returns a shuffled version of a given sequence, one to purge a chromosome in order to make it look like a sequence of number/operator/number/operator an so on, one to evaluate the value of string represented by the chromosome, and finally the one which compute the fitness value of the given chromosome.
def randperm(seq):
"""Return a shuffled version of given sequence.
Differently from the shuffle method from random module, the modifications are
not made in place.
"""
indices = range(len(seq))
random.shuffle(indices)
return [seq[i] for i in indices]
def purge_chromosome(chromosome):
"""Correct the given chromosome from syntactical errors.
A correct sequence is composed by a number an operator a number and so on.
"""
correct_chromosome = []
next_num = True
for item in chromosome:
if next_num and item in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
correct_chromosome.append(item)
next_num = False
elif not next_num and item in ['+', '-', '*', '/']:
correct_chromosome.append(item)
next_num = True
if next_num and correct_chromosome:
correct_chromosome.pop()
return correct_chromosome
def my_eval(command):
"""Evaluate the given command from left to right by not taking into account
the precedences between arithmetical operators.
"""
command = list(command)
if not command:
raise SyntaxError
result = int(command.pop(0))
while command:
op = command.pop(0)
other = int(command.pop(0))
if op == '+':
result += other
if op == '-':
result -= other
if op == '*':
result *= other
if op == '/':
result /= other
return result
def fitness(chromosome):
"""Compute the fitness function of the given chromosome.
@param chromosome the chromosome to evaluate.
@return the fitness value if everything is ok, None otherwise.
"""
command = ''.join(purge_chromosome(chromosome))
try:
value = my_eval(command)
if value == TARGET:
return 1
else:
# use the gaussian function to compute the distance between the current
# and the target solution
return math.exp(-(TARGET - value)**2/GAUSSIAN_SPREAD)
except SyntaxError:
return 0
except ZeroDivisionError:
return 0
Class objects
I decided to create a couple of class for the representation of both the chromosomes and the population; in particular the Population class exports the method used to evolve such as the the selection, crossover and the mutation operators. There is a lot of python in these definitions:
class Chromosome(object):
def __init__(self, **kwargs):
"""Create a new chromosome by specifying its value, and a method used to
compute the fitness function. Here is the list of accepted keywords:
data
chromosome_size
"""
if 'data' in kwargs:
self.data = kwargs['data']
else:
data = []
for _ in xrange(kwargs['chromosome_size']):
data.append(random.choice(GENES))
self.data = data
def __len__(self):
"""Return the lenght of the chromosome.
"""
return len(self.data)
def __getitem__(self, i):
"""Return the i-th gene of the chromosome.
"""
return self.data[i]
def __setitem__(self, i, value):
"""Modify the i-th gene of the chromosome.
"""
self.data[i] = value
def __str__(self):
"""Display the chromosome on screen.
"""
return ''.join(self.data)
class Population(object):
def __init__(self, **kwargs):
"""Create a new population of chromosomes and set the paramenters which are
going to be used during evolution. Here is a list of the accepted keywords:
data
chromosome_size
population_size
"""
data = []
if 'data' in kwargs:
data = kwargs['data']
elif 'population_size' in kwargs:
data = []
for _ in xrange(kwargs['population_size']):
data.append(Chromosome(**kwargs))
self.data = data
self.index = -1
def roulette_wheel_selection(self, n):
"""Select one or more chromosomes from the population by using the roulette
wheel proportional selection algorithm.
@param n the number of chromosomes to extract.
@return an array of selected chromosomes.
"""
scores = map(fitness, self.data)
total_score = sum(scores)
selection = []
for _ in xrange(n):
probability = random.uniform(0, total_score)
i = 0
while probability > scores[i]:
probability -= scores[i]
i += 1
selection.append(deepcopy(self.data[i]))
return selection
def crossover1(self, i, j, probability):
"""Apply the crossover operator on the i-th and j-th chromosomes with the
given probability. A signle random cut point is choosen.
@return an array containing the new chromosomes.
"""
if random.random() <= probability:
point = random.randint(0, len(self.data[i]) - 1)
datai = self.data[i][:point] + self.data[j][point:]
dataj = self.data[j][:point] + self.data[i][point:]
return [Chromosome(data=datai), Chromosome(data=dataj)]
else:
return []
def mutate1(self, i, probability):
"""Mutate a gene of the i-th chromosome with a given probability.
"""
data = self.data[i].data[:]
if random.random() <= probability:
point = random.randint(0, len(self.data[i]) - 1)
data[point] = random.choice(GENES)
return Chromosome(data=data)
def __iadd__(self, other):
"""Add a one or more chromosomes to the population.
"""
if isinstance(other, Chromosome):
self.data.append(other)
elif isinstance(other, list):
self.data += other
return self
def __len__(self):
"""Return the number of chromosomes contained in the population.
"""
return len(self.data)
def __str__(self):
"""Display the population on screen.
Display a chromosome per line.
"""
return '\n'.join(map(str, self.data))
def __iter__(self):
"""Make the object iterable.
"""
return self
def next(self):
"""Method invoked while iterating over the population of chromosomes.
"""
if self.index == len(self.data) - 1:
raise StopIteration
self.index = self.index + 1
return self.data[self.index]
Main
Finally we have the main function which evolve a random generated population in order to reach the goal:
def main(argv):
if len(sys.argv) != 2:
print 'Usage: %s ' % sys.argv[0]
return 1
global TARGET
TARGET = float(sys.argv[1])
pop = Population(population_size=POPULATION_SIZE,
chromosome_size=CHROMOSOME_SIZE)
generation = 0
while True:
# evaluation
if generation != 0 and not generation % SHOW_EVOLUTION:
print 'Generation #%d' % generation
for chromosome in pop:
score = fitness(chromosome)
if score == 1:
print 'Success(#%d):' % generation,
print ''.join(purge_chromosome(chromosome))
return
# selection
pop_1 = Population(data=pop.roulette_wheel_selection(POPULATION_SIZE))
# crossover
pop_2 = Population()
while len(pop_2) != POPULATION_SIZE:
i = random.randint(0, POPULATION_SIZE - 1)
j = random.randint(0, POPULATION_SIZE - 1)
pop_2 += pop_1.crossover1(i, j, CROSSOVER_PROBABILITY)
# mutation
pop = Population()
for i in xrange(len(pop_2)):
pop += pop_2.mutate1(i, MUTATION_PROBABILITY)
generation += 1
That's all! All you have to do right now, is to download the script and launch it by specifying a target number:
$ python find-the-sequence.py 666 Generation #50 Success(#85): 9*9-7*9
Python: packaging
Sunday, February 7th, 2010Given a structure of a project ..
gstream/
bin/
gstream
gstreamlib/
__init__.py
gui.py
player.py
playlist.py
.. you can create a simple setup.py script containing:
#!/usr/bin/env python
from distutils.core import setup
setup(name='gstream',
version='0.5',
author='Matteo Landi',
author_email='landimatte@gmail.com',
scripts=['bin/gstream'],
package_dir={'gstreamlib': 'gstreamlib'},
packages=['gstreamlib'],
)
Lorem Ipsum
Thursday, February 4th, 2010#!/usr/bin/env bash cat << EOF Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. EOF
Copy it into a file, and make it executable! Enjoy!
