Move POPULATION, INPUTS and OUTPUTS parameters to pool
This commit is contained in:
parent
ad9d45a584
commit
d9c23e6674
6 changed files with 34 additions and 29 deletions
|
@ -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
|
||||
|
|
39
genome.py
39
genome.py
|
@ -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):
|
||||
|
|
|
@ -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
12
pool.py
|
@ -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:
|
||||
|
|
|
@ -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
2
xor.py
|
@ -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)
|
||||
|
|
Reference in a new issue