Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove active fields from nodes #13

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions examples/EvolveGoals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,22 @@ macro otime(ex)

# A high-level function to run function evolve_goals.
# it sets the summary to the filename, and max_gens to default values
# If use_cache is true, then each node caches its value. This may be faster for multiple outputs.
# Example: run_evolve_goals("prun",2,13,10,20)
# calls "evolve_goals" with numinputs=2, rseed=13,numlevels=10,runs_per_goal=20
# and the output going to the file: prun2_13_10_20.csv
function run_evolve_goals(filename_prefix,numinputs,rseed,num_levels,runs_per_goal)
# The default is to not use caching and to not keep track of active nodes.
# To use caching in the above example: run_evolve_goals("prun",2,13,20,true)
# If julia is started with mulitple processes, e. g.: "julia -p 8", then these multiple processes will
# be used on different calls to function mu_lambda().
function run_evolve_goals(filename_prefix,numinputs,rseed,num_levels,runs_per_goal, use_cache::Bool=false)
directory = "out/" # subdirectory for output CSV file
filetype = ".csv" # file extension
underscore = "_"
filename = "$directory$filename_prefix$numinputs$underscore$rseed$underscore$num_levels$underscore$runs_per_goal$filetype"
ost = open(filename,"w")
max_gens = numinputs == 2 ? 10000 : 50000
@otime ost = evolve_goals(ost,filename,numinputs,num_levels,runs_per_goal,max_gens,rseed)
@otime ost = evolve_goals(ost,filename,numinputs,num_levels,runs_per_goal,max_gens,rseed,use_cache)
close(ost)
end

Expand All @@ -49,27 +54,25 @@ end
return Parameters(mu, lambda, mutrate, targetfitness, numinputs, numoutputs, numperlevel, numlevels, numlevelsback, funcs, fitfunc)
end

@everywhere function global_setup(numinputs,maxgens)
global num_inputs = numinputs
global max_gens = maxgens
num_inputs
end

# This is the function used by pmap to evolve each goal in goal_list
# This is the function used by mape and pmap to evolve each goal in goal_list
@everywhere function p_mu_lambda(g)
(ch,gens) = mu_lambda(p,g,max_gens)
n = ch.number_active_nodes
(ch,gens) = mu_lambda(p,g,max_gens,usecache)
if ch.has_cache
n = ch.cache.number_active_nodes
else
n = 0
end
(g.truth_table,gens,n)
end

@everywhere function goal_list_setup(numinputs,num_levels,runs_per_goal,maxgens)
#println("goal list setup: proc:",myid()," numinputs:",numinputs)
@everywhere function goal_list_setup(numinputs,num_levels,runs_per_goal,maxgens,use_cache)
global mutrate = 0.05
global mu = 1
global lambda = 4
global num_inputs = numinputs
global max_gens = maxgens
global num_goals = 2^2^numinputs
global usecache = use_cache
global p = Params(mu,lambda,numinputs,1,1,num_levels,num_levels,mutrate,raman_funcs)
global goal_list = [Goal(numinputs,(convert(BitString,div(i,runs_per_goal)),)) for i in 0:num_goals*runs_per_goal-1]
length(goal_list)
Expand Down Expand Up @@ -98,15 +101,17 @@ end
# For each goal, the output is the goal, the average number of generations, and the average number of active nodes.
# Creates a CSV file with the parameter settings followed by the above described output for each run.
# Also writes this information to stdout so that the user can see the progress made.
function evolve_goals( outstream::IOStream, summary::String, num_inputs, num_levels, runs_per_goal, max_gens, rseed)
function evolve_goals( outstream::IOStream, summary::String,
num_inputs, num_levels, runs_per_goal, max_gens, rseed, use_cache::Bool=false)
for proc in procs()
lg = remotecall_fetch(proc,goal_list_setup,num_inputs, num_levels, runs_per_goal, max_gens )
lg = remotecall_fetch(proc,goal_list_setup,num_inputs, num_levels, runs_per_goal, max_gens, use_cache )
end
println(outstream,summary)
println(outstream,Dates.now())
print(outstream, "host: ",readall(`hostname`))
println(outstream,"num processes: ",length(procs()))
print(outstream,readall(`julia -v`))
println(outstream,"use cache: ",usecache)
println(outstream,"num inputs: ",p.numinputs)
println(outstream,"num outputs: ",p.numoutputs)
println(outstream,"num per level: ",p.numperlevel)
Expand Down
3 changes: 3 additions & 0 deletions src/CGP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ module CGP
include("BitString.jl")
include("Func.jl")
include("Parameters.jl")
include("NodeCache.jl")
include("ChromosomeCache.jl")
include("Node.jl")
include("Chromosome.jl")
include("Goal.jl")
include("Fitness.jl")
include("Mutate.jl")
include("Execute.jl")
include("ExecuteCache.jl")
include("Evolution.jl")
include("Utilities.jl")

Expand Down
56 changes: 30 additions & 26 deletions src/Chromosome.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ type Chromosome
inputs::Vector{InputNode}
interiors::Matrix{InteriorNode}
outputs::Vector{OutputNode}
active_set::Bool
number_active_nodes::Integer
has_cache::Bool
cache::Union(Nothing,ChromosomeCache) # If has_cache is false, then cache should be nothing.
end

function Chromosome(p::Parameters)
function Chromosome(p::Parameters,use_cache::Bool=false)
inputs = Array(InputNode, p.numinputs)
interiors = Array(InteriorNode, p.numlevels, p.numperlevel)
outputs = Array(OutputNode, p.numoutputs)
fitness = 0.0
active_set = false
number_active_nodes = 0

return Chromosome(p, inputs, interiors, outputs, active_set, number_active_nodes)
if use_cache
chc = ChromosomeCache(p,use_cache)
else
chc = nothing
end
return Chromosome(p, inputs, interiors, outputs, use_cache, chc )
end

function getindex(c::Chromosome, level::Integer, index::Integer)
Expand All @@ -35,26 +36,32 @@ function getindex(c::Chromosome, level::Integer, index::Integer)
end

# Prints the chromosome in a compact text format (on one line). Active notes
# are indicated with a "+" and inactive notes with a "*". If the chromosome
# has not been evaluated, the active field is set to "?" since the status of
# the nodes (with the exception of output nodes) is unknown. If active_only is
# are indicated with a "+" and inactive notes with a "*". If active_only is
# true, then only the active notes are shown.
function print_chromosome(c::Chromosome, active_only::Bool)
active_set = c.active_set
# TO DO: A version that prints to a stream and a version that prints to a string
function print_chromosome(c::Chromosome, active_only::Bool=false)
has_cache_incoming = c.has_cache
if ! c.has_cache
c.cache = ChromosomeCache(c.params,true)
c.has_cache = true
end
if ! c.cache.outputs[1].active # will be true if chromosome has been executed
execute_chromosome(c) # execute chromosome so that active nodes will be determined
end

# Input nodes
for i = 1:length(c.inputs)
if c.inputs[i].active || !active_only
active = c.inputs[i].active ? "+" : (active_set ? "*" : "?")
if c.cache.inputs[i].active || !active_only
active = c.cache.inputs[i].active ? "+" : "*"
print("[in", i, active, "] ")
end
end

# Interior nodes
for i = 1:c.params.numlevels
for j = 1:c.params.numperlevel
if c.interiors[i,j].active || !active_only
active = c.interiors[i,j].active ? "+" : (active_set ? "*" : "?")
if c.cache.interiors[i,j].active || !active_only
active = c.cache.interiors[i,j].active ? "+" : "*"
print("[")
for k = 1:length(c.interiors[i,j].inputs)
if c.interiors[i,j].inputs[k][1] == 0
Expand All @@ -70,17 +77,14 @@ function print_chromosome(c::Chromosome, active_only::Bool)

# Output nodes
for i = 1:length(c.outputs)
active = c.outputs[i].active ? "+" : (active_set ? "*" : "?")
active = c.cache.outputs[i].active ? "+" : "*"
print("[", c.outputs[i].input, "out", i, active, "] ")
end
println()
c.has_cache = has_cache_incoming # reset to original status
return
end

function print_chromosome(c::Chromosome)
print_chromosome(c, false)
end

function first_in_level(p::Parameters, level::Integer)
if level == 0
first = 1
Expand Down Expand Up @@ -116,12 +120,12 @@ function random_node_position(p::Parameters, minlevel::Integer, maxlevel::Intege
return (level, index)
end

function random_chromosome(p::Parameters )
return random_chromosome(p, p.funcs)
function random_chromosome(p::Parameters, use_cache::Bool=false )
return random_chromosome(p, p.funcs,use_cache)
end

function random_chromosome(p::Parameters, funcs::Vector{Func})
c = Chromosome(p)
function random_chromosome(p::Parameters, funcs::Vector{Func}, use_cache::Bool=false)
c = Chromosome(p,use_cache)

for index = 1:length(c.inputs)
c.inputs[index] = InputNode(index)
Expand Down
48 changes: 48 additions & 0 deletions src/ChromosomeCache.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Base.getindex

export ChromosomeCache, getindex

# Inputs, interiors, and outputs can contain NodeCaches that store the active status of the Chromosome that owns the ChromosomeCache
# See NodeCache.jl for details about the NodeCache type.
type ChromosomeCache
params::Parameters
inputs::Vector{InputNodeCache}
interiors::Matrix{InteriorNodeCache}
outputs::Vector{OutputNodeCache}
number_active_nodes::Int
end

# if use_cache == false, then interiors, inputs, outputs are arrays whose contents are undefined.
# if use_cache == true, then interiors, inputs, outputs are arrays whose contents are NodeCaches.
function ChromosomeCache(p::Parameters, use_cache::Bool = false )
number_active_nodes = 0
interiors = Array(InteriorNodeCache, p.numlevels, p.numperlevel)
if use_cache
inputs = [ InputNodeCache() for i in 1:p.numinputs ]
outputs = [ OutputNodeCache() for i in 1:p.numoutputs ]
for i in 1:p.numlevels
for j in 1:p.numperlevel
interiors[i,j] = InteriorNodeCache()
end
end
else
inputs = Array(InputNodeCache, p.numinputs)
outputs = Array(OutputNodeCache, p.numoutputs)
end

return ChromosomeCache(p, inputs, interiors, outputs, number_active_nodes )
end

function getindex(c::ChromosomeCache, level::Integer, index::Integer)
if level == 0
return c.inputs[index]
end

if level > c.params.numlevels
return c.outputs[index]
end

return c.interiors[level, index]
end


4 changes: 2 additions & 2 deletions src/Evolution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ export mu_lambda
# mu is the number of parents and lambda is the number of children in each generation
# mu and lambda are components of the parameters
# gens is the maximum number of generations run
function mu_lambda(p::Parameters, goal::Goal, gens::Integer)
function mu_lambda(p::Parameters, goal::Goal, gens::Integer, use_cache::Bool=false)
mu = p.mu
lambda = p.lambda
funcs = p.funcs
fitfunc = p.fitfunc
perfect = p.targetfitness

pop = [random_chromosome(p) for _ in 1:(mu+lambda) ]
pop = [random_chromosome(p,use_cache) for _ in 1:(mu+lambda) ]
fit = [fitness(c,goal,fitfunc) for c in pop ]
perm = sortperm(fit, rev=true)

Expand Down
55 changes: 39 additions & 16 deletions src/Execute.jl
Original file line number Diff line number Diff line change
@@ -1,35 +1,55 @@
export execute_chromosome

function evaluate_node(c::Chromosome, node::InputNode, context::Vector{BitString})
if ! node.active
node.active = true
c.number_active_nodes += 1
return context[node.index]
end

function evaluate_node(c::Chromosome, node::InputNode, cache_node::InputNodeCache, context::Vector{BitString})
if ! cache_node.active
cache_node.active = true
c.cache.number_active_nodes += 1
end
return context[node.index]
end

function evaluate_node(c::Chromosome, node::InteriorNode, context::Vector{BitString})
if ! node.active
func = node.func
args = map(node.inputs[1:func.arity]) do position
(level, index) = position
evaluate_node(c, c[level, index], context)
end

return func.func(args...)
end

function evaluate_node(c::Chromosome, node::InteriorNode, cache_node::InteriorNodeCache, context::Vector{BitString})
if ! cache_node.active
func = node.func
args = map(node.inputs[1:func.arity]) do position
(level, index) = position
evaluate_node(c, c[level, index], context)
evaluate_node(c, c[level, index], c.cache[level,index], context)
end
node.active = true
node.cache = func.func(args...)
c.number_active_nodes += 1
cache_node.active = true
cache_node.cache = func.func(args...)
c.cache.number_active_nodes += 1
end
return node.cache
return cache_node.cache
end

function evaluate_node(c::Chromosome, node::OutputNode, context::Vector{BitString})
if ! node.active
node.active = true
(level, index) = node.input

return evaluate_node(c, c[level, index], context)
end

function evaluate_node(c::Chromosome, node::OutputNode, cache_node::OutputNodeCache, context::Vector{BitString})
if ! cache_node.active
cache_node.active = true
(level, index) = node.input
node.cache = evaluate_node(c, c[level, index], context)
c.number_active_nodes += 1
cache_node.cache = evaluate_node(c, c[level, index], c.cache[level,index], context)
c.cache.number_active_nodes += 1
end
return node.cache
return cache_node.cache
end

# TODO: Since we are caching the evaluation results we should no
Expand All @@ -38,8 +58,11 @@ end
# around.

function execute_chromosome(c::Chromosome, context::Vector{BitString})
c.active_set = true
return BitString[evaluate_node(c, node, context) for node = c.outputs]
if c.has_cache
return BitString[evaluate_node(c, c.outputs[i], c.cache.outputs[i], context) for i in 1:length(c.outputs)]
else
return BitString[evaluate_node(c, node, context) for node = c.outputs]
end
end

# Executes chrososome using the standard input context
Expand Down
Loading