gimuby 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/Gemfile +1 -0
  2. data/LICENSE.md +25 -0
  3. data/README.md +0 -0
  4. data/lib/gimuby.rb +10 -0
  5. data/lib/gimuby/config.rb +39 -0
  6. data/lib/gimuby/dependencies.rb +37 -0
  7. data/lib/gimuby/event/event.rb +29 -0
  8. data/lib/gimuby/event/event_manager.rb +34 -0
  9. data/lib/gimuby/factory.rb +275 -0
  10. data/lib/gimuby/genetic/archipelago/archipelago.rb +305 -0
  11. data/lib/gimuby/genetic/archipelago/connect_strategy/barabasi_albert_connect_strategy.rb +77 -0
  12. data/lib/gimuby/genetic/archipelago/connect_strategy/circle_connect_strategy.rb +11 -0
  13. data/lib/gimuby/genetic/archipelago/connect_strategy/connect_strategy.rb +34 -0
  14. data/lib/gimuby/genetic/archipelago/connect_strategy/constant_degree_connect_strategy.rb +22 -0
  15. data/lib/gimuby/genetic/archipelago/connect_strategy/fully_connected_connect_strategy.rb +11 -0
  16. data/lib/gimuby/genetic/archipelago/connect_strategy/random_connect_strategy.rb +29 -0
  17. data/lib/gimuby/genetic/archipelago/connect_strategy/watts_strogatz_connect_strategy.rb +63 -0
  18. data/lib/gimuby/genetic/archipelago/measure/clustering_coefficient_measure.rb +92 -0
  19. data/lib/gimuby/genetic/archipelago/measure/connected_measure.rb +64 -0
  20. data/lib/gimuby/genetic/archipelago/measure/diameter_measure.rb +38 -0
  21. data/lib/gimuby/genetic/archipelago/measure/measure.rb +7 -0
  22. data/lib/gimuby/genetic/archipelago/measure/shortest_paths_measure.rb +46 -0
  23. data/lib/gimuby/genetic/population/pick_strategy/bests_pick_strategy.rb +17 -0
  24. data/lib/gimuby/genetic/population/pick_strategy/pick_strategy.rb +21 -0
  25. data/lib/gimuby/genetic/population/pick_strategy/random_wheel_pick_strategy.rb +40 -0
  26. data/lib/gimuby/genetic/population/pick_strategy/tournament_pick_strategy.rb +26 -0
  27. data/lib/gimuby/genetic/population/population.rb +97 -0
  28. data/lib/gimuby/genetic/population/replace_strategy/replace_strategy.rb +9 -0
  29. data/lib/gimuby/genetic/population/replace_strategy/replace_worst_replace_strategy.rb +52 -0
  30. data/lib/gimuby/genetic/population/replace_strategy/uniform_replace_strategy.rb +48 -0
  31. data/lib/gimuby/genetic/solution/check_strategy/check_strategy.rb +8 -0
  32. data/lib/gimuby/genetic/solution/check_strategy/permutation_check_strategy.rb +37 -0
  33. data/lib/gimuby/genetic/solution/check_strategy/solution_space_check_strategy.rb +74 -0
  34. data/lib/gimuby/genetic/solution/function_based_solution.rb +64 -0
  35. data/lib/gimuby/genetic/solution/mutation_strategy/mutation_strategy.rb +22 -0
  36. data/lib/gimuby/genetic/solution/mutation_strategy/permutation_mutation_strategy.rb +17 -0
  37. data/lib/gimuby/genetic/solution/mutation_strategy/solution_space_mutation_strategy.rb +69 -0
  38. data/lib/gimuby/genetic/solution/new_generation_strategy/average_new_generation_strategy.rb +41 -0
  39. data/lib/gimuby/genetic/solution/new_generation_strategy/combined_new_generation_strategy.rb +27 -0
  40. data/lib/gimuby/genetic/solution/new_generation_strategy/cross_over_new_generation_strategy.rb +40 -0
  41. data/lib/gimuby/genetic/solution/new_generation_strategy/new_generation_strategy.rb +9 -0
  42. data/lib/gimuby/genetic/solution/new_generation_strategy/parent_range_new_generation_strategy.rb +42 -0
  43. data/lib/gimuby/genetic/solution/solution.rb +86 -0
  44. data/lib/gimuby/problem/foxholes/foxholes.rb +76 -0
  45. data/lib/gimuby/problem/foxholes/foxholes_solution.rb +29 -0
  46. data/lib/gimuby/problem/lennard_jones/lennard_jones.rb +38 -0
  47. data/lib/gimuby/problem/lennard_jones/lennard_jones_solution.rb +62 -0
  48. data/lib/gimuby/problem/rastrigin/rastrigin.rb +26 -0
  49. data/lib/gimuby/problem/rastrigin/rastrigin_solution.rb +35 -0
  50. data/lib/gimuby/problem/rosenbrock/rosenbrock.rb +14 -0
  51. data/lib/gimuby/problem/rosenbrock/rosenbrock_solution.rb +39 -0
  52. data/lib/gimuby/problem/schaffer/schaffer.rb +18 -0
  53. data/lib/gimuby/problem/schaffer/schaffer_solution.rb +29 -0
  54. data/lib/gimuby/problem/sphere/sphere.rb +9 -0
  55. data/lib/gimuby/problem/sphere/sphere_solution.rb +27 -0
  56. data/lib/gimuby/problem/step/step.rb +9 -0
  57. data/lib/gimuby/problem/step/step_solution.rb +29 -0
  58. data/lib/gimuby/problem/tsp/tsp.rb +76 -0
  59. data/lib/gimuby/problem/tsp/tsp_solution.rb +46 -0
  60. metadata +128 -0
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ rake
@@ -0,0 +1,25 @@
1
+ The MIT License
2
+ ===============
3
+
4
+ Copyright © 2014 Fräntz Miccoli
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ this software and associated documentation files (the "Software"), to deal in
8
+ the Software without restriction, including without limitation the rights to
9
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
+ of the Software, and to permit persons to whom the Software is furnished to do
11
+ so, subject to the following conditions:
12
+
13
+ Except as contained in this notice, the name(s) of the above copyright holders
14
+ shall not be used in advertising or otherwise to promote the sale, use or other
15
+ dealings in this Software without prior written authorization.
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
22
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
23
+ OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File without changes
@@ -0,0 +1,10 @@
1
+ require 'gimuby/dependencies'
2
+ require 'gimuby/factory'
3
+
4
+ module Gimuby
5
+
6
+ def self.get_factory
7
+ Factory.new
8
+ end
9
+
10
+ end
@@ -0,0 +1,39 @@
1
+
2
+ # Please note that the default are important to let unit test run perfectly
3
+ class GimubyConfig
4
+
5
+ def initialize
6
+ @persistence_dir_path = '/tmp'
7
+
8
+ # Problem specific configuration
9
+
10
+ # TSP
11
+ @tsp_number_points = 100
12
+
13
+ # Rastrigin
14
+ @rastrigin_dimension = 20
15
+
16
+ # Lennard Jones
17
+ @lennard_jones_atoms = 31
18
+ end
19
+
20
+ attr_accessor :persistence_dir_path
21
+
22
+ attr_accessor :problem
23
+
24
+ # TSP
25
+ attr_accessor :tsp_number_points
26
+
27
+ # Rastrigin
28
+ attr_accessor :rastrigin_dimension
29
+
30
+ # Lennard Jones
31
+ attr_accessor :lennard_jones_atoms
32
+
33
+ attr_accessor :optimal_population
34
+ attr_accessor :optimal_archipelago
35
+
36
+ end
37
+
38
+ $config = GimubyConfig.new()
39
+
@@ -0,0 +1,37 @@
1
+ require 'gimuby/factory'
2
+ require 'gimuby/event/event_manager'
3
+ require 'gimuby/problem/tsp/tsp'
4
+ require 'gimuby/problem/foxholes/foxholes'
5
+
6
+ # Dependencies container that is accessed from everywhere in the app
7
+ class Dependencies
8
+
9
+ def initialize
10
+ @event_manager = EventManager.new
11
+
12
+ # problem specific
13
+ @tsp = nil
14
+ @foxholes = nil
15
+ end
16
+
17
+ attr_accessor :event_manager
18
+
19
+ # problem specific
20
+
21
+ def tsp
22
+ if @tsp.nil?
23
+ @tsp = Tsp.new
24
+ end
25
+ @tsp
26
+ end
27
+
28
+ def foxholes
29
+ if @foxholes.nil?
30
+ @foxholes = Foxholes.new
31
+ end
32
+ @foxholes
33
+ end
34
+
35
+ end
36
+
37
+ $dependencies = Dependencies.new
@@ -0,0 +1,29 @@
1
+ require 'gimuby/dependencies'
2
+
3
+ class Event
4
+
5
+ # @param format [String] The name of the event
6
+ # @param data [Hash] An Hash containing the data that will be passed
7
+ # to the listeners
8
+ # @api
9
+ def initialize(name = nil?, data = {})
10
+ @name = name
11
+ @data = data
12
+ end
13
+
14
+ attr_accessor :name
15
+ attr_accessor :data
16
+
17
+ # Trigger the event (through the event manager)
18
+ # @api
19
+ def trigger
20
+ event_manager = get_event_manager
21
+ event_manager.trigger_event(name, self)
22
+ end
23
+
24
+ protected
25
+
26
+ def get_event_manager
27
+ $dependencies.event_manager
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ require 'gimuby/event/event'
2
+
3
+ # Core of the event managing system, this class stores listener and call them
4
+ # on event triggering.
5
+ class EventManager
6
+
7
+ def initialize
8
+ @listeners_registry = {} #contains array
9
+ end
10
+
11
+ # @param name {String} The event name
12
+ # @param block {Block} The callback that will be call
13
+ # @api
14
+ def register_listener(name, &block)
15
+ unless @listeners_registry.has_key? name
16
+ @listeners_registry[name] = []
17
+ end
18
+ @listeners_registry[name].push block
19
+ end
20
+
21
+ # @param name {String} The event name
22
+ # @param event {Event} The event object
23
+ def trigger_event(name, event = {})
24
+ if @listeners_registry.has_key? name
25
+ if event.class == Hash
26
+ event = Event.new(name, event)
27
+ end
28
+ listeners = @listeners_registry[name]
29
+ listeners.each do |block|
30
+ block.call event
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,275 @@
1
+ require 'gimuby/dependencies'
2
+ require 'gimuby/config'
3
+ require 'gimuby/genetic/archipelago/archipelago'
4
+ require 'gimuby/genetic/population/population'
5
+ require 'gimuby/genetic/population/pick_strategy/bests_pick_strategy'
6
+ require 'gimuby/genetic/population/pick_strategy/random_wheel_pick_strategy'
7
+ require 'gimuby/genetic/population/pick_strategy/tournament_pick_strategy'
8
+ require 'gimuby/genetic/population/replace_strategy/replace_worst_replace_strategy'
9
+ require 'gimuby/genetic/population/replace_strategy/uniform_replace_strategy'
10
+ require 'gimuby/genetic/archipelago/connect_strategy/circle_connect_strategy'
11
+ require 'gimuby/genetic/archipelago/connect_strategy/fully_connected_connect_strategy'
12
+ require 'gimuby/genetic/archipelago/connect_strategy/random_connect_strategy'
13
+ require 'gimuby/genetic/archipelago/connect_strategy/constant_degree_connect_strategy'
14
+ require 'gimuby/genetic/archipelago/connect_strategy/barabasi_albert_connect_strategy'
15
+ require 'gimuby/genetic/archipelago/connect_strategy/watts_strogatz_connect_strategy'
16
+ require 'gimuby/problem/tsp/tsp_solution'
17
+ require 'gimuby/problem/rastrigin/rastrigin_solution'
18
+ require 'gimuby/problem/lennard_jones/lennard_jones_solution'
19
+ require 'gimuby/problem/schaffer/schaffer_solution'
20
+ require 'gimuby/problem/rosenbrock/rosenbrock_solution'
21
+ require 'gimuby/problem/step/step_solution'
22
+ require 'gimuby/problem/sphere/sphere_solution'
23
+ require 'gimuby/problem/foxholes/foxholes_solution'
24
+
25
+ class Factory
26
+
27
+ def initialize
28
+ @optimal_population = FALSE
29
+ @optimal_archipelago = FALSE
30
+
31
+ # use for test purpose to avoid provided archipelago to be connected
32
+ @connected_archipelago = TRUE
33
+
34
+ # those values are set by the user, let them nil and we won't use them
35
+ @solutions_number = nil
36
+ @populations_number = nil
37
+
38
+ # if we have to generate values we store them here
39
+ @_solutions_number = nil
40
+ @_populations_number = nil
41
+ end
42
+
43
+ attr_accessor :optimal_population
44
+ attr_accessor :optimal_archipelago
45
+
46
+ attr_accessor :connected_archipelago
47
+
48
+ attr_accessor :solutions_number
49
+ attr_accessor :populations_number
50
+
51
+ def reset_state
52
+ @connected_archipelago = TRUE
53
+ @_populations_number = nil
54
+ @_solutions_number = nil
55
+ end
56
+
57
+ def get_population(&solution_generator)
58
+ _get_population(:population, &solution_generator)
59
+ end
60
+
61
+ def get_archipelago(&solution_generator)
62
+ if @optimal_archipelago
63
+ return get_optimal_archipelago(&solution_generator)
64
+ end
65
+ get_random_archipelago(&solution_generator)
66
+ end
67
+
68
+ def get_optimal_population(optimizer, &solution_generator)
69
+ candidate_population = get_random_population(optimizer, &solution_generator)
70
+ begin
71
+ candidate_population = get_random_population(optimizer, &solution_generator)
72
+ if candidate_population.solutions.length >= 1000
73
+ next
74
+ end
75
+ if candidate_population.solutions[0].mutation_strategy.mutation_rate >= 0.1
76
+ next
77
+ end
78
+ if candidate_population.replace_strategy.replace_proportion <= 0.4
79
+ next
80
+ end
81
+ if candidate_population.pick_strategy.pick_proportion >= 0.4
82
+ next
83
+ end
84
+ end while FALSE
85
+ candidate_population
86
+ end
87
+
88
+ def get_optimal_archipelago(&solution_generator)
89
+ is_optimal = FALSE
90
+ begin
91
+ candidate_archipelago = get_random_archipelago(&solution_generator)
92
+ is_optimal = TRUE
93
+ if (candidate_archipelago.get_population_size <= 500) or
94
+ (candidate_archipelago.get_population_size >= 1100)
95
+ is_optimal = FALSE
96
+ end
97
+ if candidate_archipelago.migration_rate <= 0.25
98
+ is_optimal = FALSE
99
+ end
100
+ if (candidate_archipelago.populations.length < 10) or
101
+ (candidate_archipelago.populations.length > 29)
102
+ is_optimal = FALSE
103
+ end
104
+ if candidate_archipelago.get_diameter >= 100
105
+ is_optimal = FALSE
106
+ end
107
+ end until is_optimal
108
+ candidate_archipelago
109
+ end
110
+
111
+ def get_random_population(optimizer, &solution_generator)
112
+ population = Population.new
113
+ population.pick_strategy = get_random_pick_strategy
114
+ population.replace_strategy = get_random_replace_strategy
115
+ populations_number = get_populations_number
116
+ unless optimizer == :archipelago
117
+ populations_number = 1
118
+ end
119
+ solutions_number = get_solutions_number(populations_number)
120
+ number_solutions_per_population = solutions_number / populations_number
121
+ mutation_rate = rand * 0.7
122
+ number_solutions_per_population.times do
123
+ solution = solution_generator.call
124
+ solution.mutation_strategy.mutation_rate = mutation_rate
125
+ population.add_solution(solution)
126
+ end
127
+
128
+ population
129
+ end
130
+
131
+ def get_random_archipelago(&solution_generator)
132
+ archipelago = Archipelago.new
133
+ archipelago.connect_strategy = get_random_connect_strategy
134
+ # between 0.01% and ~35%
135
+ archipelago.migration_rate = (rand() * 35.0 / 100.0) + 0.0001
136
+ archipelago.migration_symmetric = [true, false].choice
137
+ archipelago.migration_type = [:random, :synchronized, :fixed_time].choice
138
+ get_populations_number.times do |_|
139
+ archipelago.add_population(_get_population(:archipelago, &solution_generator))
140
+ end
141
+ if @connected_archipelago
142
+ archipelago.connect_all
143
+ end
144
+ archipelago
145
+ end
146
+
147
+ protected
148
+
149
+ def _get_population(optimizer, &solution_generator)
150
+ if @optimal_population
151
+ return get_optimal_population(optimizer, &solution_generator)
152
+ end
153
+ get_random_population(optimizer, &solution_generator)
154
+ end
155
+
156
+ def get_solutions_number(populations_number = 1)
157
+ unless @_solutions_number.nil?
158
+ return @_solutions_number
159
+ end
160
+ unless @solutions_number.nil?
161
+ return @solutions_number
162
+ end
163
+
164
+ max_population_size = 5000
165
+ min_population_size = 12 * populations_number
166
+ if @optimal_population
167
+ max_population_size = 1000
168
+ end
169
+ if @optimal_archipelago
170
+ min_population_size = 500
171
+ max_population_size = [max_population_size, 1100].min
172
+ end
173
+
174
+ solutions_number_range = *(min_population_size..max_population_size)
175
+ picked_solutions_number = 0
176
+ solutions_per_population = 0
177
+ min_solutions_per_population = min_population_size / populations_number
178
+ begin
179
+ picked_solutions_number = solutions_number_range.choice
180
+ picked_solutions_number /= populations_number
181
+ picked_solutions_number *= populations_number
182
+ solutions_per_population = picked_solutions_number / populations_number
183
+ end while solutions_per_population < min_solutions_per_population
184
+
185
+ @_solutions_number = picked_solutions_number
186
+ @_solutions_number
187
+ end
188
+
189
+ def get_populations_number
190
+ unless @_populations_number.nil?
191
+ return @_populations_number
192
+ end
193
+ unless @populations_number.nil?
194
+ @_populations_number = @populations_number
195
+ return @_populations_number
196
+ end
197
+
198
+ populations_number_range = *(2..100)
199
+ if @optimal_archipelago
200
+ populations_number_range = *(10..29)
201
+ end
202
+
203
+ @_populations_number = populations_number_range.choice
204
+ @_populations_number
205
+ end
206
+
207
+ def get_random_pick_strategy
208
+ max = 10
209
+ r = rand(max)
210
+ if r == 0
211
+ return BestsPickStrategy.new
212
+ elsif r == 1
213
+ return TournamentPickStrategy.new
214
+ end
215
+ r = rand(9) + 1
216
+ strategy = RandomWheelPickStrategy.new
217
+ reason = 1.0 / 10.0 * r
218
+ strategy.random_wheel_probability_reason = reason
219
+
220
+ # pick proportion
221
+ min = 0.1
222
+ max = 0.9
223
+ spread = max - min
224
+ strategy.pick_proportion = (rand() * spread) + min
225
+
226
+ strategy
227
+ end
228
+
229
+ def get_random_replace_strategy
230
+ if rand() < 0.2
231
+ strategy = UniformReplaceStrategy.new
232
+ else
233
+ strategy = ReplaceWorstReplaceStrategy.new
234
+ end
235
+
236
+ # replace proportion
237
+ min = 0.05
238
+ max = 1.0
239
+ spread = max - min
240
+ replace_proportion = (rand() * spread) + min
241
+ strategy.replace_proportion = replace_proportion
242
+ strategy
243
+ end
244
+
245
+ def get_random_connect_strategy
246
+ r = rand(10)
247
+ if r == 0
248
+ return CircleConnectStrategy.new
249
+ end
250
+ if r == 1
251
+ return FullyConnectedConnectStrategy.new
252
+ end
253
+ r = rand(4)
254
+ max_degree = [22, get_populations_number - 1].min
255
+ # even number means easier by two divisions
256
+ average_degree = (rand((max_degree - 1)/ 2) + 1) * 2
257
+ # avoid a bug on an edge case when max_degree = 2
258
+ average_degree = [average_degree, max_degree].min
259
+ case r
260
+ when 0
261
+ strategy = RandomConnectStrategy.new
262
+ when 1
263
+ strategy = ConstantDegreeConnectStrategy.new
264
+ when 2
265
+ strategy = BarabasiAlbertConnectStrategy.new
266
+ when 3
267
+ strategy = WattsStrogatzConnectStrategy.new
268
+ else
269
+ raise 'random value (r) has an unexpected value'
270
+ end
271
+ strategy.average_degree = average_degree
272
+ strategy
273
+ end
274
+
275
+ end