Skip to content

Commit

Permalink
nsga2 first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
hugoaboud committed Dec 30, 2020
1 parent f5f29ec commit 2b1007a
Show file tree
Hide file tree
Showing 9 changed files with 527 additions and 152 deletions.
File renamed without changes.
81 changes: 81 additions & 0 deletions examples/hoverboard/config-nsga2
@@ -0,0 +1,81 @@
#--- parameters for the XOR-2 experiment ---#

[NEAT]
fitness_criterion = max
fitness_threshold = 1000
pop_size = 50
reset_on_extinction = False

[DefaultGenome]
# node activation options
activation_default = random
activation_mutate_rate = 0.5
activation_options = relu gauss

# node aggregation options
aggregation_default = random
aggregation_mutate_rate = 0.3
aggregation_options = max sum

# node bias options
bias_init_mean = 0.0
bias_init_stdev = 1.0
bias_max_value = 2.0
bias_min_value = -2.0
bias_mutate_power = 0.5
bias_mutate_rate = 0.7
bias_replace_rate = 0.1

# genome compatibility options
compatibility_disjoint_coefficient = 1.0
compatibility_weight_coefficient = 0.5

# connection add/remove rates
conn_add_prob = 0.5
conn_delete_prob = 0.5

# connection enable options
enabled_default = True
enabled_mutate_rate = 0.01

feed_forward = False
initial_connection = partial_nodirect 0.6

# node add/remove rates
node_add_prob = 0.4
node_delete_prob = 0.4

# network parameters
num_hidden = 0
num_inputs = 5
num_outputs = 2

# node response options
response_init_mean = 1.0
response_init_stdev = 0.0
response_max_value = 30.0
response_min_value = -30.0
response_mutate_power = 0.0
response_mutate_rate = 0.0
response_replace_rate = 0.0

# connection weight options
weight_init_mean = 0.0
weight_init_stdev = 60.0
weight_max_value = 100
weight_min_value = -100
weight_mutate_power = 3
weight_mutate_rate = 0.8
weight_replace_rate = 0.3

[DefaultSpeciesSet]
compatibility_threshold = 20.0

[DefaultStagnation]
species_fitness_func = max
max_stagnation = 20
species_elitism = 2

[NSGA2Reproduction]
elitism = 1
survival_threshold = 0.2
164 changes: 164 additions & 0 deletions examples/hoverboard/evolve-flighdvnc.py
@@ -0,0 +1,164 @@
"""
Hoverboard: Flight Deviance (Double Fitness), using NSGA-II
Small example tool to control the hoverboard game using NEAT.
It uses the NSGA2Reproduction method, with two fitness values: flight time and total distance from the center.
# USAGE:
> python evolve-flightdvnc.py <ANGLE>
> python evolve-flightdvnc.py --help
@author: Hugo Aboud (@hugoaboud)
"""

from __future__ import print_function

## DEBUG
## Uses local version of neat-python
import sys
sys.path.append('../../')
## DEBUG
import neat

import os
import math
import argparse

from hoverboard import Game
from visualize import watch

# General Parameters

GAME_TIME_STEP = 0.001
CHECKPOINT_FOLDER = 'checkpoint-flightdvnc'
CONFIG_FILE = 'config-nsga2'

# CLI Parameters

GAME_START_ANGLE = 0
SILENT = False
FAST_FORWARD = False

# Evolution Flags

BEST = None
GEN = 0
POPULATION = None

##
# Reporter
# Used to watch the game after each evaluation
##

class GameReporter(neat.reporting.BaseReporter):
def post_evaluate(self, config, population, species, best_genome):
global BEST
global POPULATION
# If watch game is enabled (not silent) and best genome
# has changed, watch it
if (not SILENT and best_genome != BEST):
BEST = best_genome
species = POPULATION.species.get_species_id(BEST.key)
watch(config, GAME_TIME_STEP*(10 if FAST_FORWARD else 1), GEN, species, BEST, GAME_START_ANGLE)

##
# Evaluation
##

# Evaluate genome
def eval(genome, config):
# Create network
net = neat.nn.RecurrentNetwork.create(genome, config)
# Create game
game = Game(GAME_START_ANGLE,False)
# Run the game and calculate fitness (list)
genome.fitness = neat.nsga2.NSGA2Fitness(0,0)
while(True):
# Activate Neural Network
output = net.activate([game.hoverboard.velocity[0], game.hoverboard.velocity[1], game.hoverboard.ang_velocity, game.hoverboard.normal[0], game.hoverboard.normal[1]])

# Update game state from output and then update physics
game.hoverboard.set_thrust(output[0], output[1])
game.update(GAME_TIME_STEP)

# Fitness 0: flight time
# Fitness 1: distance from center (negative)
genome.fitness.add( GAME_TIME_STEP,
-math.sqrt((game.hoverboard.x-0.5)**2+(game.hoverboard.y-0.5)**2) )

# Fitness alternatives
#genome.fitness -= (game.hoverboard.normal[0]**2)
#genome.fitness -= math.sqrt(game.hoverboard.velocity[0]**2+game.hoverboard.velocity[1]**2)
#genome.fitness -= game.hoverboard.ang_velocity**2

# End of game
if (game.reset_flag): break

genome.fitness.values[1] /= genome.fitness.values[0]

# Evaluate generation
def eval_genomes(genomes, config):
# Global evolution flags
global GEN
# Evaluate each genome
for genome_id, genome in genomes:
eval(genome, config)
# NSGA-II required step: non-dominated sorting
POPULATION.reproduction.sort(genomes)
GEN += 1

##
# Main
##

def main():

# Parse CLI arguments
parser = argparse.ArgumentParser(description='Example of evolving a Neural Network using neat-python to play a 2D hoverboard game.')
parser.add_argument('angle', help="Starting angle of the platform")
parser.add_argument('-c', '--checkpoint', help="Number of a checkpoint on the 'checkpoint-reference' folder to start from")
parser.add_argument('-s', '--silent', help="Don't watch the game", nargs='?', const=True, type=bool)
parser.add_argument('-f', '--fastfwd', help="Fast forward the game preview (10x)", nargs='?', const=True, type=bool)
args = parser.parse_args()

# Store global parameters
global GAME_START_ANGLE
global SILENT
global FAST_FORWARD
GAME_START_ANGLE = float(args.angle)
SILENT = bool(args.silent)
FAST_FORWARD = bool(args.fastfwd)

# Load neat configuration.
# Here's where we load the NSGA-II reproduction module
config = neat.Config(neat.DefaultGenome, neat.nsga2.NSGA2Reproduction,
neat.DefaultSpeciesSet, neat.DefaultStagnation,
CONFIG_FILE)

# Create the population or load from checkpoint
global POPULATION
if (args.checkpoint != None):
POPULATION = neat.Checkpointer.restore_checkpoint(os.path.join(CHECKPOINT_FOLDER,'gen-'+str(args.checkpoint)), config)
else:
POPULATION = neat.Population(config)

# Add a stdout reporter to show progress in the terminal.
POPULATION.add_reporter(neat.StdOutReporter(False))

# Add a game reporter to watch the game post evaluation
POPULATION.add_reporter(GameReporter())

# Add a checkpointer to save population pickles
if not os.path.exists(CHECKPOINT_FOLDER):
os.makedirs(CHECKPOINT_FOLDER)
POPULATION.add_reporter(neat.Checkpointer(1,filename_prefix=os.path.join(CHECKPOINT_FOLDER,'gen-')))

# Run until a solution is found.
winner = POPULATION.run(eval_genomes)

# Display the winning genome.
print('\nBest genome:\n{!s}'.format(winner))

main()
35 changes: 22 additions & 13 deletions examples/hoverboard/evolve-flightime.py
@@ -1,6 +1,6 @@
"""
Hoverboard: Runtime (Single Fitness)
Hoverboard: Flight Time (Single Fitness)
Small example tool to control the hoverboard game using NEAT.
It uses the DefaultReproduction method, with a single fitness value: flight time.
Expand All @@ -26,6 +26,7 @@

GAME_TIME_STEP = 0.001
CHECKPOINT_FOLDER = 'checkpoint-flightime'
CONFIG_FILE = 'config-default'

# CLI Parameters

Expand All @@ -39,6 +40,22 @@
GEN = 0
POPULATION = None

##
# Reporter
# Used to watch the game after each evaluation
##

class GameReporter(neat.reporting.BaseReporter):
def post_evaluate(self, config, population, species, best_genome):
global BEST
global POPULATION
# If watch game is enabled (not silent) and best genome
# has changed, watch it
if (not SILENT and best_genome != BEST):
BEST = best_genome
species = POPULATION.species.get_species_id(BEST.key)
watch(config, GAME_TIME_STEP*(10 if FAST_FORWARD else 1), GEN, species, BEST, GAME_START_ANGLE)

##
# Evaluation
##
Expand Down Expand Up @@ -74,21 +91,10 @@ def eval(genome, config):
# Evaluate generation
def eval_genomes(genomes, config):
# Global evolution flags
global BEST
global GEN
global POPULATION
# Evaluate each genome looking for the best
max = None
for genome_id, genome in genomes:
eval(genome, config)
if (max == None or genome.fitness > max.fitness):
max = genome
# If watch game is enabled (not silent) and best genome
# has changed, watch it
if (not SILENT and max != BEST):
BEST = max
species = POPULATION.species.get_species_id(BEST.key)
watch(config, GAME_TIME_STEP*(10 if FAST_FORWARD else 1), GEN, species, BEST, GAME_START_ANGLE)
GEN += 1

##
Expand Down Expand Up @@ -116,7 +122,7 @@ def main():
# Load neat configuration.
config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
neat.DefaultSpeciesSet, neat.DefaultStagnation,
'config-flightime')
CONFIG_FILE)

# Create the population or load from checkpoint
global POPULATION
Expand All @@ -128,6 +134,9 @@ def main():
# Add a stdout reporter to show progress in the terminal.
POPULATION.add_reporter(neat.StdOutReporter(False))

# Add a game reporter to watch the game post evaluation
POPULATION.add_reporter(GameReporter())

# Add a checkpointer to save population pickles
if not os.path.exists(CHECKPOINT_FOLDER):
os.makedirs(CHECKPOINT_FOLDER)
Expand Down
8 changes: 4 additions & 4 deletions examples/hoverboard/gui.py
Expand Up @@ -180,10 +180,10 @@ def render(self, screen):
# if info enabled, render info texts
if (self.info):
img = self.font.render(str('GENERATION: {0}'.format(self.generation)), True, COLOR_TEXT)
screen.blit(img, (10,DISPLAY[1]-45))
screen.blit(img, (DISPLAY[0]-70,DISPLAY[1]-45))
img = self.font.render(str('SPECIES: {0}'.format(self.species)), True, COLOR_TEXT)
screen.blit(img, (10,DISPLAY[1]-30))
screen.blit(img, (DISPLAY[0]-70,DISPLAY[1]-30))
img = self.font.render(str('ID: {0}'.format(self.genome.key)), True, COLOR_TEXT)
screen.blit(img, (10,DISPLAY[1]-15))
screen.blit(img, (DISPLAY[0]-70,DISPLAY[1]-15))
img = self.font.render(str('FITNESS: {0}'.format(self.genome.fitness)), True, COLOR_TEXT)
screen.blit(img, (DISPLAY[0]-120,DISPLAY[1]-15))
screen.blit(img, (10,DISPLAY[1]-15))
13 changes: 13 additions & 0 deletions examples/hoverboard/sandbox.py
@@ -0,0 +1,13 @@
class Fitness:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return str((self.a,self.b))
def __gt__(self, other):
return self.a > other.a

fits = [Fitness(a,a*2) for a in range(10)]

print (fits)
print (max(fits))
1 change: 1 addition & 0 deletions neat/__init__.py
Expand Up @@ -2,6 +2,7 @@
import neat.nn as nn
import neat.ctrnn as ctrnn
import neat.iznn as iznn
import neat.nsga2 as nsga2
import neat.distributed as distributed

from neat.config import Config
Expand Down

0 comments on commit 2b1007a

Please sign in to comment.