Ejemplo n.º 1
0
 def create_new_population(self, operations):
     # print("\nCreating initial population...")
     genome = Genome(operations[:])
     genome.score = calculate_fitness(genome.operations)
     self.genomes.append(genome)
     for _ in range(self.population_size):
         genome = Genome(shuffle_valid_genome(operations[:]))
         genome.score = calculate_fitness(genome.operations)
         self.genomes.append(genome)
Ejemplo n.º 2
0
    def crossover(self, parent1: Genome, parent2: Genome) -> Genome:
        child1: Genome = Genome(2, 2)
        child2: Genome = Genome(2, 2)

        for i in range(parent1.nvariables):
            if (random() < 0.5):
                child1.variable(i, parent1.variable(i))
                child2.variable(i, parent2.variable(i))
            else:
                child1.variable(i, parent2.variable(i))
                child2.variable(i, parent1.variable(i))
        self._population.add_genome(child1)
        self._population.add_genome(child2)
Ejemplo n.º 3
0
    def run(self, ngeneration: int, populationsize: int, crossoverrate: float,
            mutationrate: float, problem: ZDTOne):
        self._population = NonDominatingSortingPopulation()
        for generationcount in range(populationsize):
            gene: Genome = Genome(2, 2)
            gene.calculate_fitnesses(problem)
            self._population.add_genome(gene)

        self._population.rank()

        for generationcount in tqdm(range(ngeneration)):
            for nchildren in range(populationsize):
                parent1: Genome = self.tournament_selection(populationsize)
                parent2: Genome = self.tournament_selection(populationsize)

                rand = random()

                if (rand < crossoverrate):
                    self.crossover(parent1, parent2)
                else:
                    self._population.add_genome(parent1)
                    self._population.add_genome(parent2)

                if (random() < mutationrate):
                    self.mutate(self._population.get_genome(-1))
                    self.mutate(self._population.get_genome(-2))

                self._population.get_genome(-1).calculate_fitnesses(problem)
                self._population.get_genome(-2).calculate_fitnesses(problem)

            self._population.rank()
            self._population.truncate(populationsize)

        return self._population
Ejemplo n.º 4
0
    def initialize_population(self, population=None, size=None):
        if population is not None:
            self.previous_generation = population
            self.max_id = len(population.keys())
        elif size is not None:
            self.max_id = self.config.POPULATION_SIZE
            pop_size = self.config.POPULATION_SIZE
            input_size = size[0]
            output_size = size[1]

            for genome_id in range(pop_size):
                genome = Genome(genome_id)

                node_id = 0
                # Input Nodes
                for _ in range(input_size):
                    node_gene = self.random.choice(self.node_classes)(node_id, NodeType.INPUT)
                    genome.add_node_gene(node_gene)
                    node_id += 1
                
                # Output Nodes
                for _ in range(output_size):
                    node_gene = self.random.choice(self.node_classes)(node_id, NodeType.OUTPUT)
                    genome.add_node_gene(node_gene)
                    node_id += 1
                
                # Connect each input to every other output
                for in_id in range(input_size):
                    for out_id in range(output_size):
                        connection = ConnectionGene(in_id, out_id + input_size, 1.0, True, self.innovator.next_innovation_number((in_id, out_id)))
                        genome.add_connection_gene(connection)

                self.previous_generation[genome_id] = genome
        else:
            raise ValueError("Invalid Parameters")
def create_asexual_genome(parent,
                          mutation_tracker,
                          newNodeProb=0.03,
                          newConnectionProb=0.05,
                          alterConnectionProb=0.8,
                          newConnectionValueProb=0.1):

    new_c_genes = {}
    new_n_genes = {}
    # clone the parent
    for key, value in parent.n_genes.items():
        new_n_genes[key] = copy.deepcopy(value)
    for key, value in parent.c_genes.items():
        new_c_genes[key] = copy.deepcopy(value)

    # apply mutation to all connection
    for c_key in new_c_genes:
        if not new_c_genes[c_key].disable:
            if np.random.uniform(0, 1) < alterConnectionProb:
                new_c_genes[c_key] = alter_connection(new_c_genes[c_key],
                                                      newConnectionValueProb)

    child_genome = Genome(parent.input_size, parent.output_size, new_n_genes,
                          new_c_genes, parent.generation + 1,
                          [parent.species_id])

    # apply new nodes mutation
    if np.random.uniform(0, 1) < newNodeProb:
        add_node_mutation(child_genome, mutation_tracker)
    # apply new connection mutation
    if np.random.uniform(0, 1) < newConnectionProb:
        add_connection_mutation(child_genome, mutation_tracker)

    return child_genome
Ejemplo n.º 6
0
	def create_members(self):
		nkey = str(self.niecheID)
		self.nieches[nkey] = Nieche(self.niecheID)
		
		for i in range(self.members):
			# Create a new genome
			gkey = str(self.genome_id)
			self.genomes[gkey] = Genome(
				weight_mutation = self.weight_mutation,
				input_nodes = self.inputs,
				output_nodes = self.outputs,
				genome_id = self.genome_id)
			
			# Create input, output nodes, and connect them
			self.genomes[gkey].create_inputs()
			self.genomes[gkey].create_outputs()
			self.genomes[gkey].create_innitial_connections()
			
			# Tell which nieche this node is belongs to, at init all belongs to the same, the 0
			self.genomes[gkey].set_nieche_id(self.niecheID)

			# At init add every member to the first nieche/species
			self.nieches[nkey].add_member(self.genome_id)
			
			# Increment ID
			self.genome_id += 1

		#The newly created Genomes has init innovations, connection between outputs and inputs
		#which we need to group together
		self.group_innovations()

		self.niecheID += 1
Ejemplo n.º 7
0
 def initial_generation(self):
     initial_genomes = []
     for i in range(self.population):
         g = Genome()
         g.solution = random_solution(self.genome_length)
         initial_genomes.append(g)
     return Generation(initial_genomes, self.answer)
Ejemplo n.º 8
0
    def conceive(self, mother: Genome, father: Genome,
                 prisoner_cb: Callable[[Prisoner], None]) -> None:
        # Construct duplicates of the mother and father.
        mother = Genome(mother.genes)
        father = Genome(father.genes)

        # Perform a uniform crossover.
        mother.crossover(father, Genetics.__uniform_crossover)

        # Mutate both sets of genes.
        mother.mutate(self.__uniform_mutation)
        father.mutate(self.__uniform_mutation)

        # Pass the two children to the callback.
        prisoner_cb(Prisoner(mother))
        prisoner_cb(Prisoner(father))
def create_new_genome(input_size, output_size, fully_connected=False):
    nodes_genes = {}
    for i in range(0, input_size):
        nodes_genes[i] = NodeGene(input_nodes=None,
                                  output_nodes=[],
                                  neuron_type='i')
    for j in range(input_size, input_size + output_size):
        nodes_genes[j] = NodeGene(input_nodes=[],
                                  output_nodes=[],
                                  neuron_type='o')

    cpt = input_size + output_size
    connection_genes = dict()
    if fully_connected:
        for i in range(0, input_size):
            for j in range(input_size, input_size + output_size):
                connection_genes[i,
                                 j] = ConnectionGene(cpt,
                                                     Mutation.get_new_weight(),
                                                     False)
                nodes_genes[i].output_nodes.append(j)
                nodes_genes[j].input_nodes.append(i)
                cpt += 1

    return Genome(input_size=input_size,
                  output_size=output_size,
                  nodes_genes=nodes_genes,
                  connection_genes=connection_genes,
                  generation=0,
                  parents_species_id=[])
Ejemplo n.º 10
0
    def run(self):

        population = []
        best_genome = None
        data = []

        for _ in range(population_total):
            genome_dna = generate(problem_grid)
            population.append(Genome(genome_dna))

        for i in range(simulations):
            population_fitness = 0

            for genome in population:
                genome.fitness()
                population_fitness += genome.getFitness()

            sorted_population = population.copy()
            best_genome = max(population, key=operator.attrgetter('fit'))
            best_fitness = round(1 / best_genome.getFitness())
            data.append(best_fitness)

            if i % 1000 == 0:
                self.show(i, best_genome)

            if best_fitness <= limit:
                print('DONE\n')
            population.clear()
            population.append(best_genome)

            while len(population) < population_total:

                new_genome = tournamentSelection(sorted_population)
                option_2 = tournamentSelection(sorted_population)

                if npR.uniform() < crossover_rate:
                    option_3 = tournamentSelection(sorted_population)
                    dna_1 = crossover(new_genome, option_2, option_3)
                    new_genome = Genome(dna_1)

                if npR.uniform() < mutation_rate:
                    new_genome.mutateSell(problem_grid)

                population.append(new_genome)

        self.show(i, best_genome)
        return data
Ejemplo n.º 11
0
def initializePool(env):
    pool = Pool(env)
    for i in range(0, Population):
        basic = Genome(pool)
        basic.basicGenome()
        pool.addToSpecies(basic)

    pool.initializeRun()
    return pool
Ejemplo n.º 12
0
	def get_child(self, parent1, parent2):
		new_genome = Genome(weight_mutation=self.weight_mutation, input_nodes=self.inputs, output_nodes=self.outputs, genome_id=self.genome_id)
		self.genome_id += 1

		fitness1 = parent1.get_fitness()
		fitness2 = parent2.get_fitness()

		if(fitness1 > fitness2):
			genome1 = parent1
			genome2 = parent2
		else:
			genome1 = parent2
			genome2 = parent1

		for conn1 in genome1.get_connection_genes():
			copy_con1 = copy.deepcopy(conn1)

			excessGene = True
			disjointGene = False
			newConn = 0
			for conn2 in genome2.get_connection_genes():
				copy_con2 = copy.deepcopy(conn2)
				# Both have connection with this innovation number
				if(conn1.get_innovation_number() == conn2.get_innovation_number()):
					excessGene = False
					# The expressed parameter is not matching
					if(conn1.expressed != conn2.expressed):
						disjointGene = True
					# Get the deltaWeight, because everything seems to be in order
					else:
						newConn = copy_con1 if(randint(0,1) == 1) else copy_con2
			
			if(excessGene == True):
				new_genome.connection_genes.append(copy_con1)
			elif(disjointGene == True):
				new_genome.connection_genes.append(copy_con1)
			else:
				new_genome.connection_genes.append(newConn)

			#Add node
			#nodeIn, nodeOut = copy.deepcopy(new_genome.connection_genes[-1].get_connected_nodes())
			nodeInID, nodeOutID = copy.copy(new_genome.connection_genes[-1].get_connected_nodes_id())

			#Add node if there wasnt any node like this before
			if(new_genome.get_node_by_id(nodeInID) == False):
				node = copy.deepcopy(genome1.get_node_by_id(nodeInID))
				new_genome.node_genes.append(node)
			if(new_genome.get_node_by_id(nodeOutID) == False):
				node = copy.deepcopy(genome1.get_node_by_id(nodeOutID))
				new_genome.node_genes.append(node)

			# Get the higher global node id
			copy_gnID1 = copy.copy(parent1.global_node_id)
			copy_gnID2 = copy.copy(parent2.global_node_id)
			new_genome.global_node_id = copy_gnID1 if(copy_gnID1>copy_gnID2) else copy_gnID2

		return new_genome
Ejemplo n.º 13
0
class UnitOfWork:
    
    _dataSet=[]
    _genomes=[Genome('Num_units',20,50),
              Genome('learning_rate',0. 0020,0.0030),
              Genome('lambda_loss_amount',0.0010,0.0020),
              Genome('Batch_size',1000,2000),
              Genome('Num_iterations',100,500),
              Genome('Segment_size',100,200),
              ]
    _popSize=10
    _perMut=0.5
    _iteration=22
    
# _test_user_ids=[2, 4, 9, 10, 12, 13, 18, 20, 24] in DataSet

    def __init__(self, pathDataset='datasets/uci_raw_data'):
        _genomes=[]
        self._dataSet=DataSet(pathDataset,'l')  
Ejemplo n.º 14
0
    def reproduce_population(self):
        """
        The miracle of life
        This method will take two random parents and create two children from
        them.
        """
        first_child, second_child = self.mate()
        mutate_genome(first_child)
        mutate_genome(second_child)

        first_genome = Genome(first_child)
        second_genome = Genome(second_child)
        first_genome.score = calculate_fitness(first_child)
        second_genome.score = calculate_fitness(second_child)

        self.genomes.append(first_genome)
        self.genomes.append(second_genome)
        self.sort_population()
        self.reap_population()
Ejemplo n.º 15
0
 def apply(self, mutant_vector, target_vector, Cr):
     genes = []
     for j in range(0, len(mutant_vector.get_genes())):
         randji = random.uniform(0, 1)
         Jrand = random.randint(0, len(mutant_vector.get_genes()))
         if randji <= Cr or j == Jrand:
             genes.append(mutant_vector.get_genes()[j])
         else:
             genes.append(target_vector.get_genes()[j])
     uig = Genome(genes)
     return uig
Ejemplo n.º 16
0
    def __init__(self, psize, bounds):
        self.population_list = []

        i = 0
        while i < psize:
            genes = []
            for j in bounds:
                genes.append(random.uniform(j[0], j[1]))
            new_genome = Genome(genes)
            self.population_list.append(new_genome)
            i = i + 1
Ejemplo n.º 17
0
    def tournament(self, prev_generation):
        new_genome = Genome()

        # pick two parents
        parent_genome_1 = pick_parent(prev_generation)
        parent_genome_2 = pick_parent(prev_generation)

        new_genome.solution = crossover(parent_genome_1, parent_genome_2)
        new_genome.solution = mutate(new_genome.solution, self.mutation_rate)

        return new_genome
Ejemplo n.º 18
0
    def breedChild(self):
        child = Genome()
        if random.random() < CrossoverChance:
            g1 = self.genomes[random.randint(1, len(self.genomes))]
            g2 = self.genomes[random.randint(1, len(self.genomes))]
            child = g1.crossover(g2)
        else:
            g = self.genomes[random.randint(1, len(self.genomes))]
            child = g.clone()

        child.mutate()
        return child
Ejemplo n.º 19
0
	def get_clone(self, genome):
		# Create a child
		child = Genome(weight_mutation=self.weight_mutation, input_nodes=self.inputs, output_nodes=self.outputs, genome_id=self.genome_id)
		self.genome_id += 1

		# Copy the parents genes to the child
		child.connection_genes = copy.deepcopy(genome.connection_genes)
		child.node_genes = copy.deepcopy(genome.node_genes)
		child.global_node_id = copy.copy(genome.global_node_id)

		# I dont have to mutate the child, it will be mutated all together.

		return child
Ejemplo n.º 20
0
 def __init__(self):
     assert INPUTS != 0 and OUTPUTS != 0, "You must call the initialize method before creating players!"
     self.fitness = -1
     self.unadjustedFitness = -1
     self.brain = Genome(INPUTS, OUTPUTS, False)
     self.vision = []
     self.actions = []
     self.lifespan = 0
     self.dead = False
     self.replay = False
     self.gen = 0
     self.name = ""
     self.speciesName = "Not yet defined"
Ejemplo n.º 21
0
 def apply(self, mutant_vector, target_vector, Cr):
     genes = []
     for j in range(0, len(mutant_vector.genes)):
         max_bounds = self.bounds[j][1]
         min_bound = self.bounds[j][0]
         res = target_vector.get_genes()[j] + Cr  * (mutant_vector.get_genes()[j] - target_vector.get_genes()[j])
         if res >= max_bounds:
             res = max_bounds
         if res <= min_bound:
             res = min_bound
         genes.append(res)
         genes.append(res)
     uig = Genome(genes)
     return uig
Ejemplo n.º 22
0
def generate_complete_genome(id, n, r, ir):
    g = Genome(id)

    for i in range(n):
        g.add_node_gene(TestNode(i, r.choice(list(NodeType))))

    for i in range(n):
        for j in range(i, n):
            w = r.random()
            a = r.random() < 0.7
            inr = ir.next_innovation_number((i, j))
            g.add_connection_gene(ConnectionGene(i, j, w, a, inr))

    return g
Ejemplo n.º 23
0
    def __init__(self, genome_1: Genome, genome_2: Genome):
        """
        Constructor

        Parameters
        ----------
        genome_1
            First genome for distance calculation
        genome_2
            Genome to test distance from the first one
        """
        gene_count_1: int = 0
        gene_count_2: int = 0
        self.genome_1: Genome = Genome(list())  # Genome 1 as String
        self.genome_2: Genome = Genome(list())  # Genome 2 as String

        for chromosome in genome_1.chromosomes:
            if len(chromosome.genes) != 0:
                gene_count_1 += len(chromosome.genes)
                self.genome_1.add_chromosome(chromosome)

        for chromosome in genome_2.chromosomes:
            if len(chromosome.genes) != 0:
                gene_count_2 += len(chromosome.genes)
                self.genome_2.add_chromosome(chromosome)

        if gene_count_1 == gene_count_2:  # Algorithm requires genomes are equal length
            self.gene_count: int = gene_count_1  # Number of genes
        else:
            raise Exception("Different numbers of genes in both genomes.\n")

        self.node_ints: ndarray = npzeros(self.gene_count * 2)
        self.node_strings_1: List[Optional[str]] = list()
        self.node_strings_2: List[Optional[str]] = list()
        self.genome_paths_1: List[Optional[BPGPath]] = list()
        self.genome_paths_2: List[Optional[BPGPath]] = list()
        self.distance: int = int()
Ejemplo n.º 24
0
    def run(self):
        for _ in range(population_total):
            scramble = shuffle(key)
            self.population.append(Genome(scramble))

        for _ in range(100000):
            #print('GENERATION :', i, round(1/population[0].getFitness()))
            population_fitness = 0

            for genome in self.population:
                genome.fitness(row_score, col_score)
                population_fitness += genome.getFitness()

            for genome in self.population:
                genome.setFitness2Population(population_fitness)

            sorted_population = sorted(
                self.population, key=operator.attrgetter('fitness_ratio'))
            sorted_population.reverse()
            best = sorted_population[0]
            sorted_population.append(best)
            if 1 / best.getFitness() <= 10:
                break
            self.population.clear()

            for _ in range(population_total - 1):

                new_genome = rouletteSelection(sorted_population)
                option_2 = rouletteSelection(sorted_population)

                if npR.uniform() < crossover_rate:
                    new_genome = Genome(crossover(new_genome, option_2))

                if npR.uniform() < mutation_rate:
                    new_genome.mutate()

                self.population.append(new_genome)
Ejemplo n.º 25
0
 def apply(self, target_vector):
     genes = []
     res_SO = SOperator(self.population_list).apply(target_vector)
     x1 = res_SO[1]
     x2 = res_SO[2]
     global F
     for i in range(0, len(target_vector.genes)-1):
         max_bound = self.bounds[i][1]
         min_bound = self.bounds[i][0]
         res = target_vector.genes[i] + F * (x1.genes[i] - x2.genes[i])
         if res >= max_bound:
            res = max_bound
         if res <= min_bound:
             res = min_bound
         genes.append(res)
     vig = Genome(genes)
     return vig
Ejemplo n.º 26
0
def generate_genome(id, n, r, ir):
    g = Genome(id)

    max_c = int(n * (n - 1) / 2)
    c = r.randint(max_c - 1, max_c)

    for i in range(n):
        g.add_node_gene(TestNode(i, r.choice(list(NodeType))))

    for _ in range(c):
        i = r.randint(0, n - 1)
        o = r.randint(0, n - 1)
        w = r.random()
        a = r.random() < 0.7
        inr = ir.next_innovation_number((i, o))
        g.add_connection_gene(ConnectionGene(i, o, w, a, inr))

    return g
Ejemplo n.º 27
0
    def loadFile(self, filename, env):
        file = open(filename, "r")
        self.__init__(env)
        self.generation = int(file.readline().replace("\n", ""))
        self.maxFitness = int(file.readline().replace("\n", ""))
        #gui.settext(5, 8, maxFitnessLabel, "Max Fitness. " .. math.floor(pool.maxFitness))
        numSpecies = int(file.readline().replace("\n", ""))
        for s in range(0, numSpecies):
            species = Species()
            self.species.append(species)
            species.topFitness = float(file.readline().replace("\n", ""))
            species.staleness = int(file.readline().replace("\n", ""))
            numGenomes = int(file.readline().replace("\n", ""))
            for g in range(0, numGenomes):
                genome = Genome(self)
                species.genomes.append(genome)
                genome.fitness = float(file.readline().replace("\n", ""))
                genome.maxneuron = int(file.readline().replace("\n", ""))
                line = file.readline().replace("\n", "")
                while line != "done":
                    genome.mutationRates[line] = float(file.readline().replace(
                        "\n", ""))
                    line = file.readline().replace("\n", "")
                numGenes = int(file.readline().replace("\n", ""))
                for n in range(0, numGenes):
                    gene = Gene()
                    genome.genes.append(gene)
                    enabled = 0
                    line = file.readline()
                    data = []
                    for x in [x for i, x in enumerate(line.split(" "))]:
                        try:
                            data.append(int(x))
                        except ValueError:
                            data.append(float(x))
                    gene.into, gene.out, gene.weight, gene.innovation, enabled = data

                    gene.enabled = enabled == 1
        file.close()

        while self.fitnessAlreadyMeasured():
            self.nextGenome()
        self.initializeRun()
        self.currentFrame = self.currentFrame + 1
Ejemplo n.º 28
0
    def __init__(self, t_population, input_n, output_n, target_fitness):

        self.t_pop = t_population
        self.input_n = input_n
        self.output_n = output_n
        self.innov_dict = {}
        self.id_dict = {}
        # id_dict is dictionary that has innovation number of connections as key and the id
        # number of node created by dividing the connection as value of the key
        self.id_timer = {}
        self.population = []
        self.species = []
        self.species_fitness_counter = []
        self.species_max_fitness = []
        self.population_max = 0
        self.population_max_timer = 0
        self.best_genome = Genome(
        )  # The fittest genome to ever exist in the population
        self.target = target_fitness
Ejemplo n.º 29
0
    def start_genomes(self, gene_pool: GenePool,
                      conditions: Conditions) -> List[Genome]:
        """
        Creates the starter genomes
        :param conditions: The conditions to use when creating new genes
        :param gene_pool: The gene pool to update with the starter genomes
        :return: A list of starter genomes
        """
        in_size = self.simulation.get_data_size()
        out_size = self.simulation.get_controls_size()

        for in_ in range(1, in_size + 1):
            gene_pool.node_depths[in_] = conditions.app_start_node_depth

        for out_ in range(0, -out_size, -1):
            gene_pool.node_depths[out_] = conditions.app_end_node_depth

        starter_genomes = []

        starter_genes = []
        for in_ in range(1, in_size + 1):
            for out_ in range(0, -out_size, -1):
                gene = Gene(random.random() *
                            (self.conditions.gene_max_weight -
                             self.conditions.gene_min_weight) +
                            self.conditions.gene_min_weight,
                            in_,
                            out_,
                            0,
                            gene_pool=gene_pool)
                starter_genes.append(gene)

        for i in range(self.conditions.population_size):
            new_genes = [gene.copy() for gene in starter_genes]
            for gene in new_genes:
                gene.weight = (random.random() *
                               (self.conditions.gene_max_weight -
                                self.conditions.gene_min_weight) +
                               self.conditions.gene_min_weight)
            starter_genomes.append(
                Genome(new_genes, in_size, out_size, gene_pool))

        return starter_genomes
Ejemplo n.º 30
0
    def run(population: int, generations: int, mutation_rate: float, tests: int, seed: int) -> None:
        random.seed(seed)

        # Population of prisoners -- modified each generation.
        prisoners = [Prisoner(Genome()) for _ in range(population)]
        # Controls mutation, crossover, and re-population.
        genetics = Genetics(mutation_rate)

        for generation in range(0, generations):
            costs = [0] * population  # Initialize costs to zero each generation.
            random.shuffle(prisoners)  # De-segregate parents and children.

            Simulation.__assess_population(prisoners, generation)

            # Group up each prisoner with a random unique partner.
            for i in range(0, population, 2):
                # Perform the prisoner's dilemma test with the two subjects N times.
                for k in range(0, tests):
                    Simulation.__cost(prisoners, costs, i, i + 1)

            # Sort the prisoners by cost, in ascending order.
            order = {v: i for i, v in enumerate(prisoners)}
            prisoners.sort(key=lambda x: costs[order[x]])

            i = 0
            cutoff_limit = population // 2

            # Called when a new child is born and added to the population.
            def born_child_cb(child: Prisoner) -> None:
                nonlocal i
                # Replace an under-performing prisoner with a new child.
                prisoners[cutoff_limit + i] = child
                i += 1

            while i in range(0, cutoff_limit):  # Loop incremented via callback.
                j = random.randrange(i + 1, cutoff_limit)
                mother, father = prisoners[i], prisoners[j]
                mother.mature()
                father.mature()
                # Switch the father with the mother's right neighbor.
                prisoners[j] = prisoners[i + 1]
                prisoners[i + 1] = father
                genetics.conceive(mother.genome, father.genome, born_child_cb)