charlie 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +10 -0
- data/Manifest.txt +6 -1
- data/README.txt +1 -1
- data/Rakefile +1 -1
- data/TODO.txt +2 -7
- data/data/BENCHMARK +4 -0
- data/data/CROSSOVER +10 -2
- data/data/GENOTYPE +23 -3
- data/data/MUTATION +15 -3
- data/data/SELECTION +15 -8
- data/examples/EXAMPLES_README.txt +58 -37
- data/examples/coevolution.rb +42 -1
- data/examples/matrix.rb +49 -0
- data/examples/neural.rb +20 -0
- data/examples/output/function_optimization_sombrero.html +1518 -1518
- data/examples/output/tsp.html +551 -551
- data/examples/string.rb +3 -0
- data/examples/tree.rb +15 -1
- data/examples/tsp.rb +1 -2
- data/lib/charlie.rb +5 -1
- data/lib/charlie/crossover.rb +1 -0
- data/lib/charlie/etc/monkey.rb +5 -4
- data/lib/charlie/gabenchmark.rb +24 -3
- data/lib/charlie/genotype.rb +19 -2
- data/lib/charlie/list/list_crossover.rb +40 -0
- data/lib/charlie/list/list_genotype.rb +6 -2
- data/lib/charlie/list/matrix.rb +121 -0
- data/lib/charlie/list/neural.rb +52 -0
- data/lib/charlie/mutate.rb +1 -0
- data/lib/charlie/population.rb +84 -21
- data/lib/charlie/selection.rb +88 -30
- data/lib/charlie/tree/tree.rb +1 -0
- data/test/t_common.rb +1 -1
- data/test/test_benchmark.rb +18 -0
- data/test/test_cross.rb +20 -0
- data/test/test_evolve.rb +25 -1
- data/test/test_matrix.rb +36 -0
- data/test/test_neural.rb +24 -0
- data/test/test_sel.rb +36 -0
- metadata +10 -3
- data/examples/output/bitstring_royalroad.html +0 -1243
data/examples/string.rb
CHANGED
@@ -92,7 +92,10 @@ loop{
|
|
92
92
|
}
|
93
93
|
puts 'Found walk of length 25:', pop[-1].genes.inspect
|
94
94
|
|
95
|
+
puts "Second try, no output but shorter code."
|
96
|
+
pop, gens = Population.evolve_multiple_until(Walk,20,1000){|b| b.fitness == 25 }
|
95
97
|
|
98
|
+
puts "Found walk of length 25 in #{gens} generations:", pop.best.genes.inspect
|
96
99
|
|
97
100
|
|
98
101
|
end
|
data/examples/tree.rb
CHANGED
@@ -92,6 +92,20 @@ solutions found for N=31:
|
|
92
92
|
pop = Population.new(PORS).evolve_on_console(500)
|
93
93
|
pp pop.max.genes
|
94
94
|
|
95
|
+
elsif ARGV[0]=='xor'
|
96
|
+
class Object; def not; !self; end;end
|
97
|
+
|
98
|
+
class XOR < TreeGenotype([:A,:B,true,false],[:not], [:&,:|])
|
99
|
+
def fitness
|
100
|
+
[[false,false],[false,true],[true,false],[true,true]].count{|a,b| eval_genes(:A=>a,:B=>b) == a^b } - $size_fac * size
|
101
|
+
end
|
102
|
+
cache_fitness
|
103
|
+
end
|
104
|
+
$size_fac = 0.01
|
105
|
+
pop, gen = Population.new(XOR).evolve_until(2000){|best| best.fitness > 3.9 }
|
106
|
+
puts "Generations needed (nil=no convergence) : #{gen}"
|
107
|
+
puts "Solution: #{pop.best}, fitness #{pop.best.fitness}"
|
108
|
+
|
95
109
|
|
96
110
|
elsif ARGV[0]=='bloat'
|
97
111
|
|
@@ -111,5 +125,5 @@ pop = Population.new(Bloat).evolve_on_console(200)
|
|
111
125
|
|
112
126
|
else
|
113
127
|
puts "Several examples for tree genotypes."
|
114
|
-
puts "Usage: ruby tree.rb cos|pors|porsx|bloat"
|
128
|
+
puts "Usage: ruby tree.rb cos|pors|porsx|xor|parity6|bloat"
|
115
129
|
end
|
data/examples/tsp.rb
CHANGED
@@ -26,8 +26,7 @@ class TSP < PermutationGenotype(CITIES.size)
|
|
26
26
|
end
|
27
27
|
|
28
28
|
puts "Running GA until the optimal solution has been found."
|
29
|
-
gen =
|
30
|
-
pop, gen = Population.new(TSP,30).evolve_until(1000,100){|b| puts b.fitness; b.fitness > -(N*N+1) } while gen.nil?
|
29
|
+
pop, gen = Population.evolve_multiple_until(TSP,30,100,1000,100){|b| puts b.fitness; b.fitness > -(N*N+1) }
|
31
30
|
puts "Fitness: #{pop.max.fitness}"
|
32
31
|
puts "Generations needed: #{gen.inspect}"
|
33
32
|
p pop.max.genes.map{|a| CITIES[a]}
|
data/lib/charlie.rb
CHANGED
@@ -4,7 +4,7 @@ $:.unshift File.dirname(__FILE__)
|
|
4
4
|
|
5
5
|
# This is just a dummy module to avoid making the VERSION constant a global.
|
6
6
|
module Charlie
|
7
|
-
VERSION = '0.
|
7
|
+
VERSION = '0.8.0'
|
8
8
|
end
|
9
9
|
|
10
10
|
require 'charlie/etc/monkey'
|
@@ -24,6 +24,10 @@ require 'charlie/list/list_mutate'
|
|
24
24
|
require 'charlie/list/list_crossover'
|
25
25
|
require 'charlie/list/list_genotype'
|
26
26
|
|
27
|
+
require 'charlie/list/matrix'
|
28
|
+
|
29
|
+
require 'charlie/list/neural'
|
30
|
+
|
27
31
|
require 'charlie/permutation/permutation'
|
28
32
|
|
29
33
|
require 'charlie/tree/tree'
|
data/lib/charlie/crossover.rb
CHANGED
@@ -23,6 +23,7 @@ end
|
|
23
23
|
|
24
24
|
# Takes crossover c1 with probability p, and crossover c2 with probability 1-p
|
25
25
|
def PCross(p,c1,c2=NullCrossover)
|
26
|
+
raise ArgumentError, "first argument to PCross should be numeric (probability)." unless p.is_a?(Numeric)
|
26
27
|
return c1 if c1==c2
|
27
28
|
c1_name, c2_name = [c1,c2].map{|c| '_cross_' + c.to_s.gsub(/[^A-Za-z0-9]/,'') }
|
28
29
|
Module.new{
|
data/lib/charlie/etc/monkey.rb
CHANGED
@@ -32,10 +32,11 @@ module Enumerable
|
|
32
32
|
end
|
33
33
|
|
34
34
|
class Array
|
35
|
-
|
36
|
-
def shuffle
|
37
|
-
|
38
|
-
|
35
|
+
if RUBY_VERSION < '1.9'
|
36
|
+
def shuffle ; sort_by { rand }; end
|
37
|
+
def shuffle!; replace shuffle; end
|
38
|
+
def find_index(&b); index find(&b); end
|
39
|
+
end
|
39
40
|
|
40
41
|
def dot_product(v)
|
41
42
|
r=0.0
|
data/lib/charlie/gabenchmark.rb
CHANGED
@@ -12,7 +12,9 @@ module GABenchmark
|
|
12
12
|
generations = dsl_obj.generations
|
13
13
|
population_size = dsl_obj.population_size
|
14
14
|
repeat_tests = dsl_obj.repeat
|
15
|
-
|
15
|
+
setup_proc = dsl_obj.setup
|
16
|
+
teardown_proc = dsl_obj.teardown
|
17
|
+
|
16
18
|
track_stat = dsl_obj.track_stat
|
17
19
|
|
18
20
|
n_tests = all_tests.size
|
@@ -30,8 +32,14 @@ module GABenchmark
|
|
30
32
|
|
31
33
|
test_stats = (0...repeat_tests).map{
|
32
34
|
print '.'; $stdout.flush
|
33
|
-
|
35
|
+
|
36
|
+
population = Population.new(gclass,population_size)
|
37
|
+
setup_proc.call(population)
|
38
|
+
best = population.evolve_silent(generations).last
|
39
|
+
|
34
40
|
stat = track_stat.call(best)
|
41
|
+
teardown_proc.call(population)
|
42
|
+
|
35
43
|
overall_best = [best, stat] if overall_best[0].nil? || (overall_best[1] <=> stat) < 0 # use <=> to allow arrays
|
36
44
|
stat
|
37
45
|
}
|
@@ -159,7 +167,8 @@ class StrategiesDSL
|
|
159
167
|
def initialize
|
160
168
|
@repeat = 10
|
161
169
|
@population_size = 20
|
162
|
-
@generations = 50
|
170
|
+
@generations = 50
|
171
|
+
@setup = @teardown = proc{}
|
163
172
|
selection []
|
164
173
|
crossover []
|
165
174
|
mutator []
|
@@ -176,6 +185,18 @@ class StrategiesDSL
|
|
176
185
|
end
|
177
186
|
alias :track_stats :track_stat
|
178
187
|
|
188
|
+
# Pass a block that does the setup. Rarely needed. The proc is passes the population before each test (i.e. between Population.new and #evolve_silent)
|
189
|
+
def setup(&b)
|
190
|
+
return @setup unless block_given?
|
191
|
+
@setup = b
|
192
|
+
end
|
193
|
+
|
194
|
+
# Pass a block that does the setup. Rarely needed. Called with the population as argument AFTER track_stats.
|
195
|
+
def teardown(&b)
|
196
|
+
return @teardown unless block_given?
|
197
|
+
@teardown = b
|
198
|
+
end
|
199
|
+
|
179
200
|
# Get all the tests. Basically a cartesian product of all selection, crossover and mutation methods.
|
180
201
|
def get_tests
|
181
202
|
t = []
|
data/lib/charlie/genotype.rb
CHANGED
@@ -36,7 +36,7 @@ class Genotype
|
|
36
36
|
|
37
37
|
# Creates a new instance of your genotype class given its genes.
|
38
38
|
def from_genes(g)
|
39
|
-
r = allocate
|
39
|
+
r = allocate
|
40
40
|
r.genes = g
|
41
41
|
r
|
42
42
|
end
|
@@ -52,6 +52,9 @@ class Genotype
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
def clear_cache
|
56
|
+
@fitness_cache = nil
|
57
|
+
end
|
55
58
|
# Used by Genotype.cache_fitness. This accessor can be used to clear the cache.
|
56
59
|
# Also could be used by niche selection, etc. as a place to change the effective fitness w/o changing the actual selection algorithms.
|
57
60
|
attr_accessor :fitness_cache
|
@@ -61,13 +64,27 @@ class Genotype
|
|
61
64
|
end
|
62
65
|
|
63
66
|
def mutate
|
64
|
-
dup
|
67
|
+
cp = dup
|
68
|
+
cp.mutate!
|
69
|
+
cp
|
65
70
|
end
|
66
71
|
|
67
72
|
def to_s
|
68
73
|
@genes.to_s
|
69
74
|
end
|
70
75
|
|
76
|
+
# Defines fight in terms of fight_points (self wins if it has at least as many points as other). Used in co-evolution.
|
77
|
+
#def fight(other)
|
78
|
+
# r = fight_points(other)
|
79
|
+
# return r[0]>=r[1]
|
80
|
+
#end
|
81
|
+
|
82
|
+
# Defines fight_points in terms of fight (1 point for the winner, 0 for the loser). Used in co-evolution.
|
83
|
+
##def fight_points(other)
|
84
|
+
# fight(other) ? [1,0] : [0,1]
|
85
|
+
#end
|
86
|
+
|
87
|
+
|
71
88
|
include Comparable
|
72
89
|
def <=>(b)
|
73
90
|
fitness <=> b.fitness
|
@@ -29,6 +29,8 @@ def NPointCrossover(n=2)
|
|
29
29
|
}
|
30
30
|
}
|
31
31
|
end
|
32
|
+
TwoPointCrossover = NPointCrossover(2)
|
33
|
+
ThreePointCrossover = NPointCrossover(2)
|
32
34
|
|
33
35
|
# Uniform crossover, returns two children.
|
34
36
|
module UniformCrossover
|
@@ -47,4 +49,42 @@ module UniformCrossover
|
|
47
49
|
end
|
48
50
|
|
49
51
|
|
52
|
+
# Blending crossover, common in evolutionary strategies.
|
53
|
+
# * Given two parents x1(i), x2(i)
|
54
|
+
# * Returns two children with as i'th elements y(i)x1(i)+(1-y(i))x2(i) and y(i)x2(i)+(1-y(i))x1(i)
|
55
|
+
# * type=:cube takes y(i) independent, so children roughly within the hypercube spanned by the parents.
|
56
|
+
# * type=:line takes y(i)=y(1), so children roughly on the line between the parents.
|
57
|
+
# * <tt>exploration_alpha</tt> defines how far outside the hypercube/line spanned by the parents the children can be. (more specifically, y(i) = (1+2*alpha)*rand - alpha)
|
58
|
+
def BlendingCrossover(exploration_alpha=0.1,type=:cube)
|
59
|
+
sz_rand = 1 + 2 * exploration_alpha
|
60
|
+
Module.new{
|
61
|
+
self.name = "BlendingCrossover(#{exploration_alpha},#{type})"
|
62
|
+
if(type==:cube)
|
63
|
+
define_method(:cross){|parent1,parent2|
|
64
|
+
c1 = []; c2=[]; g1 = parent1.genes; g2 = parent2.genes
|
65
|
+
g1.each_with_index{|e,i|
|
66
|
+
y = rand*sz_rand - exploration_alpha
|
67
|
+
x2 = g2[i]
|
68
|
+
c1 << y*e + (1-y)*x2
|
69
|
+
c2 << y*x2 + (1-y)*e
|
70
|
+
}
|
71
|
+
[c1,c2].map{|x| from_genes(x) }
|
72
|
+
}
|
73
|
+
elsif(type==:line)
|
74
|
+
define_method(:cross){|parent1,parent2|
|
75
|
+
c1 = []; c2=[]; g1 = parent1.genes; g2 = parent2.genes
|
76
|
+
y = rand*sz_rand - exploration_alpha
|
77
|
+
g1.each_with_index{|e,i|
|
78
|
+
x2 = g2[i]
|
79
|
+
c1 << y*e + (1-y)*x2
|
80
|
+
c2 << y*x2 + (1-y)*e
|
81
|
+
}
|
82
|
+
[c1,c2].map{|x| from_genes(x) }
|
83
|
+
}
|
84
|
+
else
|
85
|
+
raise ArgumentError,"Invalid BlendingCrossover type #{type}"
|
86
|
+
end
|
87
|
+
}
|
88
|
+
end
|
89
|
+
BlendingCrossover=BlendingCrossover()
|
50
90
|
|
@@ -8,7 +8,9 @@
|
|
8
8
|
def FloatListGenotype(n,range=0..1)
|
9
9
|
Class.new(Genotype) {
|
10
10
|
@@range = range
|
11
|
-
|
11
|
+
[self,metaclass].each{|c| c.class_eval{ # include both in class and metaclass
|
12
|
+
define_method(:size){ n }
|
13
|
+
}}
|
12
14
|
def initialize
|
13
15
|
@genes = Array.new(size){ rand * (@@range.end - @@range.begin) + @@range.begin }
|
14
16
|
end
|
@@ -23,7 +25,9 @@ end
|
|
23
25
|
# Individuals are initialized as an array of +n+ random bits.
|
24
26
|
def BitStringGenotype(n)
|
25
27
|
Class.new(Genotype) {
|
26
|
-
|
28
|
+
[self,metaclass].each{|c| c.class_eval{ # include both in class and metaclass
|
29
|
+
define_method(:size){ n }
|
30
|
+
}}
|
27
31
|
def initialize
|
28
32
|
@genes = Array.new(size){ rand(2) }
|
29
33
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
# Generic ancestor for matrix genotypes
|
5
|
+
def MatrixGenotype(rows,columns=rows)
|
6
|
+
Class.new(Genotype) {
|
7
|
+
[self,metaclass].each{|c| c.class_eval{
|
8
|
+
define_method(:rows) { rows }
|
9
|
+
define_method(:columns) { columns }
|
10
|
+
define_method(:size) { rows * columns }
|
11
|
+
}}
|
12
|
+
def genes=(g)
|
13
|
+
@genes = g.map(&:dup)
|
14
|
+
end
|
15
|
+
use MatrixUniformCrossover.dup
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Genotype for a 2D array of floats.
|
20
|
+
def FloatMatrixGenotype(rows,columns=rows,range=0..1)
|
21
|
+
Class.new(MatrixGenotype(rows,columns)) {
|
22
|
+
@@range = range
|
23
|
+
def initialize
|
24
|
+
self.genes = Array.new(rows){ Array.new(columns){ rand * (@@range.end - @@range.begin) + @@range.begin } }
|
25
|
+
end
|
26
|
+
def to_s
|
27
|
+
@genes.inspect
|
28
|
+
end
|
29
|
+
use MatrixMutator()
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Genotype for a 2D array of bits, for example: connection matrices for graphs
|
34
|
+
def BitMatrixGenotype(rows,columns=rows)
|
35
|
+
Class.new(MatrixGenotype(rows,columns)) {
|
36
|
+
def initialize
|
37
|
+
self.genes = Array.new(rows){ Array.new(columns){ rand(2) } }
|
38
|
+
end
|
39
|
+
def to_s
|
40
|
+
@genes.map{|r| r.map(&:to_s).join }.join("\n")
|
41
|
+
end
|
42
|
+
use MatrixMutator(:expected_n,:flip)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Uniform crossover for matrices
|
47
|
+
module MatrixUniformCrossover
|
48
|
+
def cross(parent1,parent2)
|
49
|
+
tc1 = []; tc2=[]
|
50
|
+
g1 = parent1.genes; g2 = parent2.genes
|
51
|
+
g1.each_with_index{|rg1,ri|
|
52
|
+
tc1 << (r1=[]); tc2 << (r2=[])
|
53
|
+
rg2 = g2[ri];
|
54
|
+
rg1.each_with_index{|e,i|
|
55
|
+
if rand(2).zero?
|
56
|
+
r1 << e; r2 << rg2[i]
|
57
|
+
else
|
58
|
+
r2 << e; r1 << rg2[i]
|
59
|
+
end
|
60
|
+
}
|
61
|
+
}
|
62
|
+
[tc1,tc2].map{|x| from_genes(x) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# The mutation strategies for MatrixMutator
|
67
|
+
# * :n_point[n=3] : Mutates a single element, n times. Example: :n_point, :n_point[2]
|
68
|
+
# * :probability[ p=0.05 ] : Mutates each element with probability p
|
69
|
+
# * :expected_n[n=3] : Mutates each element with probability n/genes.size, i.e. such that the expected # of mutations is n
|
70
|
+
# * :expected_n_per_row[n=3], :expected_n_per_column[n=3] : Likewise
|
71
|
+
MatrixMutationStrategies = {
|
72
|
+
:probability => MMSPRB = Proc.new{|genes,pointmut,p|
|
73
|
+
p ||= 0.05
|
74
|
+
genes.map!{|r| r.map!{|e| rand < p ? pointmut.call(e) : e } }
|
75
|
+
},
|
76
|
+
:expected_n => Proc.new{|genes,pointmut,n| n ||= 3
|
77
|
+
MMSPRB.call(genes,pointmut,n.to_f / (genes.size * genes[0].size))
|
78
|
+
},
|
79
|
+
:expected_n_per_row => Proc.new{|genes,pointmut,n| n ||= 3
|
80
|
+
MMSPRB.call(genes,pointmut,n.to_f / genes[0].size)
|
81
|
+
},
|
82
|
+
:expected_n_per_column => Proc.new{|genes,pointmut,n| n ||= 3
|
83
|
+
MMSPRB.call(genes,pointmut,n.to_f / genes.size)
|
84
|
+
},
|
85
|
+
:n_point => Proc.new{|genes,pointmut,n|
|
86
|
+
n ||= 3
|
87
|
+
s, r = genes.size, genes.rows;
|
88
|
+
n.times{ i,j = rand(s).divmod(r); genes[i][j] = pointmut.call(genes[i][j]) }
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
# Generates a module which can be used as a mutator for matrix-based genotypes like FloatMatrixGenotype and BitMatrixGenotype
|
93
|
+
# * strategy should be one of the MatrixMutationStrategies, or a proc
|
94
|
+
# * point_mutator should be one of the PointMutators (like in ListMutator), or a proc
|
95
|
+
# * nil is equivalent to proc{} for the point mutator
|
96
|
+
def MatrixMutator(strategy=:expected_n ,point_mutator=:uniform)
|
97
|
+
strat, *strat_args = *strategy
|
98
|
+
pm , *pm_args = *point_mutator
|
99
|
+
|
100
|
+
pm ||= proc{}
|
101
|
+
|
102
|
+
strat = MatrixMutationStrategies[strat.intern] unless strat.is_a? Proc
|
103
|
+
pm = PointMutators[pm.intern] unless pm.is_a? Proc
|
104
|
+
|
105
|
+
raise ArgumentError,"Invalid mutation strategy" if strat.nil?
|
106
|
+
raise ArgumentError,"Invalid point mutator" if point_mutator.nil?
|
107
|
+
|
108
|
+
if pm_args.empty?
|
109
|
+
point_mutator_with_args = pm
|
110
|
+
else
|
111
|
+
point_mutator_with_args = proc{|*args| pm.call(*(args+pm_args) ) }
|
112
|
+
end
|
113
|
+
|
114
|
+
Module.new{
|
115
|
+
define_method(:mutate!) {
|
116
|
+
strat.call(@genes,point_mutator_with_args,*strat_args)
|
117
|
+
self
|
118
|
+
}
|
119
|
+
self.name= "MatrixMutator(#{strategy.inspect},#{point_mutator.inspect})"
|
120
|
+
}
|
121
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# This file contains everything related to neural network genotypes
|
2
|
+
|
3
|
+
# The tanh function is the default
|
4
|
+
NN_TANH = proc{|x| Math.tanh x }
|
5
|
+
# The sign function is also commonly used
|
6
|
+
NN_SIGN = proc{|x| x>=0?1:-1 }
|
7
|
+
|
8
|
+
# Genotype for neural networks with a single hidden layer.
|
9
|
+
# * Inherits FloatListGenotype
|
10
|
+
# * input_n, hidden_n, output_n are the number of neurons at each layer
|
11
|
+
# * scaling determines how the floats in genes relate to the weights of the links (important for mutation size, initial values).
|
12
|
+
# * input to hidden weights are (list element) multiplied by scaling / input_n
|
13
|
+
# * hidden to output weights are (list element) multiplied by scaling / hidden_n
|
14
|
+
# * hidden_f, output_f are the (usually sigmoidal) functions to determine the output of nodes.
|
15
|
+
# * Output of hidden node i is hidden_f( weights . input - threshold)
|
16
|
+
# ----------------------------------------------------
|
17
|
+
# functions of the returned class:
|
18
|
+
# * output(input) - runs the neural network in some input
|
19
|
+
def NeuralNetworkGenotype(input_n,hidden_n,output_n=1,scaling=1.0,hidden_f=NN_TANH,output_f=NN_TANH)
|
20
|
+
links_n = hidden_n * (input_n + output_n)
|
21
|
+
thr_n = hidden_n + output_n
|
22
|
+
Class.new(FloatListGenotype(links_n+thr_n, -1..1 )) {
|
23
|
+
define_method(:output){|input|
|
24
|
+
raise ArgumentError unless input.size==input_n
|
25
|
+
si = scaling / input_n
|
26
|
+
sh = scaling / hidden_n
|
27
|
+
|
28
|
+
wts = genes
|
29
|
+
input = input.map{|i| i * si } # scale inputs instead of weights: same effect, but faster
|
30
|
+
|
31
|
+
hidden_val = Array.new(hidden_n,0.0)
|
32
|
+
output_val = Array.new(output_n,0.0)
|
33
|
+
(0...hidden_n).each{|i|
|
34
|
+
wi = i * input_n
|
35
|
+
thr = wts[links_n + i]
|
36
|
+
v = 0.0
|
37
|
+
(0...input_n).each{|j| v += input[j] * wts[wi + j] }
|
38
|
+
hidden_val[i] = hidden_f.call(v - thr) * sh # again, scale this instead of hidden->output weights
|
39
|
+
}
|
40
|
+
oi = hidden_n * input_n
|
41
|
+
(0...output_n).each{|i|
|
42
|
+
wi = oi + i * hidden_n
|
43
|
+
thr = wts[links_n + hidden_n + i]
|
44
|
+
v = 0.0
|
45
|
+
(0...hidden_n).each{|j| v += hidden_val[j] * wts[wi + j] }
|
46
|
+
output_val[i] = output_f.call(v - thr)
|
47
|
+
}
|
48
|
+
output_val
|
49
|
+
}
|
50
|
+
use UniformCrossover.dup
|
51
|
+
}
|
52
|
+
end
|