Ejemplo n.º 1
0
    def from_config(world_config):
        world = World()
        if 'tools' in world_config:
            for t in world_config['tools']:
                tool = Tool.from_config(t)
                world.tools_repo.register_tool(tool)

        if 'populations' in world_config:
            for p in world_config['populations']:
                population = Population.from_config(p)
                world.populations.append(population)

        if len(world.populations) == 0:
            for i in range(0, world_config['populations_count']):
                pop_id = Population.generate_id()
                size = random.randint(world_config['min_pop_size'], world_config['max_pop_size'])
                world.populations.append(Population(pop_id, size))

        if len(world.tools_repo) == 0:
            min_adopting = 1
            max_adopting = round(math.sqrt(len(world.populations)))
            for i in range(0, world_config['tools_count']):
                tool = Tool.generate_instance()
                world.tools_repo.register_tool(tool)
                adopting_pops = random.randint(min_adopting, max_adopting)
                adopters = random.sample(world.populations, adopting_pops)
                [pop.adopt_tool(tool.id) for pop in adopters]

        return world
Ejemplo n.º 2
0
    def setUp(self):
        turns = ['F', 'L', 'L', 'F', 'R', 'F', 'R']
        values = ['P', 'P', 'H', 'P', 'H', 'H', 'P']
        self.turnList1 = TurnList(turns, values)

        turns2 = ['R', 'L', 'L', 'F', 'L', 'F', 'R']
        values2 = ['H', 'P', 'H', 'P', 'H', 'H', 'P']
        self.turnList2 = TurnList(turns2, values2)

        turns3 = ['F', 'L', 'L', 'F', 'R', 'F', 'R']
        values3 = ['P', 'P', 'H', 'P', 'H', 'H', 'P']
        self.turnList3 = TurnList(turns3, values3)

        bigValues1 = list("PPHPPHHPPHHPPPPPHHHHHHHHHHPPPPPPHHPPHHPPHPPHHHHH")
        bigTurns1 = list("FRFRLFLFRFLFFRLFLRRLLRRFRFLFRLLRFRLFLFRLFFFFRLLF")
        self.bigTL1 = TurnList(bigTurns1, bigValues1)

        bigValues2 = list("PPHPPHHPPHHPPPPPHHHHHHHHHHPPPPPPHHPPHHPPHPPHHHHH")
        bigTurns2 = list("FRFRLFLFRFLFFRLFLRRLLRRFRFLFRLLRFRLFLFRLFFFFRLLF")
        self.bigTL2 = TurnList(bigTurns2, bigValues2)

        self.pop1 = Population([self.turnList1],
                               [self.turnList1.calculateFitness()])
        self.pop2 = Population([self.turnList2],
                               [self.turnList2.calculateFitness()])
        self.pop3 = Population([self.turnList3],
                               [self.turnList3.calculateFitness()])
        self.bigPop1 = Population([self.bigTL1],
                                  [self.bigTL1.calculateFitness()])
        self.bigPop2 = Population([self.bigTL2],
                                  [self.bigTL2.calculateFitness()])
        self.emptyPopulation = Population()
Ejemplo n.º 3
0
    def __init__(self, sequenceOfValues, 
                 populationSize=100, 
                 generations=200,
                 elitePercent=.10, 
                 crossoverPercent=.80, 
                 mutationPercent=.25):
        self.sequenceOfValues   = sequenceOfValues                      # Values
        self.geneLength         = len(sequenceOfValues)                 # Length of a gene
        self.populationSize     = populationSize                        # Size of the Population
        self.generations        = generations                           # Number of generations to run.
        self.elitePercent       = elitePercent                          # Percent of genes to preserve as elite.
        self.crossoverPercent   = crossoverPercent                      # Percent of genes to crossover.
        self.mutationPercent    = mutationPercent                       # Percent of gene turns to mutate.
        
        self.currentPopulation  = Population(genes=[], fitness=[])      # Stores the current Population
        self.newPopulation      = Population(genes=[], fitness=[])      # Temporary Population used to construct next new population.

        self.elites             = Population(genes=[], fitness=[])      # The elites set aside while constructing the new population.
        self.nonElites          = Population(genes=[], fitness=[])      # The non-elites identified from the previous generation.
        # Crossed and Uncrossed have references to genes
        # from the previous generations - but they will be in their
        # mutated form.  Use these Population sets for access to
        # crossedover indexes and locations.
        self.crossed            = Population(genes=[], fitness=[])      # The genes which have been crossed over for the new generation.
        self.uncrossed          = Population(genes=[], fitness=[])      # The genes which where not crossed for the new generation.
        # Mutations is the final form of the current generation, minus the 
        # elites.  Use for the information about mutation locations.
        self.mutations          = Population(genes=[], fitness=[])      # The crossed and uncrossed genes combined for mutation.
        
        self.history            = []
Ejemplo n.º 4
0
 def createNewPopulation(self):
     """
         Combines the elite genes of the current generation
         with those non-elite genes which have been crossed over
         and mutated.  Stores them in a new population.
     """
     self.newPopulation = Population(genes=[], fitness=[])
     self.newPopulation.appendPopulation(self.elites)
     self.newPopulation.appendPopulation(self.mutations)
    def test_population_keeps_track_of_exploration_over_life_cycle(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()

        assert hasattr(
            self.pop, "explorationShortHistory"
        ), "population must keep track of its exploration history!"
    def test_population_keeps_track_of_resources_over_life_cycle(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()

        assert hasattr(
            self.pop, "ecologyShortHistory"
        ), "population must keep track of its ecological history!"
    def test_ecological_time_correctly_assessed(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()
        self.pop.lifeCycle()
        ngen = 5

        assert self.pop.ecoTime == self.pop.routineSteps, "one life cycle should be {0} units of ecological time, not {1}".format(
            self.pop.routineSteps, self.pop.ecoTime)

        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()
        for i in range(ngen):
            self.pop.lifeCycle()

        assert self.pop.ecoTime == self.pop.routineSteps * ngen, "{2} life cycles should be {0} units of ecological time, not {1}".format(
            self.pop.routineSteps * 10, self.pop.ecoTime, ngen)
    def test_population_keeps_track_of_ecological_time(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()

        assert hasattr(
            self.pop,
            "ecoTime"), "population must keep track of its ecological time!"
    def test_mutants_are_drawn_from_binomial(self, pseudorandom):
        pseudorandom(0)
        self.nIndividuals = 1000
        self.fakepop = Pop("test/test/parameters.txt")
        self.fakepop.create(n=self.nIndividuals)
        self.mutationRate = 0.2

        self.mutantCount = 0
        for ind in self.fakepop.individuals:
            ind.mutate(mutRate=self.mutationRate, mutStep=0.05)
            if ind.mutant:
                self.mutantCount += 1

        stat1, pval1 = scistats.ttest_1samp(
            [1] * self.mutantCount + [0] *
            (self.nIndividuals - self.mutantCount), self.mutationRate)
        assert pval1 > 0.05, "T-test mean failed. Observed: {0}, Expected: {1}".format(
            self.mutantCount / self.nIndividuals, self.mutationRate)
        self.test = scistats.binom_test(self.mutantCount,
                                        self.nIndividuals,
                                        self.mutationRate,
                                        alternative="two-sided")
        assert self.test > 0.05, "Success rate = {0} when mutation rate = {1}".format(
            self.mutantCount / self.nIndividuals, self.mutationRate)

        gc.collect()
    def test_population_creates_grid(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create(n=20)

        assert hasattr(self.pop, "grid")
        assert type(self.pop.grid) is Grid
        assert self.pop.grid.resources.shape == (self.pop.gridSize,
                                                 self.pop.gridSize)
    def test_resources_info_augmented_at_each_routine(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()
        for i in range(self.pop.routineSteps):
            self.pop.routine()

        assert self.pop.ecologyShortHistory.shape == ((self.pop.gridSize**2) *
                                                      self.pop.routineSteps, 4)
Ejemplo n.º 12
0
 def createMutations(self):
     """
         Mutate members of the current population which have
         been set aside as non-elite.
         NOTE: crossed-over genes have their new instances mutated,
               however the non-crossed-over non-elites will have
               their original genes (those in the current population)
               effected.
     """
     self.mutations = Population(genes=[], fitness=[])
     self.mutations.appendPopulation(self.crossed)
     self.mutations.appendPopulation(self.uncrossed)
     numMutations = (self.mutationPercent * self.mutations.size() *
                     len(self.mutations.getGene(0)))
     #self.mutations.mutatePopulation(numMutations)
     self.mutations.mutatePopulationWithIncreaseAndCooling(
         numMutations, len(self.history))
    def test_exploration_info_augmented_at_each_routine(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.predation = 0
        self.pop.create()
        for i in range(self.pop.routineSteps):
            self.pop.routine()

        assert self.pop.explorationShortHistory.shape == (
            self.pop.nIndiv * self.pop.routineSteps, 4)
    def test_resources_crash_when_too_large(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.initRes = 20
        self.pop.growth = 100
        self.pop.create()
        self.pop.lifeCycle()

        for cell in np.nditer(self.pop.grid.resources):
            assert cell <= self.pop.initRes, "resources too large, should have crashed"
Ejemplo n.º 15
0
 def createNewPopulation(self):
     """
         Combines the elite genes of the current generation
         with those non-elite genes which have been crossed over
         and mutated.  Stores them in a new population.
     """
     self.newPopulation = Population(genes=[], fitness=[])
     self.newPopulation.appendPopulation(self.elites)
     self.newPopulation.appendPopulation(self.mutations)
    def test_update_replaces_old_gen_with_new_gen(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create()
        self.pop.routine()
        self.pop.reproduce()
        oldgen = self.pop.individuals
        self.pop.update()

        assert len(self.pop.individuals) == self.pop.nIndiv
        assert self.pop.individuals != oldgen
    def test_population_can_gather_vs_survive(self):
        assert hasattr(Pop(), "gatherAndSurvive")
        assert callable(getattr(Pop(), "gatherAndSurvive"))

        self.pop = Pop("test/test/parameters.txt")
        self.pop.create()
        try:
            self.pop.gatherAndSurvive()
        except ValueError as e:
            assert False, "missing info: {0}".format(e)
    def test_mutation_does_not_affect_phenotype_type(self):
        self.fakepop = Pop("test/test/parameters.txt")
        self.fakepop.create(n=1)
        self.indiv = self.fakepop.individuals[0]
        self.phen = self.indiv.vigilance

        self.indiv.mutate(mutRate=1, mutStep=0.05)
        assert type(self.indiv.vigilance) is float

        gc.collect()
Ejemplo n.º 19
0
    def test_fertility_returns_positive_float(self):
        self.fakepop = Pop("test/test/parameters.txt")
        self.fakepop.create()

        for ind in range(len(self.fakepop.individuals)):
            indiv = self.fakepop.individuals[ind]
            setattr(indiv, "storage", 1 + 9 * ind)
            indiv.reproduce(fecundity=2)
            assert type(indiv.fertility) is float
            assert indiv.fertility >= 0
    def test_population_routine_changes_resources_grid(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create()

        resG = self.pop.grid.resources

        self.pop.routine()

        compareGrids = self.pop.grid.resources != resG

        assert compareGrids.all()
 def test_lam_error_when_resources_too_big(self):
     self.pop = Pop(par="test/test/parameters.txt")
     self.pop.initRes = 20000000000000000000
     self.pop.create()
     try:
         self.pop.lifeCycle()
     except ValueError as e:
         assert str(
             e
         ) == 'lam value too large', "This program should fail at poisson random draw, not '{0}'".format(
             e)
    def test_population_routine_changes_share_grid(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create()

        shareG = self.pop.grid.share

        self.pop.routine()

        compareGrids = self.pop.grid.share == shareG

        assert compareGrids.all() == False
    def test_update_returns_population_vigilance_info(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()
        self.pop.routine()
        self.pop.reproduce()
        self.pop.update()

        assert hasattr(self.pop, "vigilance")
        assert self.pop.vigilance is not None
        assert type(self.pop.vigilance) is float
        assert 0 <= self.pop.vigilance <= 1
    def test_mutants_are_defined(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create(n=1)
        self.indiv = self.pop.individuals[0]

        self.indiv.mutate(mutRate=0.5, mutStep=0.5)
        assert hasattr(
            self.indiv, "mutant"
        ), "We don't know if our individual is a mutant because it doesn't have this attribute"
        assert type(self.indiv.mutant) is bool

        gc.collect()
    def test_mutants_get_deviation_from_phenotype(self):
        self.fakepop = Pop("test/test/parameters.txt")
        self.fakepop.create(n=1)
        self.indiv = self.fakepop.individuals[0]
        self.indiv.mutate(mutRate=1, mutStep=0.05)
        assert hasattr(
            self.indiv, "mutationDeviation"
        ), "Individual is a mutant: it needs to be set a deviation from phenotype"

        assert -1 < self.indiv.mutationDeviation < 1

        gc.collect()
    def test_population_exploration_gives_share_info(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create()
        self.pop.explore()
        m = self.pop.gridSize

        assert hasattr(self.pop, "ncell")
        assert type(self.pop.ncell) is np.ndarray
        assert self.pop.ncell.shape == (m, m)
        assert hasattr(self.pop, "vcell")
        assert type(self.pop.vcell) is np.ndarray
        assert self.pop.vcell.shape == (m, m)
    def test_deviation_function_returns_float(self):
        self.fakepop = Pop("test/test/parameters.txt")
        self.fakepop.create(n=1)
        self.indiv = self.fakepop.individuals[0]
        self.v = self.indiv.vigilance

        for mutationBool in [True, False]:
            self.indiv.mutant = mutationBool
            self.indiv.deviate(mutStep=0.05)
            assert type(self.indiv.mutationDeviation) is float

        gc.collect()
Ejemplo n.º 28
0
    def test_grid_gif_is_created(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create()
        self.pop.launch(dev='on')

        self.filesListRootOut = os.listdir("./output")
        assert "grid_out.gif" in self.filesListRootOut, "no output grid gif created"
        os.remove("output/vigilance_out.txt")
        os.remove("output/vigilance_out.gif")
        os.remove('output/resources_out.txt')
        os.remove('output/exploration_out.txt')
        os.remove('output/grid_out.gif')
Ejemplo n.º 29
0
 def test_population_has_plotting_option(self):
     self.pop = Pop("test/test/parameters.txt")
     self.pop.create()
     try:
         self.pop.launch(dev='on')
     except TypeError as e:
         assert False, "allow for 'on' device to show plots"
     os.remove("output/vigilance_out.txt")
     os.remove("output/vigilance_out.gif")
     os.remove('output/resources_out.txt')
     os.remove('output/exploration_out.txt')
     os.remove('output/grid_out.gif')
Ejemplo n.º 30
0
    def test_fertility_increases_with_storage(self):
        self.fakepop = Pop("test/test/parameters.txt")
        self.fakepop.create()

        fertility = 0

        for ind in range(len(self.fakepop.individuals)):
            indiv = self.fakepop.individuals[ind]
            setattr(indiv, "storage", 1 + 9 * ind)
            indiv.reproduce(fecundity=2)
            assert indiv.fertility > fertility
            fertility = indiv.fertility
    def test_gathering_gives_individuals_storage(self):
        self.pop = Pop(par="test/test/parameters.txt")
        self.pop.create()
        self.pop.grid.share = np.full([self.pop.gridSize, self.pop.gridSize],
                                      0.8)

        for i in self.pop.individuals:
            i.vigilance = 0

        self.pop.gatherAndSurvive()

        for i in self.pop.individuals:
            assert i.storage > 0
    def test_only_live_individuals_gather_and_survive(self):
        self.pop = Pop("test/test/parameters.txt")
        self.pop.create()
        self.pop.explore()

        for ind in self.pop.individuals:
            ind.alive = False

        self.pop.gatherAndSurvive()

        for ind in self.pop.individuals:
            assert ind.alive == False
            assert ind.storage == 0
Ejemplo n.º 33
0
 def createMutations(self):
     """
         Mutate members of the current population which have
         been set aside as non-elite.
         NOTE: crossed-over genes have their new instances mutated,
               however the non-crossed-over non-elites will have
               their original genes (those in the current population)
               effected.
     """
     self.mutations = Population(genes=[], fitness=[])
     self.mutations.appendPopulation(self.crossed)
     self.mutations.appendPopulation(self.uncrossed)
     numMutations = (self.mutationPercent * self.mutations.size() * len(self.mutations.getGene(0)))
     #self.mutations.mutatePopulation(numMutations)
     self.mutations.mutatePopulationWithIncreaseAndCooling(numMutations, len(self.history))
Ejemplo n.º 34
0
    def next(self):
        """
            Performs the genetic algorithm on the previous population to construct
            a new population.
            @return: int  the current generation.
        """
        generation = len(self.history)
        # If this is the first iteration of the genetic algorithm,
        # a new, random population needs to be created.
        if len(self.history) == 0:
            self.initializeOriginalPopulation()
            self.history.append(self.currentPopulation.getGene(0))
            return generation
        # For >1 generations, perform the following steps:
        elif len(self.history) < self.generations:
            # Prepares an empty new population.
            self.newPopulation = Population(genes=[], fitness=[])

            # 1) Identify the elites.
            self.pullElites()
            # 2) Perform crossover on the non-elites.
            self.createCrossover()
            # 3) Perform mutations on the non-elites.
            self.createMutations()
            # 4) Construct a new population from the elites and non-elites.
            self.createNewPopulation()
            # 5) Assign the new population to the current population. 
            self.currentPopulation = self.newPopulation
            
            # Create a history of the highest scoring member of the
            # current population.
            self.history.append(self.currentPopulation.getGene(0))
            
            return generation
        else:
            raise StopIteration
Ejemplo n.º 35
0
class GeneticAlgorithm:
    """
        An iterator that implements the genetic algorithm 
        on a population of TurnList genes.
    """
    def __init__(self, sequenceOfValues, 
                 populationSize=100, 
                 generations=200,
                 elitePercent=.10, 
                 crossoverPercent=.80, 
                 mutationPercent=.25):
        self.sequenceOfValues   = sequenceOfValues                      # Values
        self.geneLength         = len(sequenceOfValues)                 # Length of a gene
        self.populationSize     = populationSize                        # Size of the Population
        self.generations        = generations                           # Number of generations to run.
        self.elitePercent       = elitePercent                          # Percent of genes to preserve as elite.
        self.crossoverPercent   = crossoverPercent                      # Percent of genes to crossover.
        self.mutationPercent    = mutationPercent                       # Percent of gene turns to mutate.
        
        self.currentPopulation  = Population(genes=[], fitness=[])      # Stores the current Population
        self.newPopulation      = Population(genes=[], fitness=[])      # Temporary Population used to construct next new population.

        self.elites             = Population(genes=[], fitness=[])      # The elites set aside while constructing the new population.
        self.nonElites          = Population(genes=[], fitness=[])      # The non-elites identified from the previous generation.
        # Crossed and Uncrossed have references to genes
        # from the previous generations - but they will be in their
        # mutated form.  Use these Population sets for access to
        # crossedover indexes and locations.
        self.crossed            = Population(genes=[], fitness=[])      # The genes which have been crossed over for the new generation.
        self.uncrossed          = Population(genes=[], fitness=[])      # The genes which where not crossed for the new generation.
        # Mutations is the final form of the current generation, minus the 
        # elites.  Use for the information about mutation locations.
        self.mutations          = Population(genes=[], fitness=[])      # The crossed and uncrossed genes combined for mutation.
        
        self.history            = []
    
    def __iter__(self):
        return self
    
    def next(self):
        """
            Performs the genetic algorithm on the previous population to construct
            a new population.
            @return: int  the current generation.
        """
        generation = len(self.history)
        # If this is the first iteration of the genetic algorithm,
        # a new, random population needs to be created.
        if len(self.history) == 0:
            self.initializeOriginalPopulation()
            self.history.append(self.currentPopulation.getGene(0))
            return generation
        # For >1 generations, perform the following steps:
        elif len(self.history) < self.generations:
            # Prepares an empty new population.
            self.newPopulation = Population(genes=[], fitness=[])

            # 1) Identify the elites.
            self.pullElites()
            # 2) Perform crossover on the non-elites.
            self.createCrossover()
            # 3) Perform mutations on the non-elites.
            self.createMutations()
            # 4) Construct a new population from the elites and non-elites.
            self.createNewPopulation()
            # 5) Assign the new population to the current population. 
            self.currentPopulation = self.newPopulation
            
            # Create a history of the highest scoring member of the
            # current population.
            self.history.append(self.currentPopulation.getGene(0))
            
            return generation
        else:
            raise StopIteration
    
    def initializeOriginalPopulation(self):
        """
            Creates an original population from the values provided
            to this GeneticAlgorithm. 
        """
        for __ in range(self.populationSize):
            valid = False
            while not valid: 
                # 1) Produce a sequence of turns using the MonteCarlo algorithm.
                sequenceOfTurns = montecarlo.getDefaultMCSequence(self.geneLength)
                # 2) Create a TurnList from the sequence of turns and
                #    their corresponding values.
                turnList = TurnList(sequenceOfTurns, self.sequenceOfValues)
                # 3) Determine if the sequence of turns is valid.
                if not turnList.toCoordinates().hasConflict():
                    self.currentPopulation.appendGene(turnList)
                    valid = True
    
    def pullElites(self):
        """
            Construct a set of elite members of the population and
            a set of non-elite members of the population.
        """
        numElites = int(self.populationSize * self.elitePercent)
        self.elites, self.nonElites = self.currentPopulation.splitElite(numElites)
    
    def createCrossover(self):
        """
            Apply Crossover to the non-elite members of the current
            population.
            NOTE: New instances of the genes are created.
        """
        numCrossovers = int(self.crossoverPercent * self.populationSize / 2 )
        #self.crossed, self.uncrossed = self.nonElites.createCrossoverPopulation(numCrossovers)
        self.crossed, self.uncrossed = self.nonElites.createCrossoverPopulationWithIncreaseAndCooling(numCrossovers, len(self.history))

    def createMutations(self):
        """
            Mutate members of the current population which have
            been set aside as non-elite.
            NOTE: crossed-over genes have their new instances mutated,
                  however the non-crossed-over non-elites will have
                  their original genes (those in the current population)
                  effected.
        """
        self.mutations = Population(genes=[], fitness=[])
        self.mutations.appendPopulation(self.crossed)
        self.mutations.appendPopulation(self.uncrossed)
        numMutations = (self.mutationPercent * self.mutations.size() * len(self.mutations.getGene(0)))
        #self.mutations.mutatePopulation(numMutations)
        self.mutations.mutatePopulationWithIncreaseAndCooling(numMutations, len(self.history))
        
    def createNewPopulation(self):
        """
            Combines the elite genes of the current generation
            with those non-elite genes which have been crossed over
            and mutated.  Stores them in a new population.
        """
        self.newPopulation = Population(genes=[], fitness=[])
        self.newPopulation.appendPopulation(self.elites)
        self.newPopulation.appendPopulation(self.mutations)
          
    def displayStateChanges(self):
        """
            A visually useful manner of inspecting the current generation of
            this Genetic Algorithm.
              * The first row references the indexes of the parents
                from which the current gene has been crossed-over.
              * The second row provides indexes for the genes of the current
                generation.  An "!" indicates the gene was produced from a crossover
                operation that did not complete successfully.  A "x" indicates
                that the gene was the product of a crossover operation.
              * The following rows indicate the turn state of the gene at a particular
                cell.  A turn surrounded by "<" and ">" indicates a cross-over location
                from the previous generation.  An "!" indicates a mutation created this cell.
              * The final row indicates the fitness of the gene.  A "*" indicates an elite
                gene that was carried over directly from the previous generation.
        """
        I = (len(str(self.populationSize-1)) * 2) + 1
        if I < 5:
            I = 5
        crossedGenes = self.crossed.crossedOverIndexes.keys()
        numElites = int(self.populationSize * self.elitePercent)
        results = "\n"
        #  Identify each gene's cross-over parents (if they had any). 
        for geneIndex in range(len(self.currentPopulation.genes)):
            if self.currentPopulation.genes[geneIndex] in crossedGenes:
                sourceIndexes = self.crossed.crossedOverIndexes[self.currentPopulation.genes[geneIndex]]
                results += str( str(sourceIndexes[0] + numElites) + "x" + str(sourceIndexes[1] + numElites) ).center(I) + "|"
            else:
                results += str(" " * I) + "|"
        results += '\n'
        #  Identify the gene's current index, whether it was the product of a crossover
        #  operation, and whether that crossover operation was "bad".
        for geneIndex in range(len(self.currentPopulation.genes)):
            badCross = False
            cross = False
            
            if self.currentPopulation.genes[geneIndex] in self.nonElites.cheapCrosses:
                badCross = True
            if self.currentPopulation.genes[geneIndex] in crossedGenes:
                cross = True
            
            placed = ""
            if badCross:
                placed += "!"
            placed += str(geneIndex)
            if cross:
                placed += "x"
            results += placed.center(I) + "|"
        results += '\n'
        #  A divider.
        for geneIndex in range(len(self.currentPopulation.genes)):
            results += str("-" * I) + "|"
        results += "\n"
        
        #  Displays rows for the cell values of each gene, indicating
        #  crossover points and mutations.
        for cellIndex in range(len(self.currentPopulation.genes[0])):
            for geneIndex in range(len(self.currentPopulation.genes)):
                currentGene = self.currentPopulation.genes[geneIndex]
                mutation = False
                crossed = False
                if (self.mutations.mutationLocations.has_key(currentGene) and
                    cellIndex in self.mutations.mutationLocations[currentGene]):
                    mutation = True
                if (currentGene in crossedGenes and 
                    self.crossed.crossedOverLocations[currentGene] == cellIndex):
                    crossed = True
                placed = ""
                if crossed:
                    placed += "<"
                placed += str(self.currentPopulation.genes[geneIndex].get(cellIndex)[0])
                if mutation:
                    placed += "!"
                if crossed:
                    placed += ">"
                results += placed.center(I) + "|"
            results += '\n'
        # Another divider.
        for geneIndex in range(len(self.currentPopulation.genes)):
            results += str("-" * I) + "|"
        results += "\n"
        
        # Display the fitness value for each gene, indicating whether the gene was elite
        # and carried directly from the previous generation.
        for geneIndex in range(len(self.currentPopulation.genes)):
            if self.currentPopulation.genes[geneIndex] in self.elites.genes:
                results += str(str(self.currentPopulation.fitness[geneIndex]) + "*").center(I) + "|"
            else:
                results += str(self.currentPopulation.fitness[geneIndex]).center(I) + "|"
        return results + '\n'