Skip to content

Commit

Permalink
Removed C++ implementations (they may be restored and cleaned up to m…
Browse files Browse the repository at this point in the history
…atch the final implementation in a later version).

Moved code in the library that looks like tests and/or demos into a "basic" examples directory.
Added example script to produce animation of single-pole balancing output.
Added "delete connection" as a mutation option.
 Updated CTRNN version of single-pole balancing to work with current code.
Fix typos and PEP 8 complaints.
Removed unused/commented code.
Added numpy as an explicit dependency.
  • Loading branch information
CodeReclaimers committed Oct 24, 2015
1 parent 8cf3000 commit bdd636e
Show file tree
Hide file tree
Showing 48 changed files with 852 additions and 2,123 deletions.
33 changes: 33 additions & 0 deletions examples/basic/chromosome_visualize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from neat import visualize, genome
from neat import chromosome
from neat.config import Config


# Example
# define some attributes
node_gene_type = genome.NodeGene # standard neuron model
conn_gene_type = genome.ConnectionGene # and connection link
Config.nn_activation = 'exp' # activation function
Config.weight_stdev = 0.9 # weights distribution

Config.input_nodes = 2 # number of inputs
Config.output_nodes = 1 # number of outputs

# creates a chromosome for recurrent networks
# c1 = Chromosome.create_fully_connected()

# creates a chromosome for feedforward networks
chromosome.node_gene_type = genome.NodeGene

c2 = chromosome.FFChromosome.create_fully_connected()
# add two hidden nodes
c2.add_hidden_nodes(2)
# apply some mutations
# c2._mutate_add_node()
# c2._mutate_add_connection()

# check the result
# visualize.draw_net(c1) # for recurrent nets
visualize.draw_net(c2, view=True) # for feedforward nets
# print the chromosome
print c2
38 changes: 38 additions & 0 deletions examples/basic/ctrnn_visualize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This example follows from Beer's C++ source code available at:
# http://mypage.iu.edu/~rdbeer/
import numpy as np
import matplotlib.pyplot as plt

from neat.nn import nn_pure as nn
from neat.ctrnn import CTNeuron

# create two output neurons (they won't receive any external inputs)
N1 = CTNeuron('OUTPUT', 1, -2.75, 1.0, 'exp', 0.5)
N2 = CTNeuron('OUTPUT', 2, -1.75, 1.0, 'exp', 0.5)
N1.set_init_state(-0.084000643)
N2.set_init_state(-0.408035109)

neurons_list = [N1, N2]
# create some synapses
conn_list = [(1, 1, 4.5), (1, 2, -1.0), (2, 1, 1.0), (2, 2, 4.5)]
# create the network
net = nn.Network(neurons_list, conn_list)
# activates the network
print "%.17f %.17f" % (N1._output, N2._output)
outputs = []
for i in xrange(1000):
output = net.pactivate()
outputs.append(output)
print "%.17f %.17f" % (output[0], output[1])

outputs = np.array(outputs).T

plt.title("CTRNN model")
plt.ylabel("Outputs")
plt.xlabel("Time")
plt.grid()
plt.plot(outputs[0], "g-", label="output 0")
plt.plot(outputs[1], "r-", label="output 1")
plt.legend(loc="best")
plt.show()
plt.close()
11 changes: 11 additions & 0 deletions examples/basic/iznn_visualize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from neat import visualize
from neat.iznn import Neuron

n = Neuron(10)
spike_train = []
for i in range(1000):
spike_train.append(n.potential)
print '%d\t%f' % (i, n.potential)
n.advance()

visualize.plot_spikes(spike_train, view=True, filename='spiking_neuron.svg')
22 changes: 22 additions & 0 deletions examples/basic/nn_serial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Example
# from neat import visualize
from neat.nn.nn_pure import FeedForward

nn = FeedForward([2, 10, 3], use_bias=False, activation_type='exp')
##visualize.draw_ff(nn)
print 'Serial activation method: '
for t in range(3):
print nn.sactivate([1, 1])

# print 'Parallel activation method: '
# for t in range(3):
# print nn.pactivate([1,1])

# defining a neural network manually
# neurons = [Neuron('INPUT', 1), Neuron('HIDDEN', 2), Neuron('OUTPUT', 3)]
# connections = [(1, 2, 0.5), (1, 3, 0.5), (2, 3, 0.5)]

# net = Network(neurons, connections) # constructs the neural network
# visualize.draw_ff(net)
# print net.pactivate([0.04]) # parallel activation method
# print net # print how many neurons and synapses our network has
18 changes: 18 additions & 0 deletions examples/basic/population_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# TODO: fix this and turn it into a real test.
from neat.population import Population
from neat import config, chromosome, genome

config.Config.pop_size = 100
chromosome.node_gene_type = genome.NodeGene


# sample fitness function
def eval_fitness(population):
for individual in population:
individual.fitness = 1.0


# creates the population
pop = Population()
# runs the simulation for 250 epochs
pop.epoch(eval_fitness, 250)
8 changes: 4 additions & 4 deletions examples/pole_balancing/double_pole/cart_pole.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ def run(self, testing=False):

def __non_markov(self, network, max_steps, testing):
# variables used in Gruau's fitness function
den = 0.0
f1 = 0.0
f2 = 0.0
F = 0.0
#den = 0.0
#f1 = 0.0
#f2 = 0.0
#F = 0.0
last_values = []

steps = 0
Expand Down
4 changes: 2 additions & 2 deletions examples/pole_balancing/double_pole/evolve_double_pole.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from cart_pole import CartPole


def evaluate_population(population):
simulation = CartPole(population, markov=False)
def evaluate_population(pop):
simulation = CartPole(pop, markov=False)
# comment this line to print the status
simulation.print_status = False
simulation.run()
Expand Down
9 changes: 3 additions & 6 deletions examples/pole_balancing/double_pole/run_cartpole.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,11 @@
total_score = []

def average(values):
''' Returns the population average '''
sum = 0.0
for i in values:
sum += i
return sum/len(values)
""" Returns the population average """
return sum(values)/len(values)

def stdev(values):
''' Returns the population standard deviation '''
""" Returns the population standard deviation """
# first compute the average
u = average(values)
error = 0.0
Expand Down
108 changes: 54 additions & 54 deletions examples/pole_balancing/single_pole/evolve_single_pole.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,102 +7,102 @@
from neat import config, population, chromosome, genome, visualize
from neat.nn import nn_pure as nn

angle_limit = 30 * math.pi / 180 # radians


def cart_pole(net_output, x, x_dot, theta, theta_dot):
""" Directly copied from Stanley's C++ source code """

GRAVITY = 9.8
MASSCART = 1.0
MASSPOLE = 0.1
GRAVITY = 9.8 # m/sec^2
MASSCART = 1.0 # kg
MASSPOLE = 0.1 # kg
TOTAL_MASS = (MASSPOLE + MASSCART)
LENGTH = 0.5 # actually half the pole's length
LENGTH = 0.5 # meters, actually half the pole's length
POLEMASS_LENGTH = (MASSPOLE * LENGTH)
FORCE_MAG = 10.0
FORCE_MAG = 10.0 # newtons
TAU = 0.02 # seconds between state updates
FOURTHIRDS = 1.3333333333333

# If the net output is 0.5 or greater, apply a force to the
# right, otherwise apply force to the left.
force = 0
if net_output > 0.5:
force = FORCE_MAG
else:
elif net_output < 0.5:
force = -FORCE_MAG

costheta = math.cos(theta)
sintheta = math.sin(theta)
temp = (force + POLEMASS_LENGTH * theta_dot * theta_dot * sintheta)/ TOTAL_MASS
thetaacc = (GRAVITY*sintheta - costheta*temp)\
/(LENGTH * (FOURTHIRDS - MASSPOLE * costheta * costheta/TOTAL_MASS))
xacc = temp - POLEMASS_LENGTH * thetaacc * costheta / TOTAL_MASS
#Update the four state variables, using Euler's method
x += TAU * x_dot
x_dot += TAU * xacc
theta += TAU * theta_dot

temp = (force + POLEMASS_LENGTH * theta_dot * theta_dot * sintheta) / TOTAL_MASS

thetaacc = (GRAVITY * sintheta - costheta * temp) \
/ (LENGTH * (FOURTHIRDS - MASSPOLE * costheta * costheta / TOTAL_MASS))

xacc = temp - POLEMASS_LENGTH * thetaacc * costheta / TOTAL_MASS

# Update the four state variables, using Euler's method
x += TAU * x_dot
x_dot += TAU * xacc
theta += TAU * theta_dot
theta_dot += TAU * thetaacc

return x, x_dot, theta, theta_dot

def evaluate_population(population):

twelve_degrees = 0.2094384 #radians
num_steps = 10**5

for chromo in population:



def evaluate_population(chromosomes):
num_steps = 10 ** 5

for chromo in chromosomes:

net = nn.create_phenotype(chromo)

# initial conditions (as used by Stanley)
x = random.randint(0, 4799)/1000.0 - 2.4
x_dot = random.randint(0, 1999)/1000.0 - 1.0
theta = random.randint(0, 399)/1000.0 - 0.2
theta_dot = random.randint(0, 2999)/1000.0 - 1.5
x = random.randint(0, 4799) / 1000.0 - 2.4
x_dot = random.randint(0, 1999) / 1000.0 - 1.0
theta = random.randint(0, 399) / 1000.0 - 0.2
theta_dot = random.randint(0, 2999) / 1000.0 - 1.5

fitness = 0

for trials in xrange(num_steps):

# maps into [0,1]
inputs = [(x + 2.4)/4.8,
(x_dot + 0.75)/1.5,
(theta + twelve_degrees)/0.41,
(theta_dot + 1.0)/2.0]

# a normalizacao so acontece para estas condicoes iniciais
# nada garante que a evolucao do sistema leve a outros
# valores de x, x_dot e etc...

inputs = [(x + 2.4) / 4.8,
(x_dot + 0.75) / 1.5,
(theta + angle_limit) / 0.41,
(theta_dot + 1.0) / 2.0]

action = net.pactivate(inputs)

action[0] += 0.4 * (random.random() - 0.5)

# Apply action to the simulated cart-pole
x, x_dot, theta, theta_dot = cart_pole(action[0], x, x_dot, theta, theta_dot)

# Check for failure. If so, return steps
# the number of steps indicates the fitness: higher = better
fitness += 1
if (abs(x) >= 2.4 or abs(theta) >= twelve_degrees):
#if abs(theta) > twelve_degrees: # Igel (p. 5) uses theta criteria only
if (abs(x) >= 2.4 or abs(theta) >= angle_limit):
# if abs(theta) > angle_limit: # Igel (p. 5) uses theta criteria only
# the cart/pole has run/inclined out of the limits
break

chromo.fitness = fitness

if __name__ == "__main__":

if __name__ == "__main__":
# Load the config file, which is assumed to live in
# the same directory as this script.
local_dir = os.path.dirname(__file__)
config.load(os.path.join(local_dir, 'spole_config'))

# Temporary workaround
chromosome.node_gene_type = genome.NodeGene

pop = population.Population()
pop.epoch(evaluate_population, 200, report=1, save_best=0)
print 'Number of evaluations: %d' %(pop.stats()[0][-1]).id
pop.epoch(evaluate_population, 200, report=True)

print 'Number of evaluations: %d' % (pop.stats()[0][-1]).id

# Save the winner,
with open('winner_chromosome', 'w') as f:
pickle.dump(pop.stats()[0][-1], f)
Expand Down
Loading

0 comments on commit bdd636e

Please sign in to comment.