ga 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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