def __ramped_half_and_half(self, population_size, max_depth, funcs, terms):
        """Creates a population with Ramped half-and-half initialization

        Arguments:
            population_size {int} -- Size of the population.
            max_depth {int} -- Max depth of a tree contained in an individual
            funcs {[object]} -- List of functions to be used in individual
                                creation
            terms {[string]} -- List of terminals to be used in individual
                                creation

        Returns:
            [Individual] -- List of individual, i.e., the population.
        """
        pop = []
        group = (population_size / (max_depth - 1))
        full = True

        for i in range(2, max_depth + 1):
            for j in range(int(group)):
                pop.append(
                    Individual(self.__gen_rnd_expr(funcs, terms, i, full)))
                full = not full

        for i in range(population_size % (max_depth - 1)):
            pop.append(
                Individual(self.__gen_rnd_expr(funcs, terms, max_depth, full)))

        return pop
class TestIndividual(TestCase):
    p = random.randint(2, 10)
    G = get_graph('../data/SJC1.dat')
    i1 = Individual(p, G)
    i2 = Individual(p, G)

    def testMutation(self):
        i3 = mutate(self.i1, self.G)
        intersection = self.i1.chromosome.intersection(i3.chromosome)
        self.assertEqual(len(i3.chromosome), self.p)
        self.assertEqual(len(intersection), self.p - 1)
        self.assertNotEqual(self.i1, i3)

    def testCrossover(self):
        (i3, i4) = crossover(self.i1, self.i2)
        union1 = self.i1.chromosome.union(self.i2.chromosome)
        union2 = i3.chromosome.union(i4.chromosome)
        self.assertEqual(union1, union2)
        self.assertEqual(len(i3.chromosome), self.p)
        self.assertEqual(len(i4.chromosome), self.p)

    def testCrossoverSameIndividuals(self):
        (i3, i4) = crossover(self.i1, self.i1)
        self.assertEqual(i3, self.i1)
        self.assertEqual(i4, None)

    def testCrossoverDiffIndividuals(self):
        (i3, i4) = crossover(self.i1, self.i2, c=3)
        self.assertNotEqual(i3, i4)
        union1 = self.i1.chromosome.union(self.i2.chromosome)
        union2 = i3.chromosome.union(i4.chromosome)
        self.assertEqual(union1, union2)
        self.assertEqual(len(i3.chromosome), self.p)
        self.assertEqual(len(i4.chromosome), self.p)
Exemple #3
0
    def crossover(self):
        new_generation = [self.fittest()]
        for index in range(len(self.individuals) - 1):
            rnd = random.random()
            if rnd <= Population.cp:
                child = crossovers.aox(self.individuals[index].chromosome,
                                       self.individuals[index + 1].chromosome)
                new_generation.append(Individual(child))
            else:
                if rnd <= 0.5:
                    new_generation.append(self.individuals[index])
                else:
                    new_generation.append(self.individuals[index + 1])
        rnd = random.random()

        # crossover du dernier et premier
        if rnd <= Population.cp:
            child = crossovers.aox(self.individuals[-1].chromosome,
                                   self.individuals[0].chromosome)
            new_generation.append(Individual(child))
        else:
            if rnd <= 0.5:
                new_generation.append(self.individuals[-1])
            else:
                new_generation.append(self.individuals[0])

        self.individuals = new_generation
Exemple #4
0
def reproduce(population):
    """Applies the evolutionary survival process to the population.

    A few select elites with the best reward (fitness) is automatically part of the next generation.
    From the population parents are selected to create offspring. The offspring will be part of
    the next generation. The parents will cease to exist unless they were part of the elite.
    Mutation is applied to the offspring, which has a chance of altering the chromosomes.

    Args:
        The population which is a list of the object Individual. The entire population just ran in
        the simulation and their reward (fitness) was stored.

    Returns:
        A new population which is a list of the object Individual. The new population is the next generation
        and consists of the elite from the previous population and offspring from the previous generation.
    """

    # Sort the population by its reward (fitness)
    population = sorted(population, key=lambda x: x.reward, reverse=True)
    total_remove = 94

    if judgement_day(population):
        new_population = [population[0]]

        for i in range(100 - len(new_population)):
            new_population.append(random_neural_network())

        return new_population

    # Elitism
    new_population = population[:(len(population) - total_remove)]

    # Selection, may be replaced by tournament_selection method
    parent_1, parent_2 = wheel_selection(total_remove, population)

    for i in range(int(total_remove / 2)):
        weight_1, weight_2 = uniform_crossover(parent_1[i].chromosome_1,
                                               parent_2[i].chromosome_1)
        bias_1, bias_2 = uniform_crossover(parent_1[i].chromosome_2,
                                           parent_2[i].chromosome_2)

        mutate(weight_1)
        mutate(weight_2)
        mutate(bias_1)
        mutate(bias_2)

        c1 = Individual()
        c2 = Individual()

        c1.chromosome_1 = weight_1
        c1.chromosome_2 = bias_1

        c2.chromosome_1 = weight_2
        c2.chromosome_2 = bias_2

        new_population.append(c1)
        new_population.append(c2)

    return new_population
 def test_fight(self):
     ind1 = Individual("01101110")
     ind2 = Individual("00111000")
     fight1 = ind2.fight(ind1)
     self.assertEqual(ind1.fitness, 1)
     self.assertEqual(ind2.fitness, 0)
     fight2 = ind1.fight(ind2)
     self.assertEqual(fight1, False)
     self.assertEqual(fight2, False)
Exemple #6
0
def crossover(parent_1, parent_2):
    offspring_1, offspring_2 = Individual(), Individual()
    # first offspring
    offspring_1.chromosome[0] = parent_1.chromosome[0]
    offspring_1.chromosome[1] = parent_2.chromosome[1]
    # second offspring
    offspring_2.chromosome[0] = parent_2.chromosome[0]
    offspring_2.chromosome[1] = parent_1.chromosome[1]
    return [offspring_1, offspring_2]
Exemple #7
0
    def parseFile(self, filePath):
        """
			Parses the output file from the evolution process to extract 
			info about the individuals and their fitness value
		"""
        print 'Parsing...'

        # Open the file and read the contents
        file = open(filePath, 'r')
        fileContents = file.read()
        file.close()

        # Parse the xml data
        xmlfile = parseString(fileContents)

        # Getting population data:
        # ---------------------------------------------------------------------------------------------
        xmlPopulation = xmlfile.getElementsByTagName('Deme')[0]

        # Create new individuals list:
        self.individuals = []

        # Load the hall of fame individuals
        self.individuals.append(
            Individual('HallOfFame',
                       xmlPopulation.getElementsByTagName('Individual')[0]))

        # Load the population individuals
        for xmlIndividual in xmlPopulation.getElementsByTagName(
                'Individual')[1:]:
            self.individuals.append(
                Individual('Individual' + str(len(self.individuals)),
                           xmlIndividual))

        # Set the info about the loaded individuals on listBox
        self.listBoxIndividuals.Set([str(i) for i in self.individuals])

        # Getting scene file / log file filepath
        # ----------------------------------------------------------------------------------------------
        xmlSceneFile = xmlfile.getElementsByTagName('Registry')[0]
        found = False
        for entry in xmlSceneFile.getElementsByTagName('Entry'):
            if entry.getAttribute('key') == 'robot.simulationfile':
                self.robotSceneFile = entry.childNodes[0].data
                #print self.robotSceneFile
                if found:
                    break
                else:
                    found = True

            if entry.getAttribute('key') == 'log.filename':
                self.logFile = entry.childNodes[0].data

                if found:
                    break
                else:
                    found = True
Exemple #8
0
    def test_is_alive(self):
        indi1 = Individual('1')
        indi2 = Individual('2')
        indi1.alive = False
        indi2.alive = True
        individuals = {'1': indi1, '2': indi2}

        self.assertFalse(is_alive(individuals, '1'))
        self.assertTrue(is_alive(individuals, '2'))
    def partialMappedCrossover(self):
        tmp_pop = []
        random.shuffle(self.population)

        while len(tmp_pop) < len(self.population):
            if random.random() < self.crossoverProb:
                pos = [
                    random.randint(0, self.geneSize - 1),
                    random.randint(0, self.geneSize - 1)
                ]
                pos.sort()

                p1 = self.population[random.randint(0,
                                                    len(self.population) -
                                                    1)].gene
                p2 = self.population[random.randint(0,
                                                    len(self.population) -
                                                    1)].gene

                ch1 = p2[pos[0]:pos[1]]
                ch2 = p1[pos[0]:pos[1]]

                tmp1 = []
                tmp2 = []

                o1 = []
                o2 = []

                for locus in p1:
                    if locus in ch1:
                        tmp1.append(locus)
                for locus in p2:
                    if locus in ch2:
                        tmp2.append(locus)

                for locus in p1:
                    if not locus in ch1:
                        tmp1.append(locus)
                    else:
                        tmp2.pop(0)
                for locus in p2:
                    if not locus in ch2:
                        tmp2.append(locus)
                    else:
                        tmp1.pop(0)

                o1 = tmp1[:pos[0]] + ch1 + tmp1[pos[0]:]
                o2 = tmp2[:pos[0]] + ch2 + tmp2[pos[0]:]

                np1 = Individual(self.geneSize)
                np2 = Individual(self.geneSize)
                np1.initialization(o1)
                np2.initialization(o2)
                tmp_pop.append(np1)
                tmp_pop.append(np2)

        self.offspring = tmp_pop
Exemple #10
0
 def reproduce(self):
     for pair in self.mating.pairs:
         first_child_genes, second_child_genes = self.pmx(
             pair.individual1.genes, pair.individual2.genes)
         first_child = Individual(genes=first_child_genes)
         second_child = Individual(genes=second_child_genes)
         self.new_generation.append(first_child)
         self.new_generation.append(second_child)
     self.mating.selection.population.individuals.extend(
         self.new_generation)
    def uniform_like_crossover(self, parent1, parent2):
        """ Crea dos hijos a partir de dos padres """

        child1 = Individual(parent1.size, [-1] * parent1.size)
        child2 = Individual(parent1.size, [-1] * parent1.size)

        self.generate_child(child1, parent1, parent2)
        self.generate_child(child2, parent1, parent2)

        return child1, child2
Exemple #12
0
	def reproduce(self):
		for pair in self.mating.pairs:
			x = random.randint(2, Settings.BOARD_SIZE/2)
			first_child_genes = pair.individual1.genes[:x] + pair.individual2.genes[x:]
			second_child_genes = pair.individual2.genes[:x] + pair.individual1.genes[x:]
			first_child = Individual(genes=first_child_genes)
			second_child = Individual(genes=second_child_genes)
			self.new_generation.append(first_child)
			self.new_generation.append(second_child)
		self.mating.selection.population.individuals.extend(self.new_generation)
def reproduce(population):
    """Applies the evolutionary survival process to the population.

    A few select elites with the best reward (fitness) is automatically part of the next generation.
    From the population parents are selected to create offspring. The offspring will be part of
    the next generation. The parents will cease to exist unless they were part of the elite.
    Mutation is applied to the offspring, which has a chance of altering the chromosomes.

    Args:
        The population which is a list of the object Individual. The entire population just ran in
        the simulation and their reward (fitness) was stored.

    Returns:
        A new population which is a list of the object Individual. The new population is the next generation
        and consists of the elite from the previous population and offspring from the previous generation.
    """

    # Sort the population to find out which had the best performance
    population = sorted(population, key=lambda x: x.reward, reverse=True)
    total_remove = 94

    # Elitism
    new_population = population[:(len(population) - total_remove)]

    parent_1, parent_2 = wheel_selection(total_remove, population)

    for i in range(int(total_remove / 2)):

        if random_offspring(parent_1[i], parent_2[i]):
            individual_1, individual_2 = create_random_offspring()
        else:
            rule_1, rule_2 = uniform_crossover_binary(parent_1[i].chromosome_1,
                                                      parent_2[i].chromosome_1)
            range_1, range_2 = uniform_crossover(parent_1[i].chromosome_2,
                                                 parent_2[i].chromosome_2)

            mutate(rule_1)
            mutate(rule_2)

            ca_mutate(range_1)
            ca_mutate(range_2)

            individual_1 = Individual()
            individual_2 = Individual()

            individual_1.chromosome_1 = rule_1
            individual_2.chromosome_1 = rule_2

            individual_1.chromosome_2 = range_1
            individual_2.chromosome_2 = range_2

        new_population.append(individual_1)
        new_population.append(individual_2)

    return new_population
Exemple #14
0
def order_crossover(gen1, gen2, rt=None, rb=None):
    """
    Generates two childre from the parents provided using
    Order Cross
    """

    # len(gen) = n
    # we need up to n-1
    # and we discard the last two
    # randint is include a <= x <= b

    size = len(gen1.slides)
    child1, child2 = [], []
    rand_top = rt or randint(3, size - 3) + 2
    rand_botton = rb or randint(0, rand_top - 1)

    # set of
    used1 = set()
    used2 = set()

    for i in range(rand_botton, rand_top + 1):
        sld1 = gen1.slides[i]
        sld2 = gen2.slides[i]

        child1.append(sld1)
        child2.append(sld2)

        used1.union(set([x.pk for x in sld1.photos if x != None]))
        used2.union(set([x.pk for x in sld2.photos if x != None]))

    lower_limit = (rand_top + 1) % size
    top_limit = rand_top

    pos = lower_limit

    while pos != top_limit:

        if gen2.slides[pos].pk not in used1:
            child1.append(gen2.slides[pos])
            used1.union(
                set([x.pk for x in gen2.slides[pos].photos if x != None]))

        if gen1.slides[pos].pk not in used2:
            child2.append(gen1.slides[pos])
            used2.union(
                set([x.pk for x in gen1.slides[pos].photos if x != None]))

        pos = (pos + 1) % size

    if gen2.slides[rand_top].pk not in used1:
        child2.append(gen1.slides[rand_top])
    if gen1.slides[rand_top].pk not in used2:
        child1.append(gen2.slides[rand_top])

    return Individual(child1), Individual(child2)
 def create_individual(self):
     if (self.parameters['type'] == 'permutation'):
         phen = \
         np.array(list(range(0,self.environment.get_chromosome_length())))
         random.shuffle(phen)
         individual = \
         Individual(phenotype=phen)
     else:
         individual = \
         Individual(Genotype(self.environment.get_chromosome_length()))
     return individual
def recombination(parent1, parent2):
	cross_point = np.random.randint(1, len(parent1.chromosome))

	child1 = Individual(
		chromosome = np.append(parent1.chromosome[:cross_point], parent2.chromosome[cross_point:]),
	)
	child2 = Individual(
		chromosome = np.append(parent2.chromosome[:cross_point], parent1.chromosome[cross_point:]),
	)
	
	return [ child1, child2 ]
 def crossover_individuals(individual1, individual2, type='single'):
     genotype1 = individual1.genotype
     genotype2 = individual2.genotype
     crossp = randint(1, genotype1.n - 1)
     mask1 = ((1 << (genotype1.n - crossp)) - 1) << crossp
     mask2 = (1 << crossp) - 1
     ch1 = mask1 & genotype1.chromosome | mask2 & genotype2.chromosome
     ch2 = mask1 & genotype2.chromosome | mask2 & genotype1.chromosome
     child1 = Genotype(genotype1.n, ch1)
     child2 = Genotype(genotype1.n, ch2)
     return [Individual(child1), Individual(child2)]
Exemple #18
0
 def uniform_crossover(parent1, parent2):
     sub_child1, sub_child2 = [], []
     for i in range(parent1.length()):
         if (randint(0, 1)):
             sub_child1.append(parent1.binary_string[i])
             sub_child2.append(parent2.binary_string[i])
         else:
             sub_child1.append(parent2.binary_string[i])
             sub_child2.append(parent1.binary_string[i])
     child1 = Individual(''.join(sub_child1))
     child2 = Individual(''.join(sub_child2))
     return child1, child2
Exemple #19
0
 def one_point_crossover(parent1, parent2):
     sub_child1, sub_child2 = [], []
     # Create a random crossover_point
     crossover_point = randint(0, parent1.length())
     for i in range(crossover_point):
         sub_child1.append(parent1.binary_string[i])
         sub_child2.append(parent2.binary_string[i])
     for i in range(crossover_point, parent1.length()):
         sub_child1.append(parent2.binary_string[i])
         sub_child2.append(parent1.binary_string[i])
     child1 = Individual(''.join(sub_child1))
     child2 = Individual(''.join(sub_child2))
     return child1, child2
Exemple #20
0
def generate_child(parent_a, parent_b, mutation_probability):
    child_a_chromosome, child_b_chromosome = Crossover.cut_and_crossfill(
        parent_a.chromosome, parent_b.chromosome)
    child_a = Individual(chromosome=child_a_chromosome)
    child_b = Individual(chromosome=child_b_chromosome)

    if should_event_happen(mutation_probability):
        child_a.mutate()

    if should_event_happen(mutation_probability):
        child_b.mutate()

    return child_a, child_b
Exemple #21
0
    def eval(cls, ind, p):
        if 'rates' in ind.__dict__:
            c_ind = Individual(genome=np.copy(ind.genome),
                               fitness=np.nan,
                               rates=np.copy(ind.rates))
        else:
            c_ind = Individual(genome=np.copy(ind.genome), fitness=np.nan)
        if np.random.uniform() < p:
            for n in range(len(c_ind.genome)):
                c_ind.genome[n] += np.random.normal(0, 0.01)
                c_ind.genome[n] = max(0.0, c_ind.genome[n])

        c_ind.genome = c_ind.genome / sum(c_ind.genome)
        return [c_ind]
Exemple #22
0
def crossover(population, pop_len, pcross, fit, center, limit):
    parents = list(filter(lambda individual: np.random.rand() < pcross,
                          population[:int(pop_len * pcross)]))
    parent_pairs = []
    for i, p in enumerate(parents):
        parent_pairs += list(product([p], parents[:i] + parents[i + 1:]))

    childs = []
    for p1, p2 in parent_pairs:
        # Q1, Q2
        ch1, ch2, sol1, sol2 = cross(p1[0], p2[0], limit)
        childs.append((Individual(ch1, sol1), fit(center, sol1)))
        childs.append((Individual(ch2, sol2), fit(center, sol2)))
    return childs
Exemple #23
0
    def crossover(self, individu1, individu2, mergingPoint, heuristicStrategy):
        newGenome = []
        newGenome2 = []
        newGenome.extend(individu1.getGenome()[:mergingPoint])
        newGenome2.extend(individu2.getGenome()[:mergingPoint])
        newGenome.extend(
            individu2.getGenome()[mergingPoint:len(individu2.getGenome())])
        newGenome2.extend(
            individu1.getGenome()[mergingPoint:len(individu1.getGenome())])

        newIndividu1 = Individual(newGenome, heuristicStrategy)
        newIndividu2 = Individual(newGenome2, heuristicStrategy)

        return [newIndividu1, newIndividu2]
Exemple #24
0
def two_point_crossover(parent1, parent2, ff):
    if len(parent1.genotype) is not len(parent2.genotype):
        raise ValueError("Parents have different length!?")

    split1 = random.randint(0, len(parent1.genotype))
    split2 = random.randint(split1, len(parent1.genotype))

    child1 = Individual(
        parent2.genotype[0:split1] + parent1.genotype[split1:split2] +
        parent2.genotype[split2:], ff)
    child2 = Individual(
        parent1.genotype[0:split1] + parent2.genotype[split1:split2] +
        parent1.genotype[split2:], ff)
    return child1, child2
 def cx_crossover(individual1,individual2):
     n = len(individual1.phenotype)
     child1= individual2.phenotype.copy()
     child2= individual1.phenotype.copy()
     city_pos = 0
     map_1 = {individual1.phenotype[i]:i for i in range(0,n)}
     while(True):
         city_pos = PermutationCrossover.apply_mapping(\
         child1,child2,individual1,individual2,city_pos,map_1)
         if(map_1[individual2.phenotype[city_pos]] is 0):
             PermutationCrossover.apply_mapping(\
             child1,child2,individual1,individual2,city_pos,map_1)
             break
     return  [Individual(phenotype=child1),Individual(phenotype=child2)]
Exemple #26
0
    def start(self, number_of_generations, size_of_population,
              cross_probability, mutation_probability):
        self.size_of_population = size_of_population
        self.cross_probability = cross_probability
        self.mutation_probability = mutation_probability
        self.create_population()
        self.number_of_generations = 0
        number_of_childs_to_born = 50

        #pętla główna algorytmu
        while self.number_of_generations <= number_of_generations:
            for x in range(number_of_childs_to_born):
                while True:
                    first_parent = Individual(individual=self.population[
                        randint(0,
                                len(self.population) - 1)])
                    second_parent = Individual(individual=self.population[
                        randint(0,
                                len(self.population) - 1)])
                    if not (first_parent == second_parent
                            and first_parent.is_parent
                            and second_parent.is_parent):
                        break

                temp_rand = uniform(0, 1)
                if self.cross_probability >= temp_rand:

                    child = Individual(individual=self.pmx_child_creation(
                        first_parent, second_parent))
                    self.population.append(child)

            for x in range(len(self.population)):
                self.population[x].is_parent = True

            temp_rand = uniform(0, 1)
            if self.mutation_probability >= temp_rand:
                individual_to_mutate = self.population[randint(
                    0,
                    len(self.population) - 1)]
                self.mutate(individual_to_mutate)

            self.population_selection()
            self.number_of_generations += 1

        best_individual = self.population[0]

        for x in range(self.number_of_cities):
            self.route.append(best_individual.path[x])
        self.best_cycle_cost = best_individual.path_cost
    def get_new_individuals(self, generation):
        """
        Provides a new list of individuals, based on the given generation.
        Performs crossover and mutations on the childs.
        Can also just copy some individuals (elite) into the next generation.
        """
        new_individuals = []

        if settings.ELITE_AMOUNT:
            new_individuals.extend(
                generation.get_elite(settings.ELITE_AMOUNT)
            )

        if len(set([str(t.printable_path) for t in generation.individuals])) == 1:
            # This weird 'if' above will be True in case all the individuals are equal
            print('Population has converged. Finishing simulation.')
            exit()

        def get_parent_index():
            # Selects a parent (through the index),
            # based on the probability distribution.
            r = random()
            probs = generation.cumulative_probabilities
            for i in range(len(probs)):
                if probs[i] < r <= probs[i+1]:
                    return i

        # Runs until we have the correct amount of individuals for this generation
        while True:
            parent_1 = generation.ranked_individuals[get_parent_index()]
            parent_2 = generation.ranked_individuals[get_parent_index()]

            while Individual.have_the_same_path(parent_1, parent_2):
                # Parents are the same -- retry second parent
                parent_2 = generation.ranked_individuals[get_parent_index()]

            child_a, child_b = Individual(self.world), Individual(self.world)

            child_a_path, child_b_path = self.crossover(parent_1, parent_2)
            self.mutate(child_a_path)
            self.mutate(child_b_path)
            child_a.path, child_b.path = child_a_path, child_b_path

            new_individuals.extend([child_a, child_b])
            if len(new_individuals) >= settings.POPULATION_AMOUNT:
                new_individuals = new_individuals[0:settings.POPULATION_AMOUNT+1]
                break

        return new_individuals
def global_uniform_selection(population):
    objvar_lists = _choose_lists_from_pop(population, (lambda x: x.objvars))
    sigma_lists = _choose_lists_from_pop(population, (lambda x: x.sigmas))
    alpha_lists = _choose_lists_from_pop(population, (lambda x: x.alphas))

    p1 = Individual(objvar_lists[0],
                    sigma_lists[0],
                    alpha_lists[0],
                    compute_fitness=False)
    p2 = Individual(objvar_lists[1],
                    sigma_lists[1],
                    alpha_lists[1],
                    compute_fitness=False)

    return p1, p2
    def ox1_crossover(individual1,individual2):
        n,i,f,i_central,i_sides= PermutationCrossover.mapping_sections(\
        individual1,individual2)
        child1,child2 = PermutationCrossover.child_templates(n) 

        p1_central = set(individual1.phenotype[i_central])
        p2_central = set(individual2.phenotype[i_central])

        child1[i_central]= individual1.phenotype[i_central]
        child2[i_central]= individual2.phenotype[i_central]        
        child1[i_sides[:n-(f-i)-1]] = list(filter(lambda x:x not in p1_central,\
        individual2.phenotype[i_sides]))
        child2[i_sides[:n-(f-i)-1]] = list(filter(lambda x:x not in p2_central,\
        individual1.phenotype[i_sides])) 
        return [Individual(phenotype=child1),Individual(phenotype=child2)]
Exemple #30
0
def PMX(parent1, parent2):
    offspring1 = Individual([-1 for x in range(len(parent1.path))], 0)
    offspring2 = Individual([-1 for x in range(len(parent1.path))], 0)

    path_length = len(parent1.path)
    seg_start = random.randint(0, path_length - 1)
    seg_end = random.randint(seg_start, path_length - 1)
    for i in range(seg_start, seg_end + 1):
        offspring1.path[i] = parent1.path[i]
        offspring2.path[i] = parent2.path[i]

    offspring1 = PMX_fill_offspring(offspring1, parent2, seg_start, seg_end)
    offspring2 = PMX_fill_offspring(offspring2, parent1, seg_start, seg_end)

    return offspring1, offspring2