Posts Tagged ‘python’

Pymaze: generate and solve mazes with python

Wednesday, March 3rd, 2010

In the last week end I decided to spend a couple of days writing a simple library for both maze generation and resolution.

At the moment the library is composed by three modules: the first one, maze, contains the base classes used to implement a general maze; the others, generators and solvers, implement some algorithms used to generate and solve respectively a maze.

There is also a test script, which will be soon moved to the bin directory, which can be used to test the above modules: in particular it consists of a gui written in gtk which let the user decide the size of the maze, the generator and solver algorithms, and finally watch the maze evolution.

You can find the library here. Feedback is welcome!

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!

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'],
     )

Python: defaults arguments

Tuesday, December 29th, 2009

Here is a funny snippet of code:

Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class foo:
...   def __init__(self, x=[]):
...     self.y = x
...
>>> a = foo()
>>> a.y.append("123456")
>>> a.y
['123456']
>>> b = foo()
>>> b.y
['123456']
>>> a.y.append("987654")
>>> a.y
['123456', '987654']
>>> b.y
['123456', '987654']

You better define foo as following:

>>> def foo():
>>>  def __init__(self, x=None):
>>>    if x is None: x = []
>>>    self.y = x

Python: Generate fake B.S.O.D.

Friday, November 27th, 2009


I’ve just created a script which simulate a bsod by creating a fullscreen window and placing the right text in the right position.

In order to make it closer to reality, it’s better for you to download and install this font.

And now the script: bsod

Python: From a list of digits, to a number

Tuesday, September 22nd, 2009
l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
n = reduce(lambda s, x: s*10 + x, l)
print l, n

Python: Multiple Replace

Monday, September 14th, 2009

Create the dictionary used for the substitusions.

repl_dict = {'a': "aRbFR", 'b': "LFaLb"}

Pre-compile the regular expression.

regex = re.compile("(%s)" % "|".join(map(re.escape, repl_dict.keys())))

Create some text, and do the replace on it.

old = "Fa"
new = regex.sub(lambda mo: repl_dict[mo.string[mo.start():mo.end()]], old)

In the example below you can see the evolution of the string which describes the Heighway Dragon at n-th step.

import re

repl_dict = {'a': "aRbFR", 'b': "LFaLb"}
regex = re.compile("(%s)" % "|".join(map(re.escape, repl_dict.keys())))

d = "Fa"
n_iterations = 5
for i in xrange(n_iterations + 1):
  if i == 0: d = "Fa"
  else: d = regex.sub(lambda mo: repl_dict[mo.string[mo.start():mo.end()]], d)

[ source: Active State Code ]

Python: next_permutation()

Sunday, September 13th, 2009

The following algorithm generates the next permutation lexicographically after a given permutation. It changes the given permutation in-place.

Find the highest index i such that s[i] < s[i+1]. If no such index exists, the permutation is the last permutation.

# given s (a list of elements)
for i in reversed(xrange(len(s))):
  if s[i] > s[i-1]:
    break
else:
  return []
i -= 1

Find the highest index j > i such that s[j] > s[i]. Such a j must exist, since i+1 is such an index.

for j in reversed(xrange(i + 1, len(s))):
  if s[j] > s[i]: break

Swap s[i] with s[j].

t = s[i]
s[i] = s[j]
s[j] = t

Reverse all the order of all of the elements after index i

s[i + 1:] = reversed(s[i + 1:])

And now the entire function:

def next_permutation(s):
  for i in reversed(xrange(len(s))):
    if s[i] > s[i-1]:
      break
  else:
    return []
  i -= 1
  for j in reversed(xrange(i + 1, len(s))):
    if s[j] > s[i]: break
  t = s[i]
  s[i] = s[j]
  s[j] = t
  s[i + 1:] = reversed(s[i + 1:])
  return s

[ source: Wikipedia ]