ga 0.1.1 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 161e6fb4eb2a3a87700d4f28cc470bb86b41c125
4
- data.tar.gz: 84fe707139234f68d4d8b6b1f5361f57e1a78d85
3
+ metadata.gz: 2831b4e765ee161b47cbfd96efa977e635443047
4
+ data.tar.gz: '09022f90fcf17f9892e576534648b48f49703ab1'
5
5
  SHA512:
6
- metadata.gz: 188f11a56b7cfbe1c4d0fa3a5ae523e5001864d506835d2d72c076855e656f6e75e393c238b9dcacb12d741e5ca89e8f645911078573e5855ed0df32a31733d0
7
- data.tar.gz: 64515b0363b31205feba5f7e55ba458ccb810784241a0952b9221bef939c136bff8a048b8469cc9ae564bd20ff09e5022c47082bc3408ef5009d61b3f2b9f454
6
+ metadata.gz: b5a0c7db9d6a018b65816060a1da1d9544da8fe3dbc44ef06174797abec42e50e407ecfa1ba115b0763b0cb95ac6812df91ac34d2ecd7ae5a2562db539e4bc34
7
+ data.tar.gz: 335144415a50aef9a3afa65dfc5e71dbc3e67d0a4bc69592940c43357be95470f60c03706eb985f6dec145c07dc49b546761ca0cc6fd7a2a787170ca26326a46
data/.gitignore CHANGED
@@ -6,7 +6,11 @@
6
6
  /doc/
7
7
  /pkg/
8
8
  /spec/reports/
9
+ /spec/examples.txt
9
10
  /tmp/
10
11
 
11
12
  *.swp
12
13
  *.gem
14
+
15
+ /examples/*.o
16
+ /examples/*.so
data/Gemfile CHANGED
@@ -4,4 +4,5 @@ source 'https://gems.ruby-china.org'
4
4
  gemspec
5
5
 
6
6
  gem 'parallel'
7
+ gem 'ffi'
7
8
 
data/examples/01bag.rb CHANGED
@@ -82,7 +82,9 @@ Benchmark.bm do |x|
82
82
  x.report('a') do
83
83
  puts 'start'
84
84
  100.times do
85
- units = Unit.evolve(32, 100, 0.8, 0.15)
85
+ ga = Unit.new_ga_zoo
86
+ ga.elite_policy!
87
+ units = ga.evolve(20, 100, 0.8, 0.15)
86
88
  unit = units.max
87
89
  print unit.inspect + "\r"
88
90
  r[unit.fitness] ||= 0
data/examples/Makefile ADDED
@@ -0,0 +1,17 @@
1
+
2
+ test: clean test.o robot_tester.o
3
+ g++ -o test test.o robot_tester.o
4
+ ./test
5
+
6
+ sharedlib: clean robot_tester.o
7
+ g++ -shared -Wall -o librt.so robot_tester.o
8
+
9
+ clean:
10
+ rm -f *.o *.so test
11
+
12
+ robot_tester.o: robot_tester.cpp
13
+ g++ -Wall -c robot_tester.cpp
14
+
15
+ test.o: test.cpp
16
+ g++ -c test.cpp
17
+
data/examples/robot.rb CHANGED
@@ -1,81 +1,35 @@
1
- require 'bundler/setup'
2
1
  require 'ga'
3
-
2
+ require 'pry'
4
3
  require 'parallel'
4
+ require 'ffi'
5
5
 
6
- MAP_SIZE = 7
7
-
8
-
9
- # 0 empty 1 dust 2 wall
10
- # [x][y]
11
- def new_map
12
- MAP_SIZE.times.map { [0] * MAP_SIZE }
13
- end
14
-
15
- def rand_map(map, dust_rate = 0.5)
16
- map.each do |cols|
17
- cols.length.times {|i| cols[i] = rand() <= dust_rate ? 1 : 0 }
18
- end
19
- end
20
-
21
- def show_map(map, bx = nil, by = nil)
22
- puts('-' * 20)
23
- MAP_SIZE.times do |y|
24
- MAP_SIZE.times do |x|
25
- if bx == x and by == y then
26
- print(map[x][y], '* ')
27
- else
28
- print(map[x][y], ' ')
29
- end
30
- end
31
- print("\n")
32
- end
33
- puts('-' * 20)
34
- end
35
-
36
-
37
- # 0 random move
38
- # 9 clear
39
- # 1 2 3
40
- # 4 5
41
- # 6 7 8
42
- ACTIONS_DATA = {
43
- 0 => nil,
44
- 9 => nil,
45
-
46
- # 1 => [-1, -1],
47
- 2 => [0, -1],
48
- # 3 => [1, -1],
49
-
50
- 4 => [-1, 0],
51
- # 5 => [1, 0],
6
+ system 'make sharedlib'
52
7
 
53
- 6 => [-1, 1],
54
- # 7 => [0, 1],
55
- 8 => [1, 1]
56
- }
8
+ # env:
9
+ # '12001'
10
+ # 1
11
+ # 2 3 4
12
+ # 5
57
13
 
58
- ACTIONS = ACTIONS_DATA.keys
59
- MOVE_ACTIONS = [2, 4, 6, 8]
14
+ # actions:
15
+ # 1
16
+ # 2 3
17
+ # 4
18
+ # 1 2 3 4 move
19
+ # 0 rand move
20
+ # 5 clean
21
+ ACTIONS = [0, 1, 2, 3, 4, 5]
60
22
 
61
23
  class Robot
62
24
  include GA
63
25
 
64
- # genome
65
- #
66
26
  # {env => action}
67
- #
68
- # env: '10122'
69
- # 1
70
- # 2 3 4
71
- # 5
72
- # action:
73
-
74
27
  attr_accessor :genome, :fitness
75
- TOTAL_VALUE_TEST = 150
28
+ TOTAL_TEST_TIMES = 100
76
29
 
77
30
  def self.random_new
78
- self.new({})
31
+ genome = {}
32
+ self.new(genome)
79
33
  end
80
34
 
81
35
  def initialize(genome)
@@ -83,37 +37,22 @@ class Robot
83
37
  end
84
38
 
85
39
  def fitness
86
- tester = RobotTester.new(self)
87
- @fitness ||= TOTAL_VALUE_TEST.times.map { tester.test }.reduce(&:+) / TOTAL_VALUE_TEST
88
- end
89
-
90
- def <=>(target)
91
- fitness <=> target.fitness
40
+ @fitness ||= RobotTester.test(TOTAL_TEST_TIMES, 200, self)
92
41
  end
93
42
 
94
43
  def analyse_env(env)
95
- @genome[env] ||= if env[2] == '1' then
96
- 9
97
- elsif env == '00000' then
98
- 0
99
- else
100
- MOVE_ACTIONS.sample
101
- end
44
+ @genome[env] ||= ACTIONS.sample
102
45
  end
103
46
 
104
47
  def cross!(target)
105
48
  all_genome = (genome.keys + target.genome.keys).uniq
106
49
  len = all_genome.length
107
- min_robot = [self, target].min
108
50
 
109
- rand(len).times do
51
+ (len / 4 + rand(len / 4)).times do
110
52
  gene = all_genome[rand(len)]
111
-
112
- if genome[gene] == target.genome[gene] and rand() < 0.3 then
113
- min_robot.genome[gene] = ACTIONS.sample
114
- else
115
- genome[gene], target.genome[gene] = target.genome[gene], genome[gene]
116
- end
53
+ genome[gene] ||= ACTIONS.sample
54
+ target.genome[gene] ||= ACTIONS.sample
55
+ genome[gene], target.genome[gene] = target.genome[gene], genome[gene]
117
56
  end
118
57
  end
119
58
 
@@ -121,94 +60,43 @@ class Robot
121
60
  all_genome = genome.keys
122
61
  len = all_genome.length
123
62
 
124
- (rand(len) + 1).times do
63
+ (len / 4 + rand(len / 2)).times do
125
64
  gene = all_genome[rand(len)]
126
- genome[gene] = ACTIONS.sample
65
+ genome[gene] = (ACTIONS - [genome[gene]]).sample
127
66
  end
128
67
  end
129
68
  end
130
69
 
131
- class RobotTester
132
- attr_reader :map, :robot
133
-
134
- def initialize(robot)
135
- @map = new_map
136
- @robot = robot
137
- end
70
+ module RobotTester
71
+ extend FFI::Library
138
72
 
139
- def test(step = 70, show = false)
140
- rand_map(map)
141
- x = rand(map.length)
142
- y = rand(map.length)
143
- @total_value = 0
144
-
145
- step.times do
146
- env = scan_env(x, y)
147
- action = robot.analyse_env(env)
148
- rx, ry = execute_action(x, y, action)
149
-
150
- x += rx
151
- y += ry
152
-
153
- if x < 0 or y < 0 or x >= MAP_SIZE or y >= MAP_SIZE then
154
- x -= rx
155
- y -= ry
156
- @total_value -= 10
157
- elsif map[x][y] == 1
158
- @total_value += 1
159
- end
160
-
161
- if show then
162
- show_map(map, x, y)
163
- sleep 0.2
164
- end
165
- end
73
+ ffi_lib File.expand_path('../librt.so', __FILE__)
74
+ callback :analyse_cb, [:string], :int
75
+ attach_function :robot_test, [:int, :int, :pointer, :pointer, :int, :analyse_cb], :int
166
76
 
167
- @total_value
168
- end
77
+ def self.test(times, step, robot)
78
+ genome = robot.genome
79
+ len = genome.length
80
+ env_pointer = FFI::MemoryPointer.new(:pointer, len)
81
+ action_pointer = FFI::MemoryPointer.new(:int, len)
169
82
 
170
- def execute_action(x, y, action)
171
- case action
172
- when 9 then
173
- if map[x][y] == 1 then
174
- map[x][y] = 0
175
- @total_value += 10
176
- else
177
- @total_value -= 10
178
- end
179
-
180
- [0, 0]
181
- when 0 then
182
- @total_value -= 1
183
- ACTIONS_DATA[MOVE_ACTIONS.sample]
184
- else
185
- @total_value -= 1
186
- ACTIONS_DATA[action]
187
- end
188
- end
83
+ eps = genome.keys.map {|k| FFI::MemoryPointer.from_string(k) }
84
+ env_pointer.write_array_of_pointer eps
85
+ action_pointer.write_array_of_int genome.values
189
86
 
190
- SCAN_COORDS = [
191
- [0, -1], [-1, 0], [0, 0], [1, 0], [0, 1]
192
- ]
193
- def scan_env(x, y)
194
- SCAN_COORDS.map do |sx, sy|
195
- rx = x + sx
196
- ry = y + sy
197
-
198
- if rx < 0 || ry < 0 || rx >= MAP_SIZE || ry >= MAP_SIZE then
199
- 2
200
- else
201
- map[rx][ry]
202
- end
203
- end.join
87
+ robot_test(
88
+ times, step,
89
+ env_pointer, action_pointer, len,
90
+ robot.method(:analyse_env)
91
+ )
204
92
  end
205
93
  end
206
94
 
207
-
208
95
  ga_zoo = Robot.new_ga_zoo
209
96
  ga_zoo.debug!
97
+ # ga_zoo.cataclysm(10, 1)
210
98
 
211
- ga_zoo.before_init_fitness do |units|
99
+ ga_zoo.before_init_fitness do |units, generation|
212
100
  vs = Parallel.map(units, in_processes: 8) do |unit|
213
101
  [unit.fitness, unit.genome]
214
102
  end
@@ -217,13 +105,14 @@ ga_zoo.before_init_fitness do |units|
217
105
  end
218
106
  end
219
107
 
220
- robots = ga_zoo.evolve(256, 300)
108
+ srand(Time.now.to_i)
109
+ robots = ga_zoo.evolve(200, 1000, 0.9, 0.2)
221
110
  robot = robots.max
222
111
 
223
- RobotTester.new(robot).test(100, true)
224
-
225
112
  puts "========= result ============="
226
113
  puts "fitness: #{robot.fitness}"
227
114
  puts robot.genome
228
-
115
+ puts "score: %i" % RobotTester.test(1, 200, robot)
116
+ binding.pry
117
+ puts 'end'
229
118
 
@@ -0,0 +1,150 @@
1
+ #include "robot_tester.h"
2
+
3
+ #include <cstdio>
4
+ #include <cstdlib>
5
+ #include <ctime>
6
+ #include <unistd.h>
7
+
8
+ RobotTester::RobotTester(int map_size, env_map* p_em) {
9
+ size = map_size;
10
+ p_env_action = p_em;
11
+ map = new int*[size];
12
+ x = 0; y = 0;
13
+ for (int i = 0; i < size; i++) {
14
+ map[i] = new int[size];
15
+ }
16
+ }
17
+
18
+ RobotTester::~RobotTester() {
19
+ for (int i = 0; i < size; i++) {
20
+ delete map[i];
21
+ }
22
+ }
23
+
24
+ int RobotTester::test(int step, analyse_callback cb, bool play = false) {
25
+ int score = 0;
26
+ char env[6] = {0, 0, 0, 0, 0, 0};
27
+ int action;
28
+ env_map::iterator em_itor;
29
+ x = 0; y = 0;
30
+
31
+ rand_map();
32
+
33
+ for (int i = 0; i < step; i++) {
34
+ scan_env(env);
35
+ em_itor = p_env_action->find(env);
36
+ if (em_itor != p_env_action->end()) {
37
+ action = em_itor->second;
38
+ } else {
39
+ action = cb(env);
40
+ p_env_action->insert(env_map::value_type(env, action));
41
+ }
42
+
43
+ score = score + execute_action(action == 0 ? (rand() % 4 + 1) : action);
44
+ if (play) {
45
+ show_map();
46
+ usleep(100 * 1000);
47
+ }
48
+ }
49
+
50
+ return score;
51
+ }
52
+
53
+ void RobotTester::rand_map() {
54
+ for (int i = 0; i < size; i++) {
55
+ for (int j = 0; j < size; j++) {
56
+ map[i][j] = rand() % 2;
57
+ }
58
+ }
59
+ }
60
+
61
+ const int scan_len = 5;
62
+ const int scan_points[scan_len][2] = {
63
+ {0, -1},
64
+ {-1, 0}, {0, 0}, {1, 0},
65
+ {0, 1}
66
+ };
67
+ void RobotTester::scan_env(char* env) {
68
+ int sx, sy;
69
+ for (int i = 0; i < scan_len; i++) {
70
+ sx = x + scan_points[i][0];
71
+ sy = y + scan_points[i][1];
72
+
73
+ if (sx < 0 || sx >= size || sy < 0 || sy >= size) {
74
+ env[i] = '2';
75
+ continue;
76
+ }
77
+ env[i] = char(map[sx][sy] + 48);
78
+ }
79
+ }
80
+
81
+ void RobotTester::show_map() {
82
+ for (int i = 0; i < size; i++) {
83
+ for (int j = 0; j < size; j++) {
84
+ printf(" %i%c", map[j][i], (x == j and y == i) ? '*' : ' ');
85
+ }
86
+ printf("\n");
87
+ }
88
+ printf("\n");
89
+ }
90
+
91
+ // 1 2 3 4 move
92
+ // 5 take
93
+ // 1
94
+ // 2 5 3
95
+ // 4
96
+ const int actions[5][2] = {
97
+ {0, 0}, {0, -1}, {-1, 0}, {1, 0}, {0, 1}
98
+ };
99
+ int RobotTester::execute_action(int action) {
100
+ switch(action) {
101
+ case 5: {
102
+ if (map[x][y] == 1) {
103
+ map[x][y] = 0;
104
+ return 10;
105
+ } else {
106
+ return -1;
107
+ }
108
+ }
109
+ case 1:
110
+ case 2:
111
+ case 3:
112
+ case 4: {
113
+ int mx = x + actions[action][0];
114
+ int my = y + actions[action][1];
115
+ if (mx < 0 || mx >= size || my < 0 || my >= size) {
116
+ return -5;
117
+ } else {
118
+ x = mx; y = my;
119
+ return 0;
120
+ }
121
+ }
122
+ default:
123
+ printf("Invalid action %i\n", action);
124
+ throw("Invalid action\n");
125
+ }
126
+ }
127
+
128
+ typedef std::unordered_map<std::string, short> env_map;
129
+
130
+
131
+ extern "C" {
132
+ int robot_test(int times, int step, char** envs, short* actions, int elen, analyse_callback cb) {
133
+ srand(time(0));
134
+
135
+ env_map emap;
136
+ for (int i = 0; i < elen; i++) {
137
+ emap.insert(env_map::value_type(envs[i], actions[i]));
138
+ }
139
+
140
+ RobotTester rt = RobotTester(10, &emap);
141
+ int total_score = 0;
142
+
143
+ for (int i = 0; i < times; i++) {
144
+ total_score = total_score + rt.test(step, cb, times == 1);
145
+ }
146
+
147
+ return total_score / times;
148
+ }
149
+ }
150
+
@@ -0,0 +1,36 @@
1
+ #ifndef ROBOT_TESTER_H
2
+ #define ROBOT_TESTER_H
3
+
4
+ #include <unordered_map>
5
+ #include <string>
6
+
7
+ typedef int(*analyse_callback)(const char* const);
8
+ typedef std::unordered_map<std::string, short> env_map;
9
+
10
+ class RobotTester {
11
+ public:
12
+ RobotTester(int, env_map*);
13
+ ~RobotTester();
14
+ int test(int, analyse_callback, bool play);
15
+
16
+ void rand_map();
17
+ void show_map();
18
+
19
+ private:
20
+ env_map* p_env_action;
21
+ int size;
22
+ int **map;
23
+ int x;
24
+ int y;
25
+
26
+ void scan_env(char[5]);
27
+ int execute_action(int);
28
+ };
29
+
30
+
31
+ extern "C" {
32
+ int robot_test(int times, int step, char** envs, short* action, int elen, analyse_callback);
33
+ }
34
+
35
+ #endif
36
+
data/examples/test.cpp ADDED
@@ -0,0 +1,35 @@
1
+ #include "robot_tester.h"
2
+
3
+ #include <cstdio>
4
+ #include <cstdlib>
5
+
6
+ #include <unordered_map>
7
+ #include <string>
8
+
9
+ int analyse_env(const char* const env) {
10
+ return rand() % 6;
11
+ }
12
+
13
+ typedef std::unordered_map<std::string, short> env_hash;
14
+
15
+ int main() {
16
+ env_hash eh;
17
+ int step = 200;
18
+ int times = 200;
19
+
20
+ printf("'robot_test' avg score: %i\n", robot_test(times, step, NULL, NULL, 0, analyse_env));
21
+
22
+ RobotTester rt = RobotTester(10, &eh);
23
+ int total_score = 0;
24
+ printf("env_hash size: %i\n", (int)eh.size());
25
+
26
+ for (int i = 0; i < times; i++) {
27
+ total_score = total_score + rt.test(step, analyse_env, false);
28
+ }
29
+
30
+ printf("env_hash size: %i\n", (int)eh.size());
31
+ printf("'RobotTester#test' avg score: %i\n", total_score / times);
32
+
33
+ return 0;
34
+ }
35
+
data/lib/ga/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module GA
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
data/lib/ga/zoo.rb CHANGED
@@ -1,21 +1,27 @@
1
1
  module GA
2
2
  class Zoo
3
- attr_reader :unit_cls
3
+ attr_reader :unit_cls, :before_fitness_callback, :after_select_callback
4
4
 
5
5
  def initialize(unit_cls)
6
6
  @unit_cls = unit_cls
7
7
  @debug = false
8
8
  @before_fitness_callback = nil
9
+ @after_select_callback = nil
10
+ @elite_policy = false
11
+ @select_sample = 2
9
12
  end
10
13
 
11
14
  def evolve(units = 32, generations = 100, crossover_rate = 0.8, mutation_rate = 0.15)
12
15
  units = units.times.map { unit_cls.random_new } if units.is_a?(Fixnum)
16
+ @start_at = Time.now
13
17
 
14
18
  generations.times do |i|
15
19
  @before_fitness_callback.call(units, i + 1) if @before_fitness_callback
16
20
  output_debug_info(units, generations, i + 1) if @debug
17
21
 
18
22
  units = select_units(units)
23
+ @after_select_callback.call(units, i + 1) if @after_select_callback
24
+
19
25
  cross(units, crossover_rate)
20
26
  mutate(units, mutation_rate)
21
27
  end
@@ -27,25 +33,40 @@ module GA
27
33
  @debug = true
28
34
  end
29
35
 
36
+ def set_select_sample(val)
37
+ @select_sample = val
38
+ end
39
+
40
+ def elite_policy!
41
+ @elite_policy = true
42
+ end
43
+
30
44
  def before_init_fitness(&block)
31
45
  @before_fitness_callback = block
32
46
  end
33
47
 
48
+ def after_select(&block)
49
+ @after_select_callback = block
50
+ end
51
+
34
52
 
35
53
  private
36
54
 
37
55
  def select_units(units)
38
56
  new_units = units.map do
39
- ou = units.sample(3).max
57
+ ou = units.sample(@select_sample).max
40
58
  unit_cls.new(ou.genome).tap {|u| u.fitness = ou.fitness }
41
59
  end
42
60
 
43
- # Elite policy
44
- min_index = new_units.index(new_units.min)
45
- if min_index != 0 then
46
- new_units[min_index], new_units[0] = new_units[0], new_units[min_index]
61
+ if @elite_policy
62
+ min_index = new_units.index(new_units.min)
63
+ if min_index != 0 then
64
+ new_units[min_index], new_units[0] = new_units[0], new_units[min_index]
65
+ end
66
+ max_unit = units.max
67
+ new_units[0] = unit_cls.new(max_unit.genome)
47
68
  end
48
- new_units[0] = unit_cls.new(units.max.genome)
69
+
49
70
  new_units
50
71
  end
51
72
 
@@ -53,7 +74,7 @@ module GA
53
74
  last_index = nil
54
75
 
55
76
  units.each_with_index do |unit, index|
56
- next if index == 0
77
+ next if @elite_policy && index == 0
57
78
  next if rand() >= rate
58
79
 
59
80
  if last_index
@@ -69,7 +90,7 @@ module GA
69
90
 
70
91
  def mutate(units, rate)
71
92
  units.each_with_index do |unit, index|
72
- next if index == 0
93
+ next if @elite_policy && index == 0
73
94
  next if rand() >= rate
74
95
  unit.mutate!
75
96
  # recalculate fitness
@@ -80,6 +101,7 @@ module GA
80
101
  def output_debug_info(units, generations, generation)
81
102
  units.sort!
82
103
  info = [
104
+ "[#{(Time.now - @start_at).to_f}]",
83
105
  "GA-#{generation}/#{generations} #{units.count}-#{units[-1].genome.length}",
84
106
  ' fitness: '
85
107
  ]
@@ -88,6 +110,7 @@ module GA
88
110
  info << units.map(&:fitness).join(', ')
89
111
  else
90
112
  info << "#{units[0..2].map(&:fitness).join(', ')}"
113
+ info << " ... #{units[units.length / 2].fitness}"
91
114
  info << " ... #{units[-5..-1].map(&:fitness).join(', ')}"
92
115
  end
93
116
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ga
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - jiangzhi.xie
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-23 00:00:00.000000000 Z
11
+ date: 2017-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -83,7 +83,11 @@ files:
83
83
  - bin/console
84
84
  - bin/setup
85
85
  - examples/01bag.rb
86
+ - examples/Makefile
86
87
  - examples/robot.rb
88
+ - examples/robot_tester.cpp
89
+ - examples/robot_tester.h
90
+ - examples/test.cpp
87
91
  - ga.gemspec
88
92
  - lib/ga.rb
89
93
  - lib/ga/version.rb