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.
- data/History.txt +11 -0
- data/Manifest.txt +5 -1
- data/README.txt +33 -43
- data/Rakefile +30 -5
- data/TODO.txt +11 -9
- data/data/CROSSOVER +6 -1
- data/data/GENOTYPE +5 -2
- data/data/MUTATION +5 -0
- data/data/SELECTION +2 -3
- data/examples/money.rb +35 -0
- data/examples/output/flattened_sombero.html +3849 -3849
- data/examples/output/flattened_sombero2_.html +2321 -2321
- data/examples/output/fopt1_dblopt.html +1276 -1276
- data/examples/output/hill10.html +3906 -3906
- data/examples/output/hill2.csv +24 -24
- data/examples/output/hill2.html +177 -177
- data/examples/output/royalroad1_report.html +531 -531
- data/examples/output/royalroad2_report.html +592 -592
- data/examples/output/royalroadquick_report.html +243 -243
- data/examples/output/tsp.html +947 -403
- data/examples/output/weasel1_report.html +616 -616
- data/examples/output/weasel2_report.html +115 -115
- data/examples/tree.rb +90 -0
- data/examples/tsp.rb +19 -12
- data/lib/charlie/etc/monkey.rb +28 -44
- data/lib/charlie/genotype.rb +30 -1
- data/lib/charlie/permutation/permutation.rb +52 -1
- data/lib/charlie/population.rb +58 -20
- data/lib/charlie/selection.rb +1 -1
- data/lib/charlie/tree/tree.rb +128 -0
- data/lib/charlie.rb +2 -5
- data/test/t_common.rb +18 -2
- data/test/test_benchmark.rb +12 -10
- data/test/test_evolve.rb +87 -0
- data/test/test_permutation.rb +37 -3
- data/test/test_tree.rb +57 -0
- metadata +69 -50
- data/test/test_basic.rb +0 -32
@@ -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>
|
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>
|
42
|
-
<tr><td>Total time</td><td>
|
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>
|
49
|
-
<td>
|
50
|
-
<td>
|
51
|
-
<td>
|
52
|
-
<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(
|
58
|
-
<td>
|
59
|
-
<td>
|
60
|
-
<td>
|
61
|
-
<td>
|
62
|
-
<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(
|
66
|
-
<td>
|
67
|
-
<td>
|
68
|
-
<td>
|
69
|
-
<td>
|
70
|
-
<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>
|
78
|
-
<td>
|
79
|
-
<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>
|
85
|
-
<td>
|
86
|
-
<td>
|
87
|
-
<td>1.
|
88
|
-
<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>
|
93
|
-
<td>
|
94
|
-
<td>
|
95
|
-
<td>1.
|
96
|
-
<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>
|
111
|
-
<td>
|
112
|
-
<td>
|
113
|
-
<td>
|
114
|
-
<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>
|
124
|
-
<td>
|
125
|
-
<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>
|
134
|
-
<td>
|
135
|
-
<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>
|
144
|
-
<td>
|
145
|
-
<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>
|
164
|
-
<td>
|
165
|
-
<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(
|
170
|
-
<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>
|
173
|
-
<td>
|
174
|
-
<td>
|
175
|
-
<td>
|
176
|
-
<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(
|
180
|
-
<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>
|
183
|
-
<td>
|
184
|
-
<td>
|
185
|
-
<td>
|
186
|
-
<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>
|
194
|
-
<td>
|
195
|
-
<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>
|
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>
|
203
|
-
<td>
|
204
|
-
<td>
|
205
|
-
<td>
|
206
|
-
<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>
|
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>
|
213
|
-
<td>
|
214
|
-
<td>
|
215
|
-
<td>1.
|
216
|
-
<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>
|
224
|
-
<td>
|
225
|
-
<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>
|
234
|
-
<td>
|
235
|
-
<td>
|
236
|
-
|
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=
|
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
|
-
|
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
|
-
#
|
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 =
|
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
|
+
|
data/lib/charlie/etc/monkey.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
|
data/lib/charlie/genotype.rb
CHANGED
@@ -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
|