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.
- 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,29 @@
|
|
1
|
+
require 'gimuby/genetic/archipelago/connect_strategy/connect_strategy'
|
2
|
+
|
3
|
+
|
4
|
+
class RandomConnectStrategy < ConnectStrategy
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@average_degree = 4.0
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :average_degree
|
11
|
+
|
12
|
+
def connect(archipelago)
|
13
|
+
nodes = get_nodes(archipelago)
|
14
|
+
connections_to_make = @average_degree * nodes.length
|
15
|
+
check_connections_to_make(connections_to_make, nodes)
|
16
|
+
made = 0
|
17
|
+
begin
|
18
|
+
from_node = nodes[rand(nodes.length)]
|
19
|
+
to_node = nodes[rand(nodes.length)]
|
20
|
+
has_edge = archipelago.has_edge(from_node, to_node)
|
21
|
+
if from_node != to_node || has_edge
|
22
|
+
archipelago.add_edge(from_node, to_node)
|
23
|
+
made += 1
|
24
|
+
end
|
25
|
+
end while made < connections_to_make
|
26
|
+
made
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'gimuby/genetic/archipelago/connect_strategy/connect_strategy'
|
2
|
+
|
3
|
+
class WattsStrogatzConnectStrategy < ConnectStrategy
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@average_degree = 4.0
|
7
|
+
@rewire_rate = 0.2
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :average_degree
|
11
|
+
attr_accessor :rewire_rate
|
12
|
+
|
13
|
+
def connect(archipelago)
|
14
|
+
make_regular_latice(archipelago)
|
15
|
+
rewire(archipelago)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def make_regular_latice(archipelago)
|
21
|
+
depth = (@average_degree / 2).floor
|
22
|
+
depth.times do |i|
|
23
|
+
# create a circle
|
24
|
+
# then create a path with all second range neighbor
|
25
|
+
# until reaching depth
|
26
|
+
current_depth = i + 1
|
27
|
+
current_depth.times do |first_node_index|
|
28
|
+
# if depth = 2, there is two paths to build
|
29
|
+
# [0, 2, 4, ...] and [1, 3, 5, ...]
|
30
|
+
nodes = get_nodes(archipelago)
|
31
|
+
path = []
|
32
|
+
nodes.each_with_index do |key, node|
|
33
|
+
if (key % current_depth) == first_node_index
|
34
|
+
path.push(node)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
archipelago.connect_path(path, TRUE)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def rewire(archipelago)
|
43
|
+
edges = get_edges_to_rewire(archipelago)
|
44
|
+
nodes = get_nodes(archipelago)
|
45
|
+
edges.each do |edge|
|
46
|
+
node1 = edge[0]
|
47
|
+
node2 = edge[1]
|
48
|
+
if node1 < node2 # we do not process the other case (undirected model)
|
49
|
+
should_rewire = @rewire_rate < rand()
|
50
|
+
if should_rewire
|
51
|
+
new_node = nodes.choice while archipelago.has_edge(node1, new_node)
|
52
|
+
archipelago.remove_edge(node1, node2)
|
53
|
+
archipelago.connect(node1, new_node)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_edges_to_rewire(archipelago)
|
60
|
+
archipelago.get_edges
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'gimuby/genetic/archipelago/measure/measure'
|
2
|
+
|
3
|
+
# Compute the network average clustering coefficient as defined on
|
4
|
+
# http://en.wikipedia.org/wiki/Clustering_coefficient#Network_average_clustering_coefficient
|
5
|
+
# revision 19:31, 6 July 2013
|
6
|
+
#
|
7
|
+
# Beware if you update code, this class has a state don't break the
|
8
|
+
# workflow around the @potential_clusters member
|
9
|
+
class ClusteringCoefficientMeasure < Measure
|
10
|
+
|
11
|
+
# @param archipelago [Archipelago]
|
12
|
+
# @return [float] the clustering coefficient
|
13
|
+
def compute(archipelago)
|
14
|
+
local_clustering_coefficients = []
|
15
|
+
archipelago.get_nodes.each do |node|
|
16
|
+
local_clustering_coefficient = get_local_clustering_coefficient(archipelago, node)
|
17
|
+
local_clustering_coefficients.push(local_clustering_coefficient)
|
18
|
+
end
|
19
|
+
local_clustering_coefficients.inject(:+) / local_clustering_coefficients.length.to_f
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def get_local_clustering_coefficient(archipelago, node)
|
25
|
+
init_local_computation_properties
|
26
|
+
potential_clusters_count = compute_potential_local_clusters(archipelago, node).length
|
27
|
+
clusters_count = compute_real_local_clusters(archipelago).length
|
28
|
+
if potential_clusters_count == 0
|
29
|
+
return 0
|
30
|
+
end
|
31
|
+
clusters_count.to_f / potential_clusters_count.to_f
|
32
|
+
end
|
33
|
+
|
34
|
+
def init_local_computation_properties
|
35
|
+
@potential_clusters = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def compute_potential_local_clusters(archipelago, node)
|
39
|
+
neighbors = archipelago.get_neighbors(node)
|
40
|
+
neighbors.each do |neighbor1|
|
41
|
+
neighbors.each do |neighbor2|
|
42
|
+
#print '---------'
|
43
|
+
#print "\n"
|
44
|
+
#print neighbors.join(' ')
|
45
|
+
#print "\n"
|
46
|
+
#print node, ' ', neighbor1, ' ', neighbor2
|
47
|
+
#print "\n"
|
48
|
+
register_potential_clusters(node, neighbor1, neighbor2)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@potential_clusters
|
52
|
+
end
|
53
|
+
|
54
|
+
def compute_real_local_clusters(archipelago)
|
55
|
+
clusters = []
|
56
|
+
@potential_clusters.each do |potential_cluster|
|
57
|
+
node1 = potential_cluster[0]
|
58
|
+
node2 = potential_cluster[1]
|
59
|
+
node3 = potential_cluster[2]
|
60
|
+
fully_connected = archipelago.has_edge(node1, node2) && \
|
61
|
+
archipelago.has_edge(node3, node2) && \
|
62
|
+
archipelago.has_edge(node1, node3)
|
63
|
+
if fully_connected
|
64
|
+
clusters.push(potential_cluster.clone)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
clusters
|
68
|
+
end
|
69
|
+
|
70
|
+
def register_potential_clusters(node1, node2, node3)
|
71
|
+
if node1 == node2
|
72
|
+
# invalid set
|
73
|
+
return FALSE
|
74
|
+
end
|
75
|
+
nodes = [node1, node2]
|
76
|
+
if nodes.include?(node3)
|
77
|
+
# invalid set
|
78
|
+
return FALSE
|
79
|
+
end
|
80
|
+
nodes.push(node3)
|
81
|
+
nodes.each do |node|
|
82
|
+
return FALSE if node.nil?
|
83
|
+
end
|
84
|
+
nodes.sort!
|
85
|
+
if @potential_clusters.include?(nodes)
|
86
|
+
# value already taken into account
|
87
|
+
return FALSE
|
88
|
+
end
|
89
|
+
@potential_clusters.push(nodes)
|
90
|
+
TRUE
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'gimuby/genetic/archipelago/measure/measure'
|
2
|
+
|
3
|
+
# Return the number of connected class for an archipelago
|
4
|
+
class ConnectedMeasure < Measure
|
5
|
+
|
6
|
+
# @param archipelago {Archipelago}
|
7
|
+
def compute(archipelago)
|
8
|
+
init_connected_classes(archipelago)
|
9
|
+
old_signature = get_state_signature
|
10
|
+
new_signature = ''
|
11
|
+
until old_signature == new_signature
|
12
|
+
old_signature = new_signature
|
13
|
+
try_to_reduce(archipelago)
|
14
|
+
new_signature = get_state_signature
|
15
|
+
end
|
16
|
+
get_connected_classes_count
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def init_connected_classes(archipelago)
|
22
|
+
nodes = archipelago.get_nodes
|
23
|
+
@connected_classes = {}
|
24
|
+
nodes.each do |node|
|
25
|
+
@connected_classes[node] = node
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_state_signature
|
30
|
+
signature = ''
|
31
|
+
@connected_classes.each do |k, v|
|
32
|
+
signature = '|' + k.to_s + '-->' + v.to_s + "\n"
|
33
|
+
end
|
34
|
+
signature
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_connected_classes_count
|
38
|
+
different_classes = []
|
39
|
+
@connected_classes.each do |_, connected_class|
|
40
|
+
unless different_classes.include?(connected_class)
|
41
|
+
different_classes.push(connected_class)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
different_classes.length
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param archipelago {Archipelago}
|
48
|
+
def try_to_reduce(archipelago)
|
49
|
+
@connected_classes.each do |key1, value1|
|
50
|
+
@connected_classes.each do |key2, value2|
|
51
|
+
unless (key1 == key2) || (value1 == value2)
|
52
|
+
if archipelago.has_edge(key1, key2)
|
53
|
+
if value1 < value2
|
54
|
+
@connected_classes[key2] = value1
|
55
|
+
else
|
56
|
+
@connected_classes[key1] = value2
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'gimuby/genetic/archipelago/measure/measure'
|
2
|
+
require 'gimuby/genetic/archipelago/measure/shortest_paths_measure'
|
3
|
+
require 'gimuby/genetic/archipelago/measure/connected_measure'
|
4
|
+
|
5
|
+
|
6
|
+
# Return the diameter of an archipelago
|
7
|
+
# If archipelago is not connex, simply output twice number of its nodes
|
8
|
+
class DiameterMeasure < Measure
|
9
|
+
|
10
|
+
def compute(archipelago)
|
11
|
+
unless is_connected(archipelago)
|
12
|
+
return 2 * archipelago.get_nodes.length
|
13
|
+
end
|
14
|
+
get_biggest_shortest_paths(archipelago)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def is_connected(archipelago)
|
20
|
+
connected_measure = get_connected_measure
|
21
|
+
connected_classes_count = connected_measure.compute(archipelago)
|
22
|
+
connected_classes_count == 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_biggest_shortest_paths(archipelago)
|
26
|
+
paths_lengths = get_shortest_paths_measure.compute(archipelago)
|
27
|
+
paths_lengths.max
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_connected_measure
|
31
|
+
ConnectedMeasure.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_shortest_paths_measure
|
35
|
+
ShortestPathsMeasure.new
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'gimuby/genetic/archipelago/measure/measure'
|
2
|
+
|
3
|
+
# Return the list of shortest path (Dijkstra) in Archipelago
|
4
|
+
class ShortestPathsMeasure < Measure
|
5
|
+
|
6
|
+
def compute(archipelago)
|
7
|
+
nodes = archipelago.get_nodes
|
8
|
+
shortest_paths_length = []
|
9
|
+
nodes.each do |node1|
|
10
|
+
nodes.each do |node2|
|
11
|
+
if node2 > node1
|
12
|
+
length = get_shortest_path_length(archipelago, node1, node2)
|
13
|
+
unless length.nil?
|
14
|
+
shortest_paths_length.push(length)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
shortest_paths_length
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def get_shortest_path_length(archipelago, node1, node2)
|
25
|
+
next_nodes = [node1]
|
26
|
+
current_neighbor_distance = 0
|
27
|
+
nodes_distances = {node1 => current_neighbor_distance}
|
28
|
+
while next_nodes.length != 0
|
29
|
+
current_node = next_nodes.shift
|
30
|
+
current_neighbor_distance = nodes_distances[current_node] + 1
|
31
|
+
neighbors = archipelago.get_neighbors(current_node)
|
32
|
+
neighbors.each do |neighbor_node|
|
33
|
+
already_processed = nodes_distances.has_key?(neighbor_node)
|
34
|
+
unless already_processed
|
35
|
+
next_nodes.push neighbor_node
|
36
|
+
nodes_distances[neighbor_node] = current_neighbor_distance
|
37
|
+
end
|
38
|
+
end
|
39
|
+
if nodes_distances.has_key?(node2)
|
40
|
+
return nodes_distances[node2]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'gimuby/genetic/population/pick_strategy/pick_strategy'
|
2
|
+
|
3
|
+
class BestsPickStrategy < PickStrategy
|
4
|
+
|
5
|
+
def pick(population)
|
6
|
+
solutions = population.solutions
|
7
|
+
number = get_number_to_pick(population)
|
8
|
+
candidates = solutions.clone
|
9
|
+
|
10
|
+
candidates.sort! do |x, y|
|
11
|
+
(population.get_fitness x) <=> (population.get_fitness y)
|
12
|
+
end
|
13
|
+
|
14
|
+
candidates.slice(0, number)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
class PickStrategy
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@pick_proportion ||= 25.to_f / 100.to_f
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :pick_proportion
|
9
|
+
|
10
|
+
# pick some solution inside the population
|
11
|
+
def pick(population)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def get_number_to_pick(population)
|
18
|
+
number = population.solutions.length * @pick_proportion
|
19
|
+
number.round
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'gimuby/genetic/population/pick_strategy/pick_strategy'
|
2
|
+
|
3
|
+
class RandomWheelPickStrategy < PickStrategy
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@random_wheel_probability_reason = 0.7
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :random_wheel_probability_reason
|
11
|
+
|
12
|
+
def pick(population)
|
13
|
+
number = get_number_to_pick(population)
|
14
|
+
reason = @random_wheel_probability_reason
|
15
|
+
solutions = population.solutions
|
16
|
+
candidates = solutions.clone
|
17
|
+
candidates.sort! do |x, y|
|
18
|
+
(population.get_fitness x) <=> (population.get_fitness y)
|
19
|
+
end
|
20
|
+
picked = []
|
21
|
+
begin
|
22
|
+
# we use a geometric sequence
|
23
|
+
max_of_the_rand = (1 - reason ** candidates.length) / (1 - reason)
|
24
|
+
r = rand() * max_of_the_rand
|
25
|
+
element = 1
|
26
|
+
candidates.each do |solution|
|
27
|
+
max_accepted_value = (1 - reason ** element) / (1 - reason)
|
28
|
+
element += 1
|
29
|
+
if max_accepted_value > r
|
30
|
+
picked.push solution
|
31
|
+
ind = candidates.index solution
|
32
|
+
candidates.slice! ind
|
33
|
+
break
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end while picked.length < number
|
37
|
+
picked
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'gimuby/genetic/population/pick_strategy/pick_strategy'
|
2
|
+
|
3
|
+
class TournamentPickStrategy < PickStrategy
|
4
|
+
|
5
|
+
def pick(population)
|
6
|
+
solutions = population.solutions
|
7
|
+
number = get_number_to_pick(population)
|
8
|
+
candidates = solutions.clone
|
9
|
+
|
10
|
+
while candidates.length > number:
|
11
|
+
sol1 = candidates.choice()
|
12
|
+
sol2 = sol1
|
13
|
+
while sol1 == sol2:
|
14
|
+
sol2 = candidates.choice()
|
15
|
+
end
|
16
|
+
if population.get_fitness(sol1) < population.get_fitness(sol2)
|
17
|
+
candidates.delete(sol2)
|
18
|
+
else
|
19
|
+
candidates.delete(sol1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
candidates
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|