2022-06-02 00:52:16 +02:00
|
|
|
import random
|
|
|
|
from typing import List
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
import numpy.typing as npt
|
|
|
|
|
|
|
|
from genome import Genome
|
|
|
|
|
|
|
|
|
|
|
|
class Population:
|
|
|
|
population: List[Genome] = [] # array to hold the current population
|
|
|
|
mating_pool: List[Genome] = [] # array which we will use for our "mating pool"
|
|
|
|
generations: int = 0 # number of generations
|
|
|
|
finished: bool = False # are we finished evolving?
|
|
|
|
mutation_rate: float
|
|
|
|
perfect_score: int
|
|
|
|
best_genome: Genome
|
|
|
|
|
|
|
|
def __init__(self, mutation_rate, population_size, perfect_score=20):
|
|
|
|
self.mutation_rate = mutation_rate
|
|
|
|
self.perfect_score = perfect_score
|
|
|
|
|
|
|
|
for i in range(0, population_size):
|
|
|
|
new_genome = Genome()
|
|
|
|
new_genome.calc_fitness()
|
|
|
|
self.population.append(new_genome)
|
|
|
|
|
|
|
|
# create a new generation
|
|
|
|
def generate(self):
|
|
|
|
max_fitness = 0
|
|
|
|
for genome in self.population:
|
|
|
|
if genome.fitness > max_fitness:
|
|
|
|
max_fitness = genome.fitness
|
|
|
|
|
|
|
|
print("Max fitness of generation " + str(self.generations) + " = " + str(max_fitness))
|
|
|
|
|
|
|
|
# refill the population with children from the mating pool
|
|
|
|
new_population = []
|
|
|
|
for genome in self.population:
|
|
|
|
partner_a = self.accept_reject(max_fitness)
|
|
|
|
partner_b = self.accept_reject(max_fitness)
|
|
|
|
child = partner_a.crossover(partner_b)
|
|
|
|
child.mutate(self.mutation_rate)
|
|
|
|
new_population.append(child)
|
|
|
|
|
|
|
|
self.population = new_population
|
|
|
|
self.generations += 1
|
|
|
|
|
|
|
|
# select random with correct probability from population
|
|
|
|
def accept_reject(self, max_fitness: int):
|
|
|
|
safe_flag = 0
|
|
|
|
|
|
|
|
while safe_flag < 10000:
|
|
|
|
partner = random.choice(self.population)
|
|
|
|
r = random.randint(0, max_fitness)
|
|
|
|
|
|
|
|
if r < partner.fitness:
|
|
|
|
return partner
|
|
|
|
|
|
|
|
safe_flag += 1
|
|
|
|
|
|
|
|
# compute the current "most fit" member of the population
|
|
|
|
def evaluate(self):
|
|
|
|
record = 0
|
|
|
|
best_index = 0
|
|
|
|
|
|
|
|
for index in range(len(self.population)):
|
|
|
|
genome = self.population[index]
|
|
|
|
if genome.fitness > record:
|
|
|
|
record = genome.fitness
|
|
|
|
best_index = index
|
|
|
|
|
|
|
|
self.best_genome = self.population[best_index]
|
|
|
|
if record >= self.perfect_score:
|
|
|
|
self.finished = True
|
|
|
|
|
|
|
|
return self.finished
|
|
|
|
|
|
|
|
def calc_fitness(self):
|
|
|
|
for genome in self.population:
|
|
|
|
genome.calc_fitness()
|