Sophie

Sophie

distrib > Mandriva > 2010.2 > i586 > media > contrib-backports > by-pkgid > df29c83ca401d91ec9c00bfcf7fea4ea > files > 157

shedskin-0.8-2mdv2010.2.i586.rpm

#!/usr/bin/env python

# placed in the public domain by Stavros Korokithakis

import time
import copy
from random import randrange, randint, seed, random, choice, triangular
seed(42)

# The size of the data bus of the multiplexer.
DATA_SIZE = 4
# The size of the selector, in bits (this is log2   (DATA_SIZE)).
MUX_SIZE = 2
MAX_DEPTH = 5

OPCODE_NONE = 0
OPCODE_AND = 1
OPCODE_OR = 2
OPCODE_NOT = 3
OPCODE_IF = 4

ARG_NUM = (0, 2, 2, 1, 3)

STATE_OPCODE = 0
STATE_ARGUMENT = 1
STATE_SUBTREE = 2

def fitness(individual):
    return individual.fitness

def make_random_genome(node, depth=0):
    if depth >= MAX_DEPTH or random() > 0.7:
        node.opcode = OPCODE_NONE
        node.args = None
        node.value = randrange(MUX_SIZE + DATA_SIZE)
    else:
        node.opcode = randint(OPCODE_AND, OPCODE_IF)
        node.value = 0
        node.args = tuple([TreeNode() for _ in range(ARG_NUM[node.opcode])])
        for arg in node.args:
            make_random_genome(arg, depth+1)

class TreeNode:
    def __init__(self, opcode=OPCODE_NONE, value=-1, args=None):
        self.opcode = opcode
        self.value = value
        self.args = args

    def mutate(self):
        """Mutate this node."""
        # If we're a terminal node, stop so we don't exceed our depth.
        if self.opcode == OPCODE_NONE:
            return

        if random() > 0.5:
            # Turn this node into a terminal node.
            make_random_genome(self, MAX_DEPTH)
        else:
            # Turn this into a different node.
            make_random_genome(self, MAX_DEPTH-1)


    def execute(self, input):
        if self.opcode == OPCODE_NONE:
            return (input & (1 << self.value)) >> self.value
        elif self.opcode == OPCODE_AND:
            return self.args[0].execute(input) & \
                   self.args[1].execute(input)
        elif self.opcode == OPCODE_OR:
            return self.args[0].execute(input) | \
                   self.args[1].execute(input)
        elif self.opcode == OPCODE_NOT:
            return 1 ^ self.args[0].execute(input)
        elif self.opcode == OPCODE_IF:
            if self.args[0].execute(input):
                return self.args[1].execute(input)
            else:
                return self.args[2].execute(input)

    def __str__(self):
        if self.opcode == OPCODE_NONE:
            output = "(bit %s)" % self.value
        elif self.opcode == OPCODE_AND:
            output = "(and %s %s)" % self.args
        elif self.opcode == OPCODE_OR:
            output = "(or %s %s)" % self.args
        elif self.opcode == OPCODE_NOT:
            output = "(not %s)" % self.args
        elif self.opcode == OPCODE_IF:
            output = "(if %s then %s else %s)" % self.args

        return output

class Individual:
    def __init__(self, genome=None):
        """
        Initialise the multiplexer with a genome and data size.
        """
        if genome is None:
            self.genome = TreeNode()
            make_random_genome(self.genome, 0)
        else:
            self.genome = genome
        # Memoize fitness for sorting.
        self.fitness = 0.0

    def __str__(self):
        """Represent this individual."""
        return "Genome: %s, fitness %s." % (self.genome, self.fitness)

    def copy(self):
        return Individual(copy.deepcopy(self.genome))

    def mutate(self):
        """Mutate this individual."""
        if self.genome.args:
            node, choice = self.get_random_node()
            node.args[choice].mutate()

    def get_random_node(self, max_depth=MAX_DEPTH):
        """Get a random node from the tree."""
        root = self.genome
        previous_root = root
        choice = 0
        for counter in range(max_depth):
            if root.args and random() > 1 / MAX_DEPTH:
                previous_root = root
                choice = randrange(len(root.args))
                root = root.args[choice]
            else:
                break
        return (previous_root, choice)

    def update_fitness(self, full_test=False):
        """Calculate the individual's fitness and update it."""
        correct = 0
        if full_test:
            data = (1 << DATA_SIZE) - 1
            for mux in range(DATA_SIZE):
                for _ in range(2):
                    # Flip the bit in question.
                    data ^= (1 << mux)
                    input = (data << 2) | mux
                    output = self.genome.execute(input)

                    # Do some bit twiddling...
                    correct_output = (data & (1 << mux)) >> mux
                    if output == correct_output:
                        correct += 1
            total = DATA_SIZE * 2
        else:
            for mux in range(DATA_SIZE):
                for data in range(1 << DATA_SIZE):
                    input = (data << 2) | mux
                    output = self.genome.execute(input)

                    # Do some bit twiddling...
                    correct_output = (data & (1 << mux)) >> mux
                    if output == correct_output:
                        correct += 1
            total = (1 << DATA_SIZE) * DATA_SIZE

        self.fitness = (1.0 * correct) / total
        return self.fitness

class Pool:
    population_size = 300

    def __init__(self):
        """Initialise the pool."""
        self.population = [Individual() for _ in range(Pool.population_size)]
        self.epoch = 0

    def crossover(self, father, mother):
        son = father.copy()
        daughter = mother.copy()
        son_node, son_choice = son.get_random_node()
        daughter_node, daughter_choice = daughter.get_random_node()
        if son_node.args and daughter_node.args:
            temp_node = son_node.args[son_choice]
            son_node.args = son_node.args[:son_choice] + (daughter_node.args[daughter_choice], ) + son_node.args[son_choice+1:]
            daughter_node.args = daughter_node.args[:daughter_choice] + (temp_node, ) + daughter_node.args[daughter_choice+1:]
        return son, daughter

    def advance_epoch(self):
        """Pass the time."""
        # Sort ascending because this is cost rather than fitness.
        self.population.sort(key=fitness, reverse=True)
        new_population = []

        # Clone our best people.
        iters = int(Pool.population_size * 0.4)
        for counter in range(iters):
            new_individual = self.population[counter].copy()
            new_population.append(new_individual)

        # Breed our best people, producing four offspring for each couple.
        iters = int(Pool.population_size * 0.6)
        for counter in range(0, iters, 2):
            # Perform rank roulette selection.
            father = self.population[int(triangular(0, iters, 0))]
            mother = self.population[int(triangular(0, iters, 0))]
            children = self.crossover(father, mother)
            children[0].mutate()
            new_population += children

        self.population = new_population
        for person in self.population:
            person.update_fitness()
        self.epoch += 1

    def get_best_individual(self):
        """Get the best individual of this pool."""
        return max(self.population, key=fitness)


def main():
    pool = Pool()
    start_time = time.time()
    for epoch in range(100):
        pool.advance_epoch()
        best_individual = pool.get_best_individual()
        if not epoch % 10:
            print "Epoch: %s, best fitness: %s" % (epoch, best_individual.fitness)

    print "Epoch: %s, best fitness: %s" % (epoch, best_individual.fitness)
    print "Finished in %0.3f sec, best individual: %s" % (time.time() - start_time, best_individual)

if __name__ == "__main__":
    main()