Understanding Evolutionary Algorithms in Python

In this post, we will be diving into the world of context-free grammars to use in evolutionary algorithms.

What is context-free grammar?

Context-free grammar, also known as CFG, is a specific type of grammar used in computer science and linguistics. It is a set of production rules that define the structure of a language. CFG is made up of four components: a finite set of nonterminal characters V, a finite set of terminal characters \Sigma, a relation between the two R, and a start symbol S.

This is what it looks like in code form:

symbols = ['S','I','T','x']
grammar = {
    'S':['I+I','I-I','I*I','I/I'],
    'I':['I+I','I-I','I*I','I/I','(I)'] + ['T']*5,
    'T':['x'],
    'x':[str(float(x)) for x in range(1,10)]
}

I used multiple instances of the symbol T as a generic pointer to a terminal symbol. Using just one instance can make the nonterminal characters more likely, resulting in expressions that never end.

attribute_size = max([len(x) for x in grammar.values()])
individual = [random.randint(0,attribute_size-1) for _ in range(30)]
print(individual)

Running it results in the following output:

[6, 5, 3, 5, 1, 8, 3, 7, 9, 3, 9, 2, 6, 9, 1, 8, 3, 0, 6, 3, 4, 8, 8, 6, 8, 5, 4, 9, 7, 3]

Now we must define a method of converting this list to a string. I checked for the case that it was done, by storing the string from the loop before and seeing if it has changed. Also, note that we can disregard how large the rule numbers can be because we can use the modulo operation to index over the rules.

def express(individual, symbols, grammar):
    expression = symbols[0]
    for branch in individual:
        expression_old = expression
        for symbol in symbols:
            if symbol in expression:
                i = expression.rindex(symbol)
                rules = grammar[symbol]
                replacement = rules[branch % len(rules)]
                expression = expression[:i] + replacement + expression[i+1:]
                break
        if expression == expression_old:
            break
    return expression

Then, our individual from earlier becomes:

7.0/1.0-4.0/9.0*2.0

Evolutionary algorithms using DEAP

Evolutionary algorithms describe the class of algorithms that use simulated evolution for the purpose of optimization. Given a fixed length of attributes, individuals can “breed” with each other using crossover points. After many generations, with an optional mutation factor, we arrive at increasingly optimal populations.

DEAP is a library for Distributed Evolutionary Algorithms in Python. I used their onemax example to write the code below to maximize arithmetic operations. In this case, I only made the individual long enough to result in two numbers, so the answer should be 9\times9=81.

Results

Tracking the population as it evolves, DEAP’s onemax example

Full code

See full code here.


About the author



Hi, I'm Nathan. Thanks for reading! Keep an eye out for more content being posted soon.


Leave a Reply

Your email address will not be published. Required fields are marked *