Archive for February, 2010

One of the best gif ..

Friday, February 26th, 2010

.. I’ve ever seen:

gstream – simple internet radio player

Friday, February 26th, 2010

Hi 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!

Gnome menu icons

Thursday, February 25th, 2010

I’ve lost half an hour wondering why my icons were not displayed beside the menu labels.
In the end I found the solution:

gconftool-2 --type bool --set /desktop/gnome/interface/menus_have_icons

Update: Genetic Algorithms: exercise #1

Monday, February 15th, 2010

Well 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, 2010

Here 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, 2010

Given 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!

Henrik Schwarz – Imagination Limitation

Tuesday, February 2nd, 2010