charlie 0.5.0 → 0.6.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.
@@ -34,206 +34,206 @@
34
34
  <table>
35
35
  <tr><td>Genotype class</td><td>Weasel</td></tr>
36
36
  <tr><td>Population size</td><td>20</td></tr>
37
- <tr><td># of generations per run</td><td>100</td></tr>
37
+ <tr><td># of generations per run</td><td>50</td></tr>
38
38
  <tr><td>Number of tests </td><td>12</td></tr>
39
39
  <tr><td>Tests repeated </td><td>10 times</td></tr>
40
40
  <tr><td>Number of runs </td><td>120</td></tr>
41
- <tr><td>Total number of generations </td><td>12000</td></tr>
42
- <tr><td>Total time</td><td>153.89 seconds</td></tr>
41
+ <tr><td>Total number of generations </td><td>6000</td></tr>
42
+ <tr><td>Total time</td><td>23.51 seconds</td></tr>
43
43
  </table
44
44
  <h1>Stats for all</h1><table>
45
45
  <tr><th>.</th><th>min</th><th>max</th><th>avg</th><th>stddev</th><th>avg-time</th></tr>
46
46
  <tr>
47
47
  <td>All</td>
48
- <td>18.00000</td>
49
- <td>27.00000</td>
50
- <td>23.21667</td>
51
- <td>1.86719</td>
52
- <td>1.28238</td>
48
+ <td>13.00000</td>
49
+ <td>22.00000</td>
50
+ <td>17.80833</td>
51
+ <td>2.25202</td>
52
+ <td>0.19585</td>
53
53
  </tr>
54
54
  </table><h1>Stats for selection</h1><table>
55
55
  <tr><th>selection</th><th>min</th><th>max</th><th>avg</th><th>stddev</th><th>avg-time</th></tr>
56
56
  <tr>
57
- <td>TruncationSelection(1)</td>
58
- <td>18.00000</td>
59
- <td>27.00000</td>
60
- <td>23.28333</td>
61
- <td>1.81743</td>
62
- <td>1.29585</td>
57
+ <td>TruncationSelection(0.2)</td>
58
+ <td>13.00000</td>
59
+ <td>22.00000</td>
60
+ <td>17.86667</td>
61
+ <td>2.17153</td>
62
+ <td>0.19556</td>
63
63
  </tr>
64
64
  <tr>
65
- <td>TruncationSelection(0.2)</td>
66
- <td>19.00000</td>
67
- <td>27.00000</td>
68
- <td>23.15000</td>
69
- <td>1.91333</td>
70
- <td>1.26892</td>
65
+ <td>TruncationSelection(1)</td>
66
+ <td>13.00000</td>
67
+ <td>22.00000</td>
68
+ <td>17.75000</td>
69
+ <td>2.32827</td>
70
+ <td>0.19614</td>
71
71
  </tr>
72
72
  </table><h1>Stats for crossover</h1><table>
73
73
  <tr><th>crossover</th><th>min</th><th>max</th><th>avg</th><th>stddev</th><th>avg-time</th></tr>
74
74
  <tr>
75
75
  <td>UniformCrossover</td>
76
+ <td>14.00000</td>
76
77
  <td>22.00000</td>
77
- <td>27.00000</td>
78
- <td>24.32500</td>
79
- <td>1.27255</td>
80
- <td>1.38479</td>
78
+ <td>19.00000</td>
79
+ <td>2.00000</td>
80
+ <td>0.21745</td>
81
81
  </tr>
82
82
  <tr>
83
83
  <td>SinglePointCrossover</td>
84
- <td>21.00000</td>
85
- <td>27.00000</td>
86
- <td>23.52500</td>
87
- <td>1.43156</td>
88
- <td>1.25068</td>
84
+ <td>14.00000</td>
85
+ <td>22.00000</td>
86
+ <td>18.20000</td>
87
+ <td>1.90000</td>
88
+ <td>0.18830</td>
89
89
  </tr>
90
90
  <tr>
91
91
  <td>NullCrossover</td>
92
- <td>18.00000</td>
93
- <td>26.00000</td>
94
- <td>21.80000</td>
95
- <td>1.86011</td>
96
- <td>1.21168</td>
92
+ <td>13.00000</td>
93
+ <td>20.00000</td>
94
+ <td>16.22500</td>
95
+ <td>1.87733</td>
96
+ <td>0.18179</td>
97
97
  </tr>
98
98
  </table><h1>Stats for mutation</h1><table>
99
99
  <tr><th>mutation</th><th>min</th><th>max</th><th>avg</th><th>stddev</th><th>avg-time</th></tr>
100
- <tr>
101
- <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
102
- <td>18.00000</td>
103
- <td>27.00000</td>
104
- <td>23.50000</td>
105
- <td>1.82117</td>
106
- <td>1.26157</td>
107
- </tr>
108
100
  <tr>
109
101
  <td>ListMutator([:n_point, 2],[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
110
- <td>19.00000</td>
111
- <td>27.00000</td>
112
- <td>22.93333</td>
113
- <td>1.86964</td>
114
- <td>1.30320</td>
102
+ <td>13.00000</td>
103
+ <td>22.00000</td>
104
+ <td>18.23333</td>
105
+ <td>2.01136</td>
106
+ <td>0.20229</td>
115
107
  </tr>
116
- </table><h1>Raw Stats</h1><table>
117
- <tr><th>selection</th><th>crossover</th><th>mutation</th><th>min</th><th>max</th><th>avg</th><th>stddev</th><th>avg-time</th></tr>
118
108
  <tr>
119
- <td>TruncationSelection(0.2)</td>
120
- <td>UniformCrossover</td>
121
109
  <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
110
+ <td>13.00000</td>
122
111
  <td>22.00000</td>
123
- <td>27.00000</td>
124
- <td>24.80000</td>
125
- <td>1.46969</td>
126
- <td>1.34398</td>
112
+ <td>17.38333</td>
113
+ <td>2.39508</td>
114
+ <td>0.18940</td>
127
115
  </tr>
116
+ </table><h1>Raw Stats</h1><table>
117
+ <tr><th>selection</th><th>crossover</th><th>mutation</th><th>min</th><th>max</th><th>avg</th><th>stddev</th><th>avg-time</th></tr>
128
118
  <tr>
129
119
  <td>TruncationSelection(1)</td>
130
120
  <td>UniformCrossover</td>
131
121
  <td>ListMutator([:n_point, 2],[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
122
+ <td>18.00000</td>
132
123
  <td>22.00000</td>
133
- <td>26.00000</td>
134
- <td>24.30000</td>
135
- <td>1.34536</td>
136
- <td>1.41264</td>
124
+ <td>19.50000</td>
125
+ <td>1.02470</td>
126
+ <td>0.22375</td>
137
127
  </tr>
138
128
  <tr>
139
129
  <td>TruncationSelection(0.2)</td>
140
130
  <td>UniformCrossover</td>
141
131
  <td>ListMutator([:n_point, 2],[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
132
+ <td>17.00000</td>
142
133
  <td>22.00000</td>
143
- <td>26.00000</td>
144
- <td>24.10000</td>
145
- <td>1.13578</td>
146
- <td>1.38662</td>
147
- </tr>
148
- <tr>
149
- <td>TruncationSelection(1)</td>
150
- <td>UniformCrossover</td>
151
- <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
152
- <td>23.00000</td>
153
- <td>26.00000</td>
154
- <td>24.10000</td>
155
- <td>0.94340</td>
156
- <td>1.39592</td>
134
+ <td>19.50000</td>
135
+ <td>1.50000</td>
136
+ <td>0.22246</td>
157
137
  </tr>
158
138
  <tr>
159
139
  <td>TruncationSelection(1)</td>
160
140
  <td>SinglePointCrossover</td>
161
141
  <td>ListMutator([:n_point, 2],[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
142
+ <td>16.00000</td>
162
143
  <td>22.00000</td>
163
- <td>27.00000</td>
164
- <td>23.90000</td>
165
- <td>1.44568</td>
166
- <td>1.27018</td>
144
+ <td>18.80000</td>
145
+ <td>2.03961</td>
146
+ <td>0.19627</td>
167
147
  </tr>
168
148
  <tr>
169
- <td>TruncationSelection(1)</td>
170
- <td>SinglePointCrossover</td>
149
+ <td>TruncationSelection(0.2)</td>
150
+ <td>UniformCrossover</td>
171
151
  <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
172
- <td>21.00000</td>
173
- <td>26.00000</td>
174
- <td>23.80000</td>
175
- <td>1.46969</td>
176
- <td>1.21699</td>
152
+ <td>14.00000</td>
153
+ <td>22.00000</td>
154
+ <td>18.50000</td>
155
+ <td>2.37697</td>
156
+ <td>0.21168</td>
177
157
  </tr>
178
158
  <tr>
179
- <td>TruncationSelection(0.2)</td>
180
- <td>SinglePointCrossover</td>
159
+ <td>TruncationSelection(1)</td>
160
+ <td>UniformCrossover</td>
181
161
  <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
182
- <td>21.00000</td>
183
- <td>26.00000</td>
184
- <td>23.50000</td>
185
- <td>1.36015</td>
186
- <td>1.23589</td>
162
+ <td>16.00000</td>
163
+ <td>22.00000</td>
164
+ <td>18.50000</td>
165
+ <td>2.45967</td>
166
+ <td>0.21192</td>
187
167
  </tr>
188
168
  <tr>
189
169
  <td>TruncationSelection(0.2)</td>
190
170
  <td>SinglePointCrossover</td>
191
171
  <td>ListMutator([:n_point, 2],[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
172
+ <td>16.00000</td>
192
173
  <td>21.00000</td>
193
- <td>24.00000</td>
194
- <td>22.90000</td>
195
- <td>1.22066</td>
196
- <td>1.27966</td>
174
+ <td>18.20000</td>
175
+ <td>1.53623</td>
176
+ <td>0.19442</td>
197
177
  </tr>
198
178
  <tr>
199
179
  <td>TruncationSelection(0.2)</td>
200
- <td>NullCrossover</td>
180
+ <td>SinglePointCrossover</td>
201
181
  <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
202
- <td>19.00000</td>
203
- <td>26.00000</td>
204
- <td>22.90000</td>
205
- <td>1.97231</td>
206
- <td>1.14157</td>
182
+ <td>14.00000</td>
183
+ <td>21.00000</td>
184
+ <td>18.10000</td>
185
+ <td>2.21133</td>
186
+ <td>0.18162</td>
207
187
  </tr>
208
188
  <tr>
209
189
  <td>TruncationSelection(1)</td>
210
- <td>NullCrossover</td>
190
+ <td>SinglePointCrossover</td>
211
191
  <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
212
- <td>18.00000</td>
213
- <td>25.00000</td>
214
- <td>21.90000</td>
215
- <td>1.97231</td>
216
- <td>1.23506</td>
192
+ <td>15.00000</td>
193
+ <td>20.00000</td>
194
+ <td>17.70000</td>
195
+ <td>1.55242</td>
196
+ <td>0.18088</td>
217
197
  </tr>
218
198
  <tr>
219
199
  <td>TruncationSelection(1)</td>
220
200
  <td>NullCrossover</td>
221
201
  <td>ListMutator([:n_point, 2],[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
202
+ <td>14.00000</td>
222
203
  <td>20.00000</td>
223
- <td>25.00000</td>
224
- <td>21.70000</td>
225
- <td>1.48661</td>
226
- <td>1.24429</td>
204
+ <td>16.80000</td>
205
+ <td>1.83303</td>
206
+ <td>0.18853</td>
227
207
  </tr>
228
208
  <tr>
229
209
  <td>TruncationSelection(0.2)</td>
230
210
  <td>NullCrossover</td>
231
211
  <td>ListMutator([:n_point, 2],[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
212
+ <td>13.00000</td>
213
+ <td>20.00000</td>
214
+ <td>16.60000</td>
215
+ <td>1.68523</td>
216
+ <td>0.18833</td>
217
+ </tr>
218
+ <tr>
219
+ <td>TruncationSelection(0.2)</td>
220
+ <td>NullCrossover</td>
221
+ <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
222
+ <td>14.00000</td>
232
223
  <td>19.00000</td>
233
- <td>23.00000</td>
234
- <td>20.70000</td>
235
- <td>1.18743</td>
236
- <td>1.22578</td>
224
+ <td>16.30000</td>
225
+ <td>1.73494</td>
226
+ <td>0.17482</td>
227
+ </tr>
228
+ <tr>
229
+ <td>TruncationSelection(1)</td>
230
+ <td>NullCrossover</td>
231
+ <td>ListMutator(:single_point,[:replace, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", " "])</td>
232
+ <td>13.00000</td>
233
+ <td>18.00000</td>
234
+ <td>15.20000</td>
235
+ <td>1.83303</td>
236
+ <td>0.17549</td>
237
237
  </tr>
238
238
  </table>
239
239
  </body>
data/examples/tree.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'pp'
2
+ require '../lib/charlie'
3
+
4
+ # several examples of genetic programming
5
+
6
+ if ARGV[0]=='cos'
7
+
8
+ # approximate cos(x) by a polynomial.on [0,3]. usually results in some kind of linear approximation.like 1.3-0.8x
9
+ class Cos < TreeGenotype([proc{3*rand-1.5},:x], [:+,:*,:-], [:-@])
10
+ def fitness
11
+ -(0..10).map{|x| (eval_genes(:x=>0.3*x) - Math.cos(0.3 * x) ).abs }.sum - 0.1*size # last term used to counter bloat
12
+ # also possible, use infinity norm instead of L1 norm
13
+ #-(0..10).map{|x| (eval_genes(:x=>0.3*x) - Math.cos(0.3 * x) ).abs }.max
14
+
15
+ # smaller range [0,1.5] and inf norm give higher order approximations
16
+ #-(0..10).map{|x| (eval_genes(:x=>0.1*x) - Math.cos(0.1 * x) ).abs }.max
17
+ end
18
+ end
19
+
20
+ pop = Population.new(Cos).evolve_on_console(500)
21
+ pp pop.max.genes
22
+
23
+ elsif ARGV[0]=='pors'
24
+
25
+ # plus one recall store, generate the number ARGV[1] || 32 using +, 1 and recall/store operations (as few ops as possible).
26
+
27
+ class Fixnum
28
+ def sto
29
+ $pors_store = self
30
+ end
31
+ end
32
+
33
+ class PORS < TreeGenotype([1,:rec], [:+], [:sto])
34
+ N = (ARGV[1] || 32).to_i
35
+ def fitness
36
+ $pors_store = 0 # reset store
37
+ -(eval_genes(:rec=>proc{$pors_store}) - N).abs*10 - 0.1 * size # find smallest tree with result 32
38
+ end
39
+ end
40
+
41
+ pop = Population.new(PORS).evolve_on_console(500)
42
+ pp pop.max.genes
43
+
44
+ elsif ARGV[0]=='porsx'
45
+
46
+ # variant on pors, multiply T by some N, with only one access to T allowed
47
+
48
+ class Fixnum
49
+ def sto
50
+ $pors_store = self
51
+ end
52
+ end
53
+
54
+ class PORS < TreeGenotype([:rec,:T], [:+], [:sto])
55
+ N = (ARGV[1] || 31).to_i # quite complicated optimal tree for 31
56
+ def fitness
57
+ -(0..5).map{|t|
58
+ $pors_store = 0 # reset store
59
+ t_val = t
60
+ values = {:rec=>proc{$pors_store},:T=>proc{ cv=t_val; t_val=0; cv} } # using T destroys T
61
+ (eval_genes(values) - N*t).abs
62
+ }.sum - 0.1*size # find a tree that multiplies T by 3
63
+ end
64
+ end
65
+ =begin
66
+ solutions found for N=31:
67
+ sto(T) + sto( sto(rec+rec+rec) + rec ) + sto(rec+rec) + rec , size 19
68
+ sto(T) + sto( sto(rec+rec) + sto(rec+rec) + rec) + rec + rec , size 19
69
+ =end
70
+
71
+ pop = Population.new(PORS).evolve_on_console(500)
72
+ pp pop.max.genes
73
+
74
+
75
+ elsif ARGV[0]=='bloat'
76
+ # just generates huge trees.
77
+ class Bloat < TreeGenotype([1], [:+], [:+@])
78
+ def fitness
79
+ size
80
+ end
81
+ def to_s
82
+ "[tree of size #{size}]"
83
+ end
84
+ end
85
+
86
+ pop = Population.new(Bloat).evolve_on_console(200)
87
+
88
+ else
89
+ puts "choose a test : cos, pors, porsx, bloat"
90
+ end
data/examples/tsp.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  require '../lib/charlie'
2
2
  # Travelling salesperson problem
3
3
 
4
- N=10
4
+ N=5
5
+
5
6
  # N cities in a circle
6
- CITIES = (0...N).map{|i| th = i * 2 * Math::PI / N; [Math.cos(th),Math.sin(th)] }
7
- #CITIES = (0...N).map{|i| [0,i] } # on a line
7
+ #CITIES = (0...N).map{|i| th = i * 2 * Math::PI / N; [Math.cos(th),Math.sin(th)] }
8
+
9
+ # .. or on a line
10
+ #CITIES = (0...N).map{|i| [0,i] }
8
11
 
9
- #CITIES = (0...N).map{|i| (0...N).map{|j| [i,j] } }.inject{|a,b|a+b} # on a grid
12
+ # .. or NxN cities on a grid
13
+ CITIES = (0...N).map{|i| (0...N).map{|j| [i,j] } }.inject{|a,b|a+b}
10
14
 
11
15
  class TSP < PermutationGenotype(CITIES.size)
12
16
  def fitness
@@ -17,19 +21,22 @@ class TSP < PermutationGenotype(CITIES.size)
17
21
  }
18
22
  -d # higher (less negative) fitness is better. This breaks RouletteSelection (use 1.0/d instead), but most other methods can handle this
19
23
  end
24
+ cache_fitness # benchmark ~ 20% faster, tournament selection 25+% faster
20
25
  end
21
26
 
27
+
22
28
  pop = Population.new(TSP,20).evolve_on_console(50)
23
29
  #p pop[-1].genes.map{|a| CITIES[a]}
24
- # 49 -6.18033988749895 [2, 3, 4, 5, 6, 7, 8, 9, 0, 1]
25
- exit
30
+ # 49 -6.18033988749895 [2, 3, 4, 5, 6, 7, 8, 9, 0, 1] for the circle
26
31
 
27
- Population.benchmark(TSP,'output/tsp.html') {
28
- selection ScaledRouletteSelection(), TruncationSelection(1), TruncationSelection(0.3)
29
- crossover PermutationCrossover, PCross(0.5,PermutationCrossover), PCross(0.75,PermutationCrossover), NullCrossover
30
- mutator PermutationMutator, PMutate(0.5,PermutationMutator), PMutate(0.75,PermutationMutator), NullMutator
31
32
 
33
+ Population.benchmark(TSP,'output/tsp.html') {
34
+ selection TruncationSelection(1), TruncationSelection(0.3), Elitism(ScaledRouletteSelection,1), TournamentSelection(4), TournamentSelection(3)
35
+ crossover EdgeRecombinationCrossover, PermutationCrossover, PCross(0.5,PermutationCrossover), NullCrossover, PCross(0.5,EdgeRecombinationCrossover,PermutationCrossover)
36
+ mutator PermutationMutator, PMutate(0.5,PermutationMutator),
37
+ InversionMutator, PMutate(0.5,InversionMutator,PermutationMutator)
32
38
  self.generations = 30
33
- self.repeat = 50
39
+ self.repeat = 25
34
40
  self.population_size = 20
35
- }
41
+ } if ARGV[0]=='bm' # takes about 7 minutes on ruby 1.9. lower the repeat number for a quicker run
42
+
@@ -1,4 +1,5 @@
1
- # I should probably replace some of this with a dependency on facets.
1
+ # I should probably replace some of this with a dependency on facets(?) or some other library.
2
+ # update: Ruby 1.9 has many of these anyway
2
3
 
3
4
  class Numeric
4
5
  def between(minval,maxval)
@@ -11,26 +12,28 @@ module Enumerable
11
12
  ret = {}
12
13
  each{|e| (ret[yield(e)] ||= []) << e }
13
14
  ret
14
- end
15
-
16
- def zip_with(a2,&b)
17
- zip(a2).map(&b)
18
- end
15
+ end if RUBY_VERSION < '1.9'
19
16
 
20
17
  def count
21
18
  count = 0
22
19
  each {|e| count+=1 if yield(e) }
23
20
  count
21
+ end if RUBY_VERSION < '1.9'
22
+
23
+ def zip_with(a2,&b)
24
+ zip(a2).map(&b)
24
25
  end
26
+
27
+ alias_method :enum_slice, :each_slice unless RUBY_VERSION < '1.9' # ruby1.9 replaces enum_* with each_*
25
28
  end
26
29
 
27
30
  class Array
28
31
 
29
32
  def shuffle
30
33
  sort_by{ rand }
31
- end
34
+ end if RUBY_VERSION < '1.9'
32
35
 
33
- def sum
36
+ def sum # TODO 1.9, use :+
34
37
  inject(0){|a,b|a+b}
35
38
  end
36
39
 
@@ -46,7 +49,7 @@ class Array
46
49
  self[rand(size)]
47
50
  end
48
51
 
49
- def stats
52
+ def stats # TODO 1.9, use minmax
50
53
  [min,max,average,stddev]
51
54
  end
52
55
 
@@ -54,16 +57,16 @@ class Array
54
57
  sum.to_f / size
55
58
  end
56
59
 
57
- def map_with_index
58
- r=[]
59
- each_with_index{|e,i| r << yield(e,i) }
60
- r
61
- end
62
-
63
60
  def stddev
64
61
  mu = average
65
62
  Math.sqrt( map{|x| (x-mu)*(x-mu) }.sum / size )
66
63
  end
64
+
65
+ def map_with_index # TODO 1.9
66
+ r=[]
67
+ each_with_index{|e,i| r << yield(e,i) }
68
+ r
69
+ end
67
70
  end
68
71
 
69
72
 
@@ -76,28 +79,29 @@ class String
76
79
  self[rand(size)]
77
80
  end
78
81
 
79
- def chars
80
- split('')
82
+ def chars # TODO 1.9
83
+ split('')
81
84
  end
82
85
 
83
86
  def each_char(&b)
84
87
  chars.each(&b)
85
- end
88
+ end if RUBY_VERSION < '1.9'
86
89
  end
87
90
 
88
91
  class Symbol
89
- def to_proc
90
- Proc.new { |*args| args.shift.__send__(self, *args) }
91
- end
92
-
92
+ undef_method :[] unless RUBY_VERSION < '1.9' # kill the 1.9 warning
93
93
  # This function was added because :npoint[3] just looks so much nicer than [:npoint,3]
94
94
  def [](*args)
95
95
  [self,*args]
96
96
  end
97
97
 
98
+ def to_proc
99
+ Proc.new { |*args| args.shift.__send__(self, *args) }
100
+ end if RUBY_VERSION < '1.9'
101
+
98
102
  def intern
99
103
  self
100
- end
104
+ end if RUBY_VERSION < '1.9'
101
105
  end
102
106
 
103
107
  class Module
@@ -107,30 +111,10 @@ class Module
107
111
 
108
112
  # Used to give anonymous modules a name.
109
113
  def name=(n)
110
- metaclass.send(:define_method,:to_s) { n }
114
+ metaclass.class_eval{ define_method(:to_s){ n } } # avoid send for ruby 1.9
111
115
  end
112
116
  end
113
117
 
114
- class Class
115
- # For each module passed:
116
- # Includes the module if it has a mutate! method, and includes it in the metaclass otherwise.
117
- # Used to include selection/crossover/mutation modules in one line, or just to avoid all the class<<self;include ...;end
118
- # class Example < Genotype
119
- # use RandomSelection, NullCrossover, NullMutator
120
- # end
121
- def use(*mods)
122
- mods.each{|mod|
123
- if mod.instance_methods.include? 'mutate!'
124
- include mod
125
- else
126
- metaclass.class_eval{
127
- include mod
128
- }
129
- end
130
- }
131
- end
132
-
133
- end
134
118
 
135
119
 
136
120
 
@@ -15,12 +15,41 @@ class Genotype
15
15
  end
16
16
 
17
17
  class << self
18
+
19
+ # For each module passed:
20
+ # Includes the module if it has a mutate! method, and includes it in the metaclass otherwise.
21
+ # Used to include selection/crossover/mutation modules in one line, or just to avoid all the class<<self;include ...;end
22
+ # class Example < Genotype
23
+ # use RandomSelection, NullCrossover, NullMutator
24
+ # end
25
+ def use(*mods)
26
+ mods.each{|mod|
27
+ if mod.instance_methods(true).find { |m| m.to_s == 'mutate!'} # ruby1.8 returns strings, 1.9 symbols so include? doesn't work
28
+ include mod
29
+ else
30
+ metaclass.class_eval{
31
+ include mod
32
+ }
33
+ end
34
+ }
35
+ end
36
+
18
37
  # Creates a new instance of your genotype class given its genes.
19
38
  def from_genes(g)
20
- r = new # how to avoid initialize here?
39
+ r = allocate # r = new # how to avoid initialize here? -- fixed
21
40
  r.genes = g
22
41
  r
23
42
  end
43
+
44
+ # Adds caching to the fitness function. Only call AFTER defining your fitness function.
45
+ # Never clears the cache. This should work most of the time because crossovers/from_genes create new instances.
46
+ def cache_fitness
47
+ alias_method :real_fitness, :fitness
48
+ define_method(:fitness) {
49
+ @fitness_cache ||= send(:real_fitness)
50
+ }
51
+ self
52
+ end
24
53
  end
25
54
 
26
55
  def dup