Move POPULATION, INPUTS and OUTPUTS parameters to pool

This commit is contained in:
Felix Bargfeldt 2019-05-11 22:18:30 +02:00
parent ad9d45a584
commit d9c23e6674
No known key found for this signature in database
GPG key ID: 99184F5FDC589A67
6 changed files with 34 additions and 29 deletions

View file

@ -1,7 +1,6 @@
from typing import List, Tuple
from genome import Genome
from neatconfig import *
TestCase = Tuple[List[float], List[float]]
@ -17,5 +16,5 @@ class Evaluator:
error: float = 0
for expected, actual in zip(expected_output, network_output):
error += abs(expected - actual)
fitness += OUTPUTS - error
fitness += genome.output_nodes - error
return fitness

View file

@ -9,16 +9,21 @@ from nodegene import NodeGene
class Genome:
def __init__(self, genome: 'Genome' = None):
def __init__(self, input_nodes: int, output_nodes: int):
self.input_nodes: int = input_nodes
self.output_nodes: int = output_nodes
self.fitness: float = 0
self.adjusted_fitness: float = 0
self.connection_gene_list: List[ConnectionGene] = []
self.nodes: Dict[int, NodeGene] = {}
if genome is not None:
self.fitness: float = genome.fitness
self.adjusted_fitness: float = genome.adjusted_fitness
for connection in genome.connection_gene_list:
self.connection_gene_list.append(connection.copy())
def copy(self) -> 'Genome':
genome: Genome = Genome(self.input_nodes, self.output_nodes)
genome.fitness: float = self.fitness
genome.adjusted_fitness: float = self.adjusted_fitness
for connection in self.connection_gene_list:
genome.connection_gene_list.append(connection.copy())
return genome
@staticmethod
def cross_over(parent1: 'Genome', parent2: 'Genome') -> 'Genome':
@ -32,7 +37,7 @@ class Genome:
for connection in parent2.connection_gene_list:
gene_map2[connection.innovation] = connection
child: Genome = Genome()
child: Genome = Genome(parent1.input_nodes, parent1.output_nodes)
for key in {*gene_map1, *gene_map2}:
if key in gene_map1 and key in gene_map2:
trait: ConnectionGene = random.choice([gene_map1, gene_map2])[key].copy()
@ -84,11 +89,11 @@ class Genome:
def generate_network(self):
self.nodes.clear()
for i in range(INPUTS):
for i in range(self.input_nodes):
self.nodes[i] = NodeGene(0)
self.nodes[INPUTS] = NodeGene(1)
self.nodes[self.input_nodes] = NodeGene(1)
for i in range(INPUTS + 1, INPUTS + 1 + OUTPUTS):
for i in range(self.input_nodes + 1, self.input_nodes + 1 + self.output_nodes):
self.nodes[i] = NodeGene(0)
for connection in self.connection_gene_list:
@ -101,17 +106,18 @@ class Genome:
def evaluate_network(self, inputs: List[float]) -> List[float]:
self.generate_network()
for i in range(INPUTS):
for i in range(self.input_nodes):
self.nodes[i].value = inputs[i]
for i in [*filter(lambda i: i >= INPUTS + OUTPUTS + 1, sorted(self.nodes)), *range(INPUTS + 1, INPUTS + 1 + OUTPUTS)]:
for i in [*filter(lambda i: i >= self.input_nodes + self.output_nodes + 1, sorted(self.nodes)),
*range(self.input_nodes + 1, self.input_nodes + 1 + self.output_nodes)]:
self.nodes[i].value = Genome.sigmoid(
sum(
self.nodes[connection.into].value * connection.weight * connection.enabled
for connection in self.nodes[i].incoming
)
)
return [self.nodes[i].value for i in range(INPUTS + 1, INPUTS + 1 + OUTPUTS)]
return [self.nodes[i].value for i in range(self.input_nodes + 1, self.input_nodes + 1 + self.output_nodes)]
@staticmethod
def sigmoid(x: float) -> float:
@ -139,8 +145,11 @@ class Genome:
def mutate_add_connection(self):
self.generate_network()
random1: int = random.choice([*filter(lambda i: i < INPUTS + 1 or i >= INPUTS + 1 + OUTPUTS, self.nodes)])
random2: int = random.choice([*filter(lambda i: i >= INPUTS + 1, self.nodes)])
random1: int = random.choice(list(filter(
lambda i: i < self.input_nodes + 1 or i >= self.input_nodes + 1 + self.output_nodes,
self.nodes
)))
random2: int = random.choice([*filter(lambda i: i >= self.input_nodes + 1, self.nodes)])
if random1 >= random2:
return
if any(connection.into == random1 and connection.out == random2 for connection in self.connection_gene_list):

View file

@ -1,8 +1,3 @@
INPUTS: int = 2
OUTPUTS: int = 1
POPULATION: int = 300
COMPATIBILITY_THRESHOLD: float = 1
EXCESS_COEFFICIENT: float = 1
DISJOINT_COEFFICIENT: float = 1

12
pool.py
View file

@ -1,15 +1,17 @@
from typing import List, Callable
from genome import Genome
from neatconfig import *
from species import Species
class Pool:
def __init__(self):
def __init__(self, population: int, input_nodes: int, output_nodes: int):
self.population: int = population
self.input_nodes: int = input_nodes
self.output_nodes = output_nodes
self.species: List[Species] = []
for _ in range(POPULATION):
self.add_to_species(Genome())
for _ in range(population):
self.add_to_species(Genome(input_nodes, output_nodes))
def add_to_species(self, genome: Genome):
for species in self.species:
@ -64,7 +66,7 @@ class Pool:
carry_over: float = 0
for species in self.species:
fchild: float = POPULATION * (species.get_total_adjusted_fitness() / total_adjusted_fitness)
fchild: float = self.population * (species.get_total_adjusted_fitness() / total_adjusted_fitness)
nchild: int = int(fchild)
carry_over += fchild - nchild
if carry_over >= 1:

View file

@ -35,6 +35,6 @@ class Species:
parent2: Genome = random.choice(self.genomes)
child: Genome = Genome.cross_over(parent1, parent2)
else:
child: Genome = Genome(random.choice(self.genomes))
child: Genome = random.choice(self.genomes).copy()
child.mutate()
return child

2
xor.py
View file

@ -5,7 +5,7 @@ from genome import Genome
from pool import Pool
evaluator: Evaluator = Evaluator([([i, j], [i ^ j]) for i in range(2) for j in range(2)])
pool: Pool = Pool()
pool: Pool = Pool(300, 2, 1)
generation: int = 1
while True:
pool.evaluate_fitness(evaluator.evaluate)