From 66ee694a4171efab2e09909353b4d998c6338311 Mon Sep 17 00:00:00 2001 From: Phil Tomson Date: Wed, 24 Feb 2021 14:28:06 -0800 Subject: [PATCH 1/3] added count_ones and parity examples --- cfg/count_ones.yaml | 21 ++++++++++ cfg/parity.yaml | 21 ++++++++++ graphing/graph_utils.jl | 5 ++- scripts/count_ones.jl | 87 +++++++++++++++++++++++++++++++++++++++++ scripts/parity.jl | 82 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 cfg/count_ones.yaml create mode 100644 cfg/parity.yaml create mode 100644 scripts/count_ones.jl create mode 100644 scripts/parity.jl diff --git a/cfg/count_ones.yaml b/cfg/count_ones.yaml new file mode 100644 index 0000000..60c8e66 --- /dev/null +++ b/cfg/count_ones.yaml @@ -0,0 +1,21 @@ +# Cambrian.jl settings +seed: 0 +d_fitness: 1 +n_population: 5 +n_elite: 1 +n_gen: 1000 +log_gen: 10 +save_gen: 10 +m_rate: 0.1 +# CartesianGeneticProgramming.jl settings +rows: 4 +columns: 8 +recur: 0.2 +n_in: 60 +n_out: 4 +out_m_rate: 0.3 +functions: + - f_and + - f_or + - f_xor + - f_not diff --git a/cfg/parity.yaml b/cfg/parity.yaml new file mode 100644 index 0000000..b7f92fa --- /dev/null +++ b/cfg/parity.yaml @@ -0,0 +1,21 @@ +# Cambrian.jl settings +seed: 0 +d_fitness: 1 +n_population: 5 +n_elite: 1 +n_gen: 3000 +log_gen: 10 +save_gen: 10 +m_rate: 0.1 +# CartesianGeneticProgramming.jl settings +rows: 2 +columns: 16 +recur: 0.0 +n_in: 8 +n_out: 1 +out_m_rate: 0.3 +functions: + - f_and + - f_or + - f_xor + - f_not diff --git a/graphing/graph_utils.jl b/graphing/graph_utils.jl index ed85328..7fc4ebb 100644 --- a/graphing/graph_utils.jl +++ b/graphing/graph_utils.jl @@ -1,10 +1,11 @@ -using CGP +using CartesianGeneticProgramming using LightGraphs using MetaGraphs using TikzGraphs using TikzPictures using LaTeXStrings -using Base.Test +using Printf +#using Base.Test function to_graph(c::Chromosome; active_outputs=trues(c.nout)) actives = [n.active for n in c.nodes] diff --git a/scripts/count_ones.jl b/scripts/count_ones.jl new file mode 100644 index 0000000..5a293dd --- /dev/null +++ b/scripts/count_ones.jl @@ -0,0 +1,87 @@ +using CartesianGeneticProgramming +using Cambrian +import Cambrian.mutate +using StatsBase +using Base.Iterators: repeated +""" +A simple example demonstrating symbolic regression on the iris dataset. For real +application, should be improved with a validation set, data shuffling, and +lexicase selection. +""" +function rand_bitarray(len) + rand(len) .< 0.5 + end + + function make_rand_array(num_ones, len) + ary = zeros(len) + rnd_idxs = sample(1:len, num_ones, replace=false) + for idx in rnd_idxs + ary[idx] = 1 + end + BitArray(ary) + end + + function bitarray_2_num(arr) + arr = reverse(arr) + sum(((i, x),) -> Int(x) << ((i-1) * sizeof(x)), enumerate(arr.chunks)) + end + + function create_testcases(num_tcs, sz, classes=10) + train_cases = [] + test_cases = [] + test_dict = Dict( ) #throw in 1 ( maps num to number of 1s) + function make_uniq_cases(cases, num) + for i in 1:floor(num) + num_ones = rand(1:classes) + ra = make_rand_array(num_ones, sz) + #make sure it's not already in the dict + while haskey(test_dict, bitarray_2_num(ra)) + println("collision - retry") + @show i + @show ra + @show num_ones + num_ones = rand(1:classes) + ra = make_rand_array(num_ones, sz) + end + push!(cases, (Array(ra), num_ones) ) + test_dict[bitarray_2_num(ra)] = num_ones + end + end + make_uniq_cases(train_cases, num_tcs) + make_uniq_cases(test_cases, num_tcs/2) + return train_cases, test_cases + end + + NUMCLASSES = 10 + WIDTH = 16 #60 + + train, test = create_testcases(20000, WIDTH, NUMCLASSES) + trainX = [ x[1] for x in train ] + trainX = hcat(trainX...) + trainY = [ x[2] for x in train ] + testX = [ x[1] for x in test ] + testX = hcat(testX...) + testY = [ x[2] for x in test ] + + +#X, Y = data_setup() +X, Y = float(trainX), trainY + +function evaluate(ind::CGPInd, X::AbstractArray, Y::AbstractArray) + accuracy = 0.0 + for i in 1:size(X, 2) + out = process(ind, X[:, i]) + #if argmax(out) == argmax(Y[:, i]) + if out == digits(Y[i],base=2, pad=4) + accuracy += 1 + end + end + [accuracy / size(X, 1)] +end + +cfg = get_config("cfg/count_ones.yaml") +fit(i::CGPInd) = evaluate(i, X, Y) +mutate(i::CGPInd) = goldman_mutate(cfg, i) +e = CGPEvolution(cfg, fit) +println("run!") +run!(e) diff --git a/scripts/parity.jl b/scripts/parity.jl new file mode 100644 index 0000000..76aa1a7 --- /dev/null +++ b/scripts/parity.jl @@ -0,0 +1,82 @@ +using CartesianGeneticProgramming +using Cambrian +import Cambrian.mutate +using StatsBase +using Base.Iterators: repeated +""" +A simple example for calculating parity +""" +function rand_bitarray(len) + rand(len) .< 0.5 + end + + function make_rand_array(num_ones, len) + ary = zeros(len) + rnd_idxs = sample(1:len, num_ones, replace=false) + for idx in rnd_idxs + ary[idx] = 1 + end + BitArray(ary) + end + + function bitarray_2_num(arr) + arr = reverse(arr) + sum(((i, x),) -> Int(x) << ((i-1) * sizeof(x)), enumerate(arr.chunks)) + end + + #bitArray parity + parity(x) = isodd(sum(x)) + + function create_testcases(sz) + train_cases = [] + for i in 0:(2^sz-1) + bin = digits(i, base=2, pad=sz) + push!(train_cases, (bin, parity(bin))) + @show (bin, parity(bin)) + end + return train_cases + end + + NUMCLASSES = 10 + WIDTH = 8 #60 + + train = create_testcases(WIDTH) + trainX = [ x[1] for x in train ] + trainX = hcat(trainX...) + trainY = [ x[2] for x in train ] + + +X, Y = trainX, trainY + +function evaluate(ind::CGPInd, X::AbstractArray, Y::AbstractArray) + accuracy = 0.0 + for i in 1:size(X, 2) + out = process(ind, float(X[:, i])) + if out[1] == Int(Y[i]) + accuracy += 1 + end + end + [accuracy / size(X, 1)] +end + +function error_count(e, X::AbstractArray, Y::AbstractArray) + failed = 0 + for i in 1:size(X,2) + y_hat = process(e.population[5], float(X[:,i])) + if y_hat[1] != Int(Y[i]) + failed += 1 + end + end + failed +end + +cfg = get_config("cfg/parity.yaml") +fit(i::CGPInd) = evaluate(i, X, Y) +mutate(i::CGPInd) = goldman_mutate(cfg, i) +e = CGPEvolution(cfg, fit) +println("run!") +run!(e) + +@show e.population[5].nodes +errors = error_count(e,X,Y ) +println("$errors out of $(size(X,2)) entries") From f3b458b550d62fb87abd60f3ef19669d1ee50ffc Mon Sep 17 00:00:00 2001 From: Phil Tomson Date: Sat, 27 Feb 2021 16:27:55 -0800 Subject: [PATCH 2/3] added to_dot.jl to create a graphviz dot file --- cfg/count_ones.yaml | 8 ++++---- graphing/to_dot.jl | 29 +++++++++++++++++++++++++++++ scripts/count_ones.jl | 15 +++++++++++++++ scripts/parity.jl | 34 +++++++++++++++------------------- 4 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 graphing/to_dot.jl diff --git a/cfg/count_ones.yaml b/cfg/count_ones.yaml index 60c8e66..3e616c3 100644 --- a/cfg/count_ones.yaml +++ b/cfg/count_ones.yaml @@ -8,10 +8,10 @@ log_gen: 10 save_gen: 10 m_rate: 0.1 # CartesianGeneticProgramming.jl settings -rows: 4 -columns: 8 -recur: 0.2 -n_in: 60 +rows: 2 +columns: 64 +recur: 0.0 +n_in: 16 n_out: 4 out_m_rate: 0.3 functions: diff --git a/graphing/to_dot.jl b/graphing/to_dot.jl new file mode 100644 index 0000000..c4c1563 --- /dev/null +++ b/graphing/to_dot.jl @@ -0,0 +1,29 @@ +function walk_nodes(ind::CGPInd) + dot_strs = [] + push!(dot_strs, "digraph cgpgraph {\n") + function visit(node_num, current_nd) + if current_nd.active + fname = String(Symbol(current_nd.f)) + visit(current_nd.x, ind.nodes[current_nd.x]) + if(CGPFunctions.arity[fname] > 1) + visit(current_nd.y, ind.nodes[current_nd.y]) + + end + @show (node_num, current_nd) + + xdotline = "N$(current_nd.x)-> N$node_num ;" + push!(dot_strs, "N$node_num [label=\"$fname:$node_num\"];") + push!(dot_strs, xdotline) + if(CGPFunctions.arity[fname] > 1) + ydotline = "N$(current_nd.y)-> N$node_num ;" + push!(dot_strs, ydotline) + end + end + end + for current_node_num in ind.outputs + current_node = ind.nodes[current_node_num] + visit(current_node_num, current_node) + end + push!(dot_strs, "}") + dot_strs +end diff --git a/scripts/count_ones.jl b/scripts/count_ones.jl index 5a293dd..252522a 100644 --- a/scripts/count_ones.jl +++ b/scripts/count_ones.jl @@ -79,9 +79,24 @@ function evaluate(ind::CGPInd, X::AbstractArray, Y::AbstractArray) [accuracy / size(X, 1)] end +function error_count(e, X::AbstractArray, Y::AbstractArray) + failed = 0 + for i in 1:size(X,2) + y_hat = process(e.population[5], float(X[:,i])) + if y_hat[1] != float(digits(Int(Y[i]), base=2, pad=4)) + failed += 1 + end + end + failed +end + cfg = get_config("cfg/count_ones.yaml") fit(i::CGPInd) = evaluate(i, X, Y) mutate(i::CGPInd) = goldman_mutate(cfg, i) e = CGPEvolution(cfg, fit) println("run!") run!(e) + +@show e.population[5].nodes +errors = error_count(e,X,Y ) +println("$errors out of $(size(X,2)) entries") diff --git a/scripts/parity.jl b/scripts/parity.jl index 76aa1a7..b20b770 100644 --- a/scripts/parity.jl +++ b/scripts/parity.jl @@ -3,27 +3,11 @@ using Cambrian import Cambrian.mutate using StatsBase using Base.Iterators: repeated +include("../graphing/to_dot.jl") """ A simple example for calculating parity """ -function rand_bitarray(len) - rand(len) .< 0.5 - end - function make_rand_array(num_ones, len) - ary = zeros(len) - rnd_idxs = sample(1:len, num_ones, replace=false) - for idx in rnd_idxs - ary[idx] = 1 - end - BitArray(ary) - end - - function bitarray_2_num(arr) - arr = reverse(arr) - sum(((i, x),) -> Int(x) << ((i-1) * sizeof(x)), enumerate(arr.chunks)) - end - #bitArray parity parity(x) = isodd(sum(x)) @@ -77,6 +61,18 @@ e = CGPEvolution(cfg, fit) println("run!") run!(e) -@show e.population[5].nodes +@show e.population[end].nodes errors = error_count(e,X,Y ) -println("$errors out of $(size(X,2)) entries") +println("$errors errors out of $(size(X,2)) entries") + +#generate dot file +dot_array = walk_nodes(e.population[5]) +dot_file = "parity_graph.dot" +open(dot_file, "w") do f + for i in dot_array + println(f, i) + end +end +#from commandline: +# $ dot -Tpng parity_graph.dot -o parity_graph.png +# $ display \ No newline at end of file From 1b7a3c00a0990cd7964cb07d65451010a34a7cfd Mon Sep 17 00:00:00 2001 From: Phil Tomson Date: Sat, 27 Feb 2021 16:35:13 -0800 Subject: [PATCH 3/3] remove count_ones example --- cfg/count_ones.yaml | 21 --------- scripts/count_ones.jl | 102 ------------------------------------------ scripts/parity.jl | 4 +- 3 files changed, 2 insertions(+), 125 deletions(-) delete mode 100644 cfg/count_ones.yaml delete mode 100644 scripts/count_ones.jl diff --git a/cfg/count_ones.yaml b/cfg/count_ones.yaml deleted file mode 100644 index 3e616c3..0000000 --- a/cfg/count_ones.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Cambrian.jl settings -seed: 0 -d_fitness: 1 -n_population: 5 -n_elite: 1 -n_gen: 1000 -log_gen: 10 -save_gen: 10 -m_rate: 0.1 -# CartesianGeneticProgramming.jl settings -rows: 2 -columns: 64 -recur: 0.0 -n_in: 16 -n_out: 4 -out_m_rate: 0.3 -functions: - - f_and - - f_or - - f_xor - - f_not diff --git a/scripts/count_ones.jl b/scripts/count_ones.jl deleted file mode 100644 index 252522a..0000000 --- a/scripts/count_ones.jl +++ /dev/null @@ -1,102 +0,0 @@ -using CartesianGeneticProgramming -using Cambrian -import Cambrian.mutate -using StatsBase -using Base.Iterators: repeated -""" -A simple example demonstrating symbolic regression on the iris dataset. For real -application, should be improved with a validation set, data shuffling, and -lexicase selection. -""" -function rand_bitarray(len) - rand(len) .< 0.5 - end - - function make_rand_array(num_ones, len) - ary = zeros(len) - rnd_idxs = sample(1:len, num_ones, replace=false) - for idx in rnd_idxs - ary[idx] = 1 - end - BitArray(ary) - end - - function bitarray_2_num(arr) - arr = reverse(arr) - sum(((i, x),) -> Int(x) << ((i-1) * sizeof(x)), enumerate(arr.chunks)) - end - - function create_testcases(num_tcs, sz, classes=10) - train_cases = [] - test_cases = [] - test_dict = Dict( ) #throw in 1 ( maps num to number of 1s) - function make_uniq_cases(cases, num) - for i in 1:floor(num) - num_ones = rand(1:classes) - ra = make_rand_array(num_ones, sz) - #make sure it's not already in the dict - while haskey(test_dict, bitarray_2_num(ra)) - println("collision - retry") - @show i - @show ra - @show num_ones - num_ones = rand(1:classes) - ra = make_rand_array(num_ones, sz) - end - push!(cases, (Array(ra), num_ones) ) - test_dict[bitarray_2_num(ra)] = num_ones - end - end - make_uniq_cases(train_cases, num_tcs) - make_uniq_cases(test_cases, num_tcs/2) - return train_cases, test_cases - end - - NUMCLASSES = 10 - WIDTH = 16 #60 - - train, test = create_testcases(20000, WIDTH, NUMCLASSES) - trainX = [ x[1] for x in train ] - trainX = hcat(trainX...) - trainY = [ x[2] for x in train ] - testX = [ x[1] for x in test ] - testX = hcat(testX...) - testY = [ x[2] for x in test ] - - -#X, Y = data_setup() -X, Y = float(trainX), trainY - -function evaluate(ind::CGPInd, X::AbstractArray, Y::AbstractArray) - accuracy = 0.0 - for i in 1:size(X, 2) - out = process(ind, X[:, i]) - #if argmax(out) == argmax(Y[:, i]) - if out == digits(Y[i],base=2, pad=4) - accuracy += 1 - end - end - [accuracy / size(X, 1)] -end - -function error_count(e, X::AbstractArray, Y::AbstractArray) - failed = 0 - for i in 1:size(X,2) - y_hat = process(e.population[5], float(X[:,i])) - if y_hat[1] != float(digits(Int(Y[i]), base=2, pad=4)) - failed += 1 - end - end - failed -end - -cfg = get_config("cfg/count_ones.yaml") -fit(i::CGPInd) = evaluate(i, X, Y) -mutate(i::CGPInd) = goldman_mutate(cfg, i) -e = CGPEvolution(cfg, fit) -println("run!") -run!(e) - -@show e.population[5].nodes -errors = error_count(e,X,Y ) -println("$errors out of $(size(X,2)) entries") diff --git a/scripts/parity.jl b/scripts/parity.jl index b20b770..aaf7c87 100644 --- a/scripts/parity.jl +++ b/scripts/parity.jl @@ -73,6 +73,6 @@ open(dot_file, "w") do f println(f, i) end end -#from commandline: +# to view from commandline: # $ dot -Tpng parity_graph.dot -o parity_graph.png -# $ display \ No newline at end of file +# $ display parity_graph.png \ No newline at end of file