Introduction to Genetic Algorithms
In this assignment, you will work with partially-completed code for a
genetic algorithm, adding crossover, selection and mutation
operators. You will also implement a fitness function for the n-queens
problem and evaluate the effectiveness of these operators and the
difficulty of the corresponding problems. While there is not a lot of
code to write for this assignment, doing this analysis may take you some time.
In this assignment, you will study the performance of a genetic
algorithm at solving three problems of increasing difficulty: the
pattern problem, the traveling salesman problem, and the n queens problem.
I have
provided the bulk of the code for you, but there are a few methods
remaining for you to implement.
About the code
This code demonstrates a Pythonic technique known as
"mixins". This should be familiar to you if you've used Lisp or
Scheme; Java's interface system is somewhat similar.
Mixins take advantage of Python's support for multiple inheritance
to "mix in" behavior from several unrelated classes. In our case,
we'll use it to deal with one of the common frustrations involving
GAs: all the different choices for implementing the algorithm.
We will mix in behavior from four different classes:
- GA. This is the class that performs the actual
algorithm. It has an __init__ method, and a run() method which
runs the GA.
- Selector. This is the class that governs selection. It has a
selectChromosomes method that takes a population as input and
returns two individuals to act as parents. It is subclassed by
TournamentSelector and RouletteSelector.
- Elitism. This is the class that governs elitism (retention of
individuals from one generation to the next.) It implements one
method: ApplyElitism, which take a population and an elitism rate
and returns a list of individuals to be kept for the next
generation. It is subclassed by DeterministicElitism,
TournamentElitism, and RouletteElitism.
- Crossover. This is the class that governs the generation of
new individuals. It implements one method: crossover, which takes
as input two parents and returns two children. It is subclassed by
OnePointStringCrossover, TwoPointStringCrossover,
PermutationCrossover, and GreedyCrossover.
We also need a class to contain the problem knowledge, which
includes a fitness function, a method to generate an initial
population, and a method to mutate chromosomes. I have provided
two of these, along with an abstract base class.
- BitStringGAProblem contains all of the information needed
to solve a "bitstring" style GA problem. Its constructor takes as
input a function to be used for fitness, and the length of each
string in the population.
- TSPGAProblem contains all of the information needed to
construct and solve TSP-style problems. Its constructor takes as
input a fitness function (I've provided one you might like called
computeTourCost) and the number of cities to work with.
This architecture may seem complicated, but it allows us to easily try
out different combinations of elitism, crossover and selection
mechanisms without editing, recompiling, or generating config
files. Instead, we create a mixin that inherits the specific classes
whose behavior we want to incorporate. For example, let's suppose we
want to solve the "all-ones" problem: find a bitstring with all ones
in it. This is easy, but will help us get going.
We start by making a fitness function:
>>> def allOnes(chr) :
return len([x for x in chr.bitstring if x == '1'])
Now, we create a BitStringProblem. Let's suppose we want each
individual to be of length 20.
import BitStringGAProblem
b = BitStringGAProblem.BitStringGAProblem(allOnes,20)
Lastly, we build a solver for this problem. Let's suppose we want to
use deterministic elitism, single-point crossover, and roulette
selection. We would do:
>>> class mySolver(GA.GA, Elitism.DeterministicElites, Crossover.OnePointStringCrossover, Selector.RouletteSelector):
... pass
This creates a class called mySolver that "mixes in" the behavior we
want. We then create an instance of this class, pass it the problem we
want to solve, and run it.
>>> m=mySolver(b)
>>> m.run()
(output omitted)
Run contains a number of optional arguments:
- popsize. How many individuals are in the population.
- elitismRate. The fraction of individuals carried over to the
next generation without crossover.
- mutationRate. The probability that a child will undergo
mutation.
- itersToRun. How long the GA should run.
So what code do I need to write, exactly?
You will need to write the following functions / classes:
- The crossover function in the GreedyCrossover class, contained in the file Crossover.py
- The selectChromosomes function in the TournamentSelector class, contained in the file Selector.py
- The createPatterMatchProblem function in the problemFactory file
- The createNQueensProblem function in the problemFactory.py file
TournamentSelection is pretty striaghtforward; GreedyCrossover is more challenging. (There are some
hints in Crossover.py). You might want to get comfortable running the
code first and start working on the next question, and then return to
this at the end.
What files are provided?
Once I've written the code, then what?
A challenge with GAs is knowing what parameters to
use and what methodologies to choose. To address this, you should
perform a set of experiments and prepare a report that summarizes your
results. You should consider the following problems:
- The "pattern" problem. Given an target string, such as
"1111100000", can the GA find it? Fitness is the number of correct
bits.
- The traveling salesman problem. Given a list of cities numbered
0..n, with routes between each city, find the minimum tour that
visits each city exactly once and returns to the start. I've
provided you with a graph class that can
generate random TSP problems.
- The n-Queens problem (for n restricted to be a power of 2)
There are lots of potential experiments to perform; rather than
explicitly list them all, I have provided some broad
guidelines. Within these guidelines, you may choose how to explore the
effect of different choices. You may want to focus on the time needed
to discover a solution, the quality of the solution found, or
both. You are free to modify or extend the code however you like,
including adding functionality to capture or log partial results,
print out answers, or add additional features. Please include a
README that describes any changes you have made.
Suggested Experiments:
- Compare the effect of different selection operators on each
problem.
- Compare the effect of different elitism mechanisms on each
problem.
- Compare the effect of different crossover operators on each
problem.
- Study the effect of different mutation rates or elitism rates.
- Study the effect of different population sizes.
- Compare the GAs ability to solve different varieties of the
pattern problem. Are some easier than others?
You may choose to do some or all of these experiments, or others
that you find interesting. Quality is more important than quantity;
a smaller number of carefully-crafted experiments with clear
conclusions is more interesting than a large number of shallow
experiments.
What to turn in
You should create a subdirectory in your cs662 subversion directory
called assignment4. This directory should contain all of the code
used for your experiments (including the code you need to write!)
In addition, this directory should contain a report (in Word or PDF)
that describes your experiments. Make sure you include enough
detail that your experiments can be replicated (e.g. all relevant
paramter settings -- I should be able to use your code to run the
same experiments easily by looking at your report). I would strongly
recommend including a graph for each experiment. You may use
whatever graphing software you like, but please be sure that your
graphs are clear and legible. You should also make sure that you
explain the results of your experiments. Don't just say "graph 3
shows compares roulette selection and tournament selection" -
explain what your conclusions are.
You will be graded on the thoroughness, quality, and presentation of
your experiments. Typical ways students are marked down are unclear
or incomplete graphs, little or no explanation of results, and a
lack of depth in experiments. Perfect English is not expected, but
we do need to be able to understand what you are trying to say.
How will this be graded
Half the grade for this assignment will be the code you need to write,
half will be for the report that you create.