gimuby 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/LICENSE.md +25 -0
- data/README.md +0 -0
- data/lib/gimuby.rb +10 -0
- data/lib/gimuby/config.rb +39 -0
- data/lib/gimuby/dependencies.rb +37 -0
- data/lib/gimuby/event/event.rb +29 -0
- data/lib/gimuby/event/event_manager.rb +34 -0
- data/lib/gimuby/factory.rb +275 -0
- data/lib/gimuby/genetic/archipelago/archipelago.rb +305 -0
- data/lib/gimuby/genetic/archipelago/connect_strategy/barabasi_albert_connect_strategy.rb +77 -0
- data/lib/gimuby/genetic/archipelago/connect_strategy/circle_connect_strategy.rb +11 -0
- data/lib/gimuby/genetic/archipelago/connect_strategy/connect_strategy.rb +34 -0
- data/lib/gimuby/genetic/archipelago/connect_strategy/constant_degree_connect_strategy.rb +22 -0
- data/lib/gimuby/genetic/archipelago/connect_strategy/fully_connected_connect_strategy.rb +11 -0
- data/lib/gimuby/genetic/archipelago/connect_strategy/random_connect_strategy.rb +29 -0
- data/lib/gimuby/genetic/archipelago/connect_strategy/watts_strogatz_connect_strategy.rb +63 -0
- data/lib/gimuby/genetic/archipelago/measure/clustering_coefficient_measure.rb +92 -0
- data/lib/gimuby/genetic/archipelago/measure/connected_measure.rb +64 -0
- data/lib/gimuby/genetic/archipelago/measure/diameter_measure.rb +38 -0
- data/lib/gimuby/genetic/archipelago/measure/measure.rb +7 -0
- data/lib/gimuby/genetic/archipelago/measure/shortest_paths_measure.rb +46 -0
- data/lib/gimuby/genetic/population/pick_strategy/bests_pick_strategy.rb +17 -0
- data/lib/gimuby/genetic/population/pick_strategy/pick_strategy.rb +21 -0
- data/lib/gimuby/genetic/population/pick_strategy/random_wheel_pick_strategy.rb +40 -0
- data/lib/gimuby/genetic/population/pick_strategy/tournament_pick_strategy.rb +26 -0
- data/lib/gimuby/genetic/population/population.rb +97 -0
- data/lib/gimuby/genetic/population/replace_strategy/replace_strategy.rb +9 -0
- data/lib/gimuby/genetic/population/replace_strategy/replace_worst_replace_strategy.rb +52 -0
- data/lib/gimuby/genetic/population/replace_strategy/uniform_replace_strategy.rb +48 -0
- data/lib/gimuby/genetic/solution/check_strategy/check_strategy.rb +8 -0
- data/lib/gimuby/genetic/solution/check_strategy/permutation_check_strategy.rb +37 -0
- data/lib/gimuby/genetic/solution/check_strategy/solution_space_check_strategy.rb +74 -0
- data/lib/gimuby/genetic/solution/function_based_solution.rb +64 -0
- data/lib/gimuby/genetic/solution/mutation_strategy/mutation_strategy.rb +22 -0
- data/lib/gimuby/genetic/solution/mutation_strategy/permutation_mutation_strategy.rb +17 -0
- data/lib/gimuby/genetic/solution/mutation_strategy/solution_space_mutation_strategy.rb +69 -0
- data/lib/gimuby/genetic/solution/new_generation_strategy/average_new_generation_strategy.rb +41 -0
- data/lib/gimuby/genetic/solution/new_generation_strategy/combined_new_generation_strategy.rb +27 -0
- data/lib/gimuby/genetic/solution/new_generation_strategy/cross_over_new_generation_strategy.rb +40 -0
- data/lib/gimuby/genetic/solution/new_generation_strategy/new_generation_strategy.rb +9 -0
- data/lib/gimuby/genetic/solution/new_generation_strategy/parent_range_new_generation_strategy.rb +42 -0
- data/lib/gimuby/genetic/solution/solution.rb +86 -0
- data/lib/gimuby/problem/foxholes/foxholes.rb +76 -0
- data/lib/gimuby/problem/foxholes/foxholes_solution.rb +29 -0
- data/lib/gimuby/problem/lennard_jones/lennard_jones.rb +38 -0
- data/lib/gimuby/problem/lennard_jones/lennard_jones_solution.rb +62 -0
- data/lib/gimuby/problem/rastrigin/rastrigin.rb +26 -0
- data/lib/gimuby/problem/rastrigin/rastrigin_solution.rb +35 -0
- data/lib/gimuby/problem/rosenbrock/rosenbrock.rb +14 -0
- data/lib/gimuby/problem/rosenbrock/rosenbrock_solution.rb +39 -0
- data/lib/gimuby/problem/schaffer/schaffer.rb +18 -0
- data/lib/gimuby/problem/schaffer/schaffer_solution.rb +29 -0
- data/lib/gimuby/problem/sphere/sphere.rb +9 -0
- data/lib/gimuby/problem/sphere/sphere_solution.rb +27 -0
- data/lib/gimuby/problem/step/step.rb +9 -0
- data/lib/gimuby/problem/step/step_solution.rb +29 -0
- data/lib/gimuby/problem/tsp/tsp.rb +76 -0
- data/lib/gimuby/problem/tsp/tsp_solution.rb +46 -0
- metadata +128 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'gimuby/dependencies'
|
2
|
+
require 'gimuby/genetic/population/pick_strategy/random_wheel_pick_strategy'
|
3
|
+
require 'gimuby/genetic/population/replace_strategy/replace_worst_replace_strategy'
|
4
|
+
|
5
|
+
class Population
|
6
|
+
|
7
|
+
def initialize(solutions = nil)
|
8
|
+
if solutions.nil?
|
9
|
+
@solutions = []
|
10
|
+
else
|
11
|
+
@solutions = solutions
|
12
|
+
end
|
13
|
+
@pick_strategy ||= RandomWheelPickStrategy.new
|
14
|
+
@replace_strategy ||= ReplaceWorstReplaceStrategy.new
|
15
|
+
|
16
|
+
trigger(:on_population_init)
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :solutions
|
20
|
+
attr_accessor :pick_strategy
|
21
|
+
attr_accessor :selection_rate
|
22
|
+
attr_accessor :replace_strategy
|
23
|
+
|
24
|
+
# Run a step of genetic algorithm: reproduction + mutation
|
25
|
+
def generation_step
|
26
|
+
reproduce
|
27
|
+
@solutions.each do |solution|
|
28
|
+
solution.mutate
|
29
|
+
end
|
30
|
+
trigger(:on_population_generation_step)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Replace part of the population (used by {Archipelago})
|
34
|
+
# @internal
|
35
|
+
def replace(solutions)
|
36
|
+
@solutions = @replace_strategy.replace(self, solutions)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Simply pick some solutions and make them reproduce
|
40
|
+
# @internal
|
41
|
+
def reproduce
|
42
|
+
@solutions = @replace_strategy.replace(self)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Pick some solutions
|
46
|
+
# @internal
|
47
|
+
def pick
|
48
|
+
@pick_strategy.pick(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a solution
|
52
|
+
# @param solution [Solution]
|
53
|
+
def add_solution(solution)
|
54
|
+
@solutions.push solution
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_population_size
|
58
|
+
@solutions.size
|
59
|
+
end
|
60
|
+
|
61
|
+
# Can be overridden
|
62
|
+
def get_fitness(solution)
|
63
|
+
solution.get_fitness
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_average_fitness
|
67
|
+
sum = 0
|
68
|
+
@solutions.each do |solution|
|
69
|
+
sum += solution.get_fitness
|
70
|
+
end
|
71
|
+
# Beware of that division by 0
|
72
|
+
sum / @solutions.length
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_best_fitness
|
76
|
+
best_solution = get_best_solution
|
77
|
+
best_solution.get_fitness
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_best_solution
|
81
|
+
best_solution = @solutions.min_by do |solution|
|
82
|
+
solution.get_fitness
|
83
|
+
end
|
84
|
+
best_solution
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def trigger(event_type)
|
90
|
+
event_data = {:population => self}
|
91
|
+
get_event_manager.trigger_event(event_type, event_data)
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_event_manager
|
95
|
+
$dependencies.event_manager
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'gimuby/genetic/population/replace_strategy/replace_strategy'
|
2
|
+
|
3
|
+
class ReplaceWorstReplaceStrategy < ReplaceStrategy
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@replace_proportion = 50.to_f / 100.to_f
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :replace_proportion
|
10
|
+
|
11
|
+
def replace(population, selected = nil)
|
12
|
+
solutions = population.solutions.clone
|
13
|
+
|
14
|
+
wished_length = solutions.length
|
15
|
+
solutions.sort! do |x, y|
|
16
|
+
x_fitness = population.get_fitness(x)
|
17
|
+
y_fitness = population.get_fitness(y)
|
18
|
+
x_fitness <=> y_fitness
|
19
|
+
end
|
20
|
+
if selected.nil?
|
21
|
+
selected = population.pick
|
22
|
+
end
|
23
|
+
number_to_remove = (wished_length * @replace_proportion).floor
|
24
|
+
if number_to_remove == wished_length
|
25
|
+
number_to_remove -= 1
|
26
|
+
end
|
27
|
+
solutions.slice!(-number_to_remove, number_to_remove)
|
28
|
+
|
29
|
+
while solutions.length < wished_length
|
30
|
+
begin
|
31
|
+
random_index1 = rand(selected.length)
|
32
|
+
random_index2 = rand(selected.length)
|
33
|
+
end while ((random_index1 == random_index2) && (selected.length != 1))
|
34
|
+
new_solutions = reproduce(selected[random_index1],
|
35
|
+
selected[random_index2])
|
36
|
+
new_solutions.each do |new_solution|
|
37
|
+
solutions.push(new_solution)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
solutions.slice!(wished_length) # we could have one that should be dropped
|
42
|
+
solutions
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def reproduce(solution1, solution2)
|
48
|
+
solution1.reproduce(solution1, solution2)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'gimuby/genetic/population/replace_strategy/replace_strategy'
|
2
|
+
|
3
|
+
class UniformReplaceStrategy < ReplaceStrategy
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@replace_proportion = 50.to_f / 100.to_f
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :replace_proportion
|
10
|
+
|
11
|
+
def replace(population, selected = nil)
|
12
|
+
solutions = population.solutions.clone
|
13
|
+
|
14
|
+
wished_length = solutions.length
|
15
|
+
solutions.shuffle!
|
16
|
+
if selected.nil?
|
17
|
+
selected = population.pick
|
18
|
+
end
|
19
|
+
number_to_remove = (wished_length * @replace_proportion).floor
|
20
|
+
if number_to_remove == wished_length
|
21
|
+
number_to_remove -= 1
|
22
|
+
end
|
23
|
+
solutions.slice!(-number_to_remove, number_to_remove)
|
24
|
+
|
25
|
+
while solutions.length < wished_length
|
26
|
+
begin
|
27
|
+
random_index1 = rand(selected.length)
|
28
|
+
random_index2 = rand(selected.length)
|
29
|
+
end while ((random_index1 == random_index2) && (selected.length != 1))
|
30
|
+
new_solutions = reproduce(selected[random_index1],
|
31
|
+
selected[random_index2])
|
32
|
+
new_solutions.each do |new_solution|
|
33
|
+
solutions.push(new_solution)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
solutions.slice!(wished_length) # we could have one that should be dropped
|
38
|
+
solutions
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def reproduce(solution1, solution2)
|
44
|
+
solution1.reproduce(solution1, solution2)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'gimuby/genetic/solution/check_strategy/check_strategy'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Permutation goes represented as permutation from [0, ... l] indexes
|
5
|
+
class PermutationCheckStrategy < CheckStrategy
|
6
|
+
|
7
|
+
def check(solution_representation)
|
8
|
+
permutation = solution_representation
|
9
|
+
expected_elements = *(0..permutation.length - 1)
|
10
|
+
duplicate = []
|
11
|
+
missing = []
|
12
|
+
expected_elements.each do |element|
|
13
|
+
match = permutation.select do |concreteElement|
|
14
|
+
concreteElement == element
|
15
|
+
end
|
16
|
+
case match.length <=> 1
|
17
|
+
when -1 then
|
18
|
+
missing.push(element)
|
19
|
+
when 1 then
|
20
|
+
duplicate.push(element)
|
21
|
+
else
|
22
|
+
# do nothing
|
23
|
+
end
|
24
|
+
end
|
25
|
+
missing.shuffle!
|
26
|
+
duplicate.each do |to_remove|
|
27
|
+
to_insert = missing.pop()
|
28
|
+
ind = permutation.index(to_remove)
|
29
|
+
permutation[ind] = to_insert
|
30
|
+
end
|
31
|
+
unless missing.empty?
|
32
|
+
solution_representation = check(solution_representation)
|
33
|
+
end
|
34
|
+
solution_representation
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'gimuby/genetic/solution/check_strategy/check_strategy'
|
2
|
+
|
3
|
+
# Permutation goes represented as permutation from [0, ... l] indexes
|
4
|
+
class SolutionSpaceCheckStrategy < CheckStrategy
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@default_min = nil
|
8
|
+
@default_max = nil
|
9
|
+
@mins = {}
|
10
|
+
@maxs = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def check(solution_representation)
|
14
|
+
solution_representation.each_index do |index|
|
15
|
+
value = solution_representation[index]
|
16
|
+
|
17
|
+
if value.class == Array
|
18
|
+
value = check(value)
|
19
|
+
else
|
20
|
+
min = get_min(index)
|
21
|
+
unless min.nil?
|
22
|
+
if value < min
|
23
|
+
value = min
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
max = get_max(index)
|
28
|
+
unless max.nil?
|
29
|
+
if value > max
|
30
|
+
value = max
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
solution_representation[index] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
solution_representation
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_min(min, index = nil)
|
42
|
+
if index.nil?
|
43
|
+
@default_min = min
|
44
|
+
else
|
45
|
+
@mins[index] = min
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_max(max, index = nil)
|
50
|
+
if index.nil?
|
51
|
+
@default_max = max
|
52
|
+
else
|
53
|
+
@maxs[index] = max
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def get_min(index)
|
60
|
+
min = @default_min
|
61
|
+
if @mins.has_key?(index)
|
62
|
+
min = @mins[index]
|
63
|
+
end
|
64
|
+
min
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_max(index)
|
68
|
+
max = @default_max
|
69
|
+
if @maxs.has_key?(index)
|
70
|
+
max = @maxs[index]
|
71
|
+
end
|
72
|
+
max
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'gimuby/genetic/solution/solution'
|
2
|
+
require 'gimuby/genetic/solution/check_strategy/solution_space_check_strategy'
|
3
|
+
require 'gimuby/genetic/solution/new_generation_strategy/combined_new_generation_strategy'
|
4
|
+
require 'gimuby/genetic/solution/new_generation_strategy/parent_range_new_generation_strategy'
|
5
|
+
require 'gimuby/genetic/solution/new_generation_strategy/cross_over_new_generation_strategy'
|
6
|
+
require 'gimuby/genetic/solution/new_generation_strategy/average_new_generation_strategy'
|
7
|
+
require 'gimuby/genetic/solution/mutation_strategy/solution_space_mutation_strategy'
|
8
|
+
|
9
|
+
class FunctionBasedSolution < Solution
|
10
|
+
|
11
|
+
def initialize(x_values = nil)
|
12
|
+
super(x_values)
|
13
|
+
|
14
|
+
@check_strategy = SolutionSpaceCheckStrategy.new
|
15
|
+
@check_strategy.set_min(get_x_value_min)
|
16
|
+
@check_strategy.set_max(get_x_value_max)
|
17
|
+
|
18
|
+
@new_generation_strategy = CombinedNewGenerationStrategy.new
|
19
|
+
@new_generation_strategy.add_strategy(ParentRangeNewGenerationStrategy.new)
|
20
|
+
@new_generation_strategy.add_strategy(CrossOverNewGenerationStrategy.new)
|
21
|
+
@new_generation_strategy.add_strategy(AverageNewGenerationStrategy.new)
|
22
|
+
|
23
|
+
@mutation_strategy = SolutionSpaceMutationStrategy.new
|
24
|
+
@mutation_strategy.set_min(get_x_value_min)
|
25
|
+
@mutation_strategy.set_max(get_x_value_max)
|
26
|
+
end
|
27
|
+
|
28
|
+
def evaluate
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_solution_representation
|
33
|
+
@x_values.clone
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_solution_representation(x_values)
|
37
|
+
@x_values = x_values.clone
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def init_representation
|
43
|
+
@x_values = []
|
44
|
+
dimension = get_dimension_number
|
45
|
+
dimension.times do |_|
|
46
|
+
range = get_x_value_max - get_x_value_min
|
47
|
+
x_value = (rand() * range) + get_x_value_min
|
48
|
+
@x_values.push(x_value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_x_value_min
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_x_value_max
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_dimension_number
|
61
|
+
raise NotImplementedError
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class MutationStrategy
|
4
|
+
|
5
|
+
def initialize(mutation_rate = 0.01)
|
6
|
+
@mutation_rate = mutation_rate
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :mutation_rate
|
10
|
+
|
11
|
+
def mutate(solution)
|
12
|
+
if rand < @mutation_rate
|
13
|
+
perform_mutation(solution)
|
14
|
+
solution.reset_fitness_state
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def perform_mutation(solution)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'gimuby/genetic/solution/mutation_strategy/mutation_strategy'
|
2
|
+
|
3
|
+
class PermutationMutationStrategy < MutationStrategy
|
4
|
+
|
5
|
+
def perform_mutation(solution)
|
6
|
+
permutation = solution.get_solution_representation
|
7
|
+
begin
|
8
|
+
index1 = rand(permutation.length)
|
9
|
+
index2 = rand(permutation.length)
|
10
|
+
end while index1 == index2
|
11
|
+
tmp = permutation[index1]
|
12
|
+
permutation[index1] = permutation[index2]
|
13
|
+
permutation[index2] = tmp
|
14
|
+
solution.set_solution_representation(permutation)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'gimuby/genetic/solution/mutation_strategy/mutation_strategy'
|
2
|
+
|
3
|
+
class SolutionSpaceMutationStrategy < MutationStrategy
|
4
|
+
|
5
|
+
def initialize(mutation_rate = 0.01)
|
6
|
+
super(mutation_rate)
|
7
|
+
@default_min = nil
|
8
|
+
@default_max = nil
|
9
|
+
@mins = {}
|
10
|
+
@maxs = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform_mutation(solution)
|
14
|
+
x_values = solution.get_solution_representation
|
15
|
+
x_values = perform_mutation_from_representation(x_values)
|
16
|
+
solution.set_solution_representation(x_values)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_min(min, index = nil)
|
20
|
+
if index.nil?
|
21
|
+
@default_min = min
|
22
|
+
else
|
23
|
+
@mins[index] = min
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_max(max, index = nil)
|
28
|
+
if index.nil?
|
29
|
+
@default_max = max
|
30
|
+
else
|
31
|
+
@maxs[index] = max
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def perform_mutation_from_representation(x_values)
|
38
|
+
index = rand(x_values.length)
|
39
|
+
|
40
|
+
x_value = x_values[index]
|
41
|
+
if x_value.class == Array
|
42
|
+
x_value = perform_mutation_from_representation(x_value)
|
43
|
+
else
|
44
|
+
min = get_min(index)
|
45
|
+
max = get_max(index)
|
46
|
+
range = max - min
|
47
|
+
x_value = rand() * range + min
|
48
|
+
end
|
49
|
+
x_values[index] = x_value
|
50
|
+
x_values
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_min(index)
|
54
|
+
min = @default_min
|
55
|
+
if @mins.has_key?(index)
|
56
|
+
min = @mins[index]
|
57
|
+
end
|
58
|
+
min
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_max(index)
|
62
|
+
max = @default_max
|
63
|
+
if @maxs.has_key?(index)
|
64
|
+
max = @maxs[index]
|
65
|
+
end
|
66
|
+
max
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|