gimuby 0.7.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.
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