def mate(self, parentOne, parentTwo): """Accepts two parents and returns ONE offspring; if the offspring is unchanged from one of the parents, the fitness values are not re-evaluated, just copied. Only one category of mutation is performed on a single offspring.""" # one parent will form the basis for the new offspring; the other is just there for # potential crossover (by default the mother will become the offspring) mother, father = parentOne.copy(), parentTwo.copy() # fitEval will be set to True if any mutations occur that make the offspring different from # its copied parent; this way we avoid unnecessary fitness evaluations fitEval = False # only one category of mutation is allowed per mating event r = urand() if r < self.pC: # parental crossover fitEval = True mNode = rchoice(mother.tree.getNodes()) fNode = rchoice(father.tree.getNodes()) CGAGenerator.single_crossover(mNode,fNode) elif r < self.pC + self.pHC: # headless chicken crossover fitEval = True mNode = rchoice(mother.tree.getNodes()) rNode = rchoice(self.initialize_tree().getNodes()) CGAGenerator.single_crossover(mNode,rNode) elif r < self.pC + self.pHC + self.pM: # point mutation (uses pM/node for mutation prob.) fitEval = True for n in mother.tree.getNodes(): if urand() < self.pM: CGAGenerator.point_mutate(mother.tree, n) fitEval = True elif r < self.pC + self.pHC + self.pM + self.pP: # pruning - guaranteed to do one pruning operation fitEval = True mNode = rchoice(mother.tree.getNodes()) CGAGenerator.prune(mother.tree,mNode) elif r < self.pC + self.pHC + self.pM + self.pP + self.pG: # growth - guaranteed to do one growth op. fitEval = True mTerm = rchoice(mother.tree.getTermini()) CGAGenerator.grow(mother.tree,mTerm) else: # offspring will just be a copy pass if fitEval: mother.fitness,mother.parsimony,mother.finitewts = self.evaluate_fitness(mother.tree) return mother