wallace 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +14 -0
- data/README.md +12 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/bin/.gitkeep +0 -0
- data/lib/analysers/fitness_distribution_analyser.rb +28 -0
- data/lib/analysers/init.rb +1 -0
- data/lib/core/analyser.rb +63 -0
- data/lib/core/breeder.rb +68 -0
- data/lib/core/breeding_graph.rb +59 -0
- data/lib/core/breeding_graph/init.rb +3 -0
- data/lib/core/breeding_graph/input_node.rb +55 -0
- data/lib/core/breeding_graph/node.rb +68 -0
- data/lib/core/breeding_graph/node_input.rb +6 -0
- data/lib/core/evaluator.rb +52 -0
- data/lib/core/evolver.rb +47 -0
- data/lib/core/exceptions.rb +6 -0
- data/lib/core/experiment.rb +22 -0
- data/lib/core/fitness.rb +8 -0
- data/lib/core/fraction.rb +7 -0
- data/lib/core/individual.rb +65 -0
- data/lib/core/logger.rb +5 -0
- data/lib/core/migrator.rb +6 -0
- data/lib/core/operator.rb +54 -0
- data/lib/core/population.rb +56 -0
- data/lib/core/selector.rb +43 -0
- data/lib/core/species.rb +52 -0
- data/lib/core/state.rb +29 -0
- data/lib/core/subpopulation.rb +53 -0
- data/lib/core/termination.rb +39 -0
- data/lib/distributions/gaussian_distribution.rb +60 -0
- data/lib/distributions/init.rb +3 -0
- data/lib/fitness/init.rb +1 -0
- data/lib/fitness/raw_fitness.rb +30 -0
- data/lib/loggers/csv_logger.rb +20 -0
- data/lib/loggers/init.rb +1 -0
- data/lib/loggers/mongo_logger.rb +5 -0
- data/lib/loggers/sqlite_logger.rb +5 -0
- data/lib/modules/ge/backus_naur_form.rb +125 -0
- data/lib/modules/ge/grammar_derivation.rb +30 -0
- data/lib/modules/ge/grammar_species.rb +29 -0
- data/lib/modules/ge/init.rb +5 -0
- data/lib/modules/init.rb +2 -0
- data/lib/modules/koza/builder.rb +48 -0
- data/lib/modules/koza/builder/full_builder.rb +92 -0
- data/lib/modules/koza/builder/grow_builder.rb +103 -0
- data/lib/modules/koza/builder/half_builder.rb +70 -0
- data/lib/modules/koza/builder/init.rb +3 -0
- data/lib/modules/koza/ephemeral.rb +33 -0
- data/lib/modules/koza/init.rb +12 -0
- data/lib/modules/koza/koza_node.rb +108 -0
- data/lib/modules/koza/koza_node_value.rb +72 -0
- data/lib/modules/koza/koza_node_value_set.rb +51 -0
- data/lib/modules/koza/koza_species.rb +48 -0
- data/lib/modules/koza/koza_tree.rb +159 -0
- data/lib/modules/koza/operators/init.rb +4 -0
- data/lib/modules/koza/operators/subtree_crossover_operation.rb +43 -0
- data/lib/modules/koza/operators/subtree_mutation_operation.rb +32 -0
- data/lib/operators/bit_flip_mutation_operation.rb +29 -0
- data/lib/operators/boundary_mutation_operation.rb +28 -0
- data/lib/operators/cycle_crossover_operation.rb +77 -0
- data/lib/operators/gaussian_mutation_operation.rb +39 -0
- data/lib/operators/half_uniform_crossover_operation.rb +24 -0
- data/lib/operators/init.rb +26 -0
- data/lib/operators/merging_crossover_operation.rb +27 -0
- data/lib/operators/one_point_crossover_operation.rb +29 -0
- data/lib/operators/order_crossover_operation.rb +38 -0
- data/lib/operators/partially_mapped_crossover_operation.rb +44 -0
- data/lib/operators/point_mutation_operation.rb +31 -0
- data/lib/operators/position_crossover_operation.rb +50 -0
- data/lib/operators/reverse_sequence_mutation_operation.rb +13 -0
- data/lib/operators/shuffle_mutation_operation.rb +17 -0
- data/lib/operators/splice_crossover_operation.rb +42 -0
- data/lib/operators/subtour_exchange_crossover_operation.rb +54 -0
- data/lib/operators/swap_mutation_operation.rb +29 -0
- data/lib/operators/three_parent_crossover_operation.rb +16 -0
- data/lib/operators/two_point_crossover_operation.rb +31 -0
- data/lib/operators/twors_mutation_operation.rb +18 -0
- data/lib/operators/uniform_crossover_operation.rb +30 -0
- data/lib/operators/uniform_mutation_operation.rb +31 -0
- data/lib/operators/variable_one_point_crossover_operation.rb +80 -0
- data/lib/patches/enumerable.rb +85 -0
- data/lib/patches/init.rb +5 -0
- data/lib/patches/range.rb +13 -0
- data/lib/selectors/init.rb +5 -0
- data/lib/selectors/random_selector.rb +14 -0
- data/lib/selectors/roulette_selector.rb +23 -0
- data/lib/selectors/tournament_selector.rb +36 -0
- data/lib/species/array_species.rb +40 -0
- data/lib/species/bit_string_species.rb +18 -0
- data/lib/species/init.rb +4 -0
- data/lib/species/permutation_species.rb +22 -0
- data/lib/species/string_species.rb +29 -0
- data/lib/utility/init.rb +4 -0
- data/lib/utility/scaled_array.rb +88 -0
- data/lib/utility/sorted_array.rb +39 -0
- data/lib/wallace.rb +40 -0
- data/test/.gitkeep +0 -0
- metadata +248 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# Used to hold terminal or non-terminal values.
|
2
|
+
class Wallace::Koza::NodeValueSet
|
3
|
+
|
4
|
+
# Constructs a new node value set.
|
5
|
+
#
|
6
|
+
# *Parameters:*
|
7
|
+
# * contents, the contents of the set.
|
8
|
+
def initialize(contents)
|
9
|
+
@contents = contents
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns an option at a given index from the set.
|
13
|
+
#
|
14
|
+
# *Parameters:*
|
15
|
+
# * index, the index of the option in the set.
|
16
|
+
def [](index)
|
17
|
+
@contents[index]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# Holds the set of terminal values.
|
23
|
+
class Wallace::Koza::TerminalSet < Wallace::Koza::NodeValueSet
|
24
|
+
|
25
|
+
# Samples an option from this set.
|
26
|
+
#
|
27
|
+
# *Parameters:*
|
28
|
+
# * opt, a hash of keyword options for this method.
|
29
|
+
# -> random, the RNG to use to sample an option.
|
30
|
+
def sample(opts = {})
|
31
|
+
opts[:random] ||= Random.new
|
32
|
+
value = @contents.sample(random: opts[:random])
|
33
|
+
value = value.sample(random: opts[:random]) if value.is_a? Wallace::Koza::Ephemeral
|
34
|
+
return value
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# Holds the set of non-terminal values.
|
40
|
+
class Wallace::Koza::NonTerminalSet < Wallace::Koza::NodeValueSet
|
41
|
+
|
42
|
+
# Samples an option from this set.
|
43
|
+
#
|
44
|
+
# *Parameters:*
|
45
|
+
# * opt, a hash of keyword options for this method.
|
46
|
+
# -> random, the RNG to use to sample an option.
|
47
|
+
def sample(opts = {})
|
48
|
+
@contents.sample(random: opts[:random] || Random.new)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Wallace::Koza::KozaSpecies < Wallace::Species
|
2
|
+
|
3
|
+
name = :koza_tree
|
4
|
+
|
5
|
+
attr_reader :terminals,
|
6
|
+
:non_terminals,
|
7
|
+
:builder,
|
8
|
+
:depth_limits
|
9
|
+
|
10
|
+
# Constructs a new koza tree-based species.
|
11
|
+
#
|
12
|
+
# *Parameters:*
|
13
|
+
# * opts, hash of keyword options used by this method.
|
14
|
+
# -> id, the unique identifier for this species.
|
15
|
+
# -> terminals, the set of terminal nodes.
|
16
|
+
# -> non_terminals, the set of non-terminal nodes.
|
17
|
+
# -> builder, the build method used to generate new trees and sub-trees.
|
18
|
+
# -> depth_limits, the range of depths trees in this species may have.
|
19
|
+
def initialize(opts = {})
|
20
|
+
super(opts)
|
21
|
+
@terminals = Wallace::Koza::TerminalSet.new(opts[:terminals])
|
22
|
+
@non_terminals = Wallace::Koza::NonTerminalSet.new(opts[:non_terminals])
|
23
|
+
@builder = opts[:builder] || Wallace::Koza::Builder::HalfBuilder.new
|
24
|
+
@depth_limits = opts[:depth_limits] || (0..6)
|
25
|
+
@builder.prepare(@terminals, @non_terminals, @depth_limits)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks whether a given tree meets the depth constraints of the species.
|
29
|
+
def valid_tree?(tree)
|
30
|
+
@depth_limits.cover?(tree.length)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check that a given individual meets the depth constraints of the species.
|
34
|
+
def valid?(individual)
|
35
|
+
return false unless super(individual)
|
36
|
+
return false unless valid_tree?(individual.data)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates a new koza tree based individual using the constraints of the species.
|
40
|
+
#
|
41
|
+
# *Parameters:*
|
42
|
+
# * opts, hash of keyword options used by this method.
|
43
|
+
# -> random, the RNG to use when spawning a new individual.
|
44
|
+
def spawn(opts = {})
|
45
|
+
Wallace::Individual.new(self, @builder.build_tree(random: opts[:random] || Random.new))
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# This class is used to represent Koza Trees, which in turn represent computer programs using
|
2
|
+
# Lisp-style S-expressions (program trees).
|
3
|
+
#
|
4
|
+
# * remove(n)
|
5
|
+
class Wallace::Koza::KozaTree
|
6
|
+
|
7
|
+
attr_reader :root,
|
8
|
+
:depth,
|
9
|
+
:nodes
|
10
|
+
|
11
|
+
# Constructs a new Koza Tree.
|
12
|
+
#
|
13
|
+
# *Parameters:*
|
14
|
+
# * root, the root node of this tree.
|
15
|
+
def initialize(root)
|
16
|
+
@root = root
|
17
|
+
calculate!
|
18
|
+
end
|
19
|
+
|
20
|
+
# Samples a node (or number of nodes) from the tree at random.
|
21
|
+
#
|
22
|
+
# *Parameters:*
|
23
|
+
# * n, the number of nodes to sample from the tree.
|
24
|
+
# * opts, a hash of keyword options for this method.
|
25
|
+
# -> size, the number of nodes to sample.
|
26
|
+
# -> random, the RNG to use for sampling.
|
27
|
+
def sample(opts = {})
|
28
|
+
@nodes.sample(opts[:size], random: opts[:random] || Random.new) unless opts[:size].nil?
|
29
|
+
@nodes.sample(random: opts[:random] || Random.new)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns a node at a given index in the tree.
|
33
|
+
def [](index)
|
34
|
+
@nodes[index]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the number of nodes in the tree.
|
38
|
+
def size
|
39
|
+
@nodes.length
|
40
|
+
end
|
41
|
+
alias :length :size
|
42
|
+
|
43
|
+
# Changes the root of the tree.
|
44
|
+
#
|
45
|
+
# *Parameters:*
|
46
|
+
# * node, the new root node for the tree.
|
47
|
+
def root=(node)
|
48
|
+
@root = node
|
49
|
+
calculate!
|
50
|
+
end
|
51
|
+
|
52
|
+
# (Re)calculates the list of nodes in the tree
|
53
|
+
# and their respective depths.
|
54
|
+
def calculate!
|
55
|
+
queue = [@root]
|
56
|
+
@nodes = [@root]
|
57
|
+
@root.depth = @depth = 0
|
58
|
+
|
59
|
+
until queue.empty?
|
60
|
+
n = queue.shift
|
61
|
+
@depth = [@depth, n.depth].max
|
62
|
+
n.children.each { |c| c.depth = n.depth + 1 }
|
63
|
+
queue += n.children
|
64
|
+
@nodes << n
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Creates a clone of this tree (along with its nodes).
|
69
|
+
def clone
|
70
|
+
Wallace::Koza::KozaTree.new(@root.clone)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Adds a node to the tree as the child of another node.
|
74
|
+
#
|
75
|
+
# *Parameters:*
|
76
|
+
# * parent, The node to use as the parent of the new node.
|
77
|
+
# * child, The node to add to the tree.
|
78
|
+
def add(parent, child)
|
79
|
+
|
80
|
+
# Could throw an exception if there is no room for the child.
|
81
|
+
parent.children << child
|
82
|
+
child.parent = parent
|
83
|
+
child.depth = parent.depth + 1
|
84
|
+
|
85
|
+
@nodes << child
|
86
|
+
@depth = child.depth if child.depth > @depth
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
# Replaces a subtree in the tree with another subtree.
|
91
|
+
#
|
92
|
+
# *Parameters:*
|
93
|
+
# * n1, the root node of the sub-tree being replaced.
|
94
|
+
# * n2, the root node of the replacement sub-tree.
|
95
|
+
def replace(n1, n2)
|
96
|
+
|
97
|
+
# Create a clone of n2 (to avoid affecting another tree).
|
98
|
+
n2 = n2.clone
|
99
|
+
|
100
|
+
# If n1 is the root of the tree then change the root to n2.
|
101
|
+
if @root == n1
|
102
|
+
@root = n2
|
103
|
+
n2.parent = nil
|
104
|
+
|
105
|
+
# Otherwise replace n1 with n2 in n1's parents list of children.
|
106
|
+
else
|
107
|
+
i = n1.parent.children.index(n1)
|
108
|
+
n1.parent.children[i] = n2
|
109
|
+
n2.parent = n1.parent
|
110
|
+
n1.parent = nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# Recalculate the node list and tree depth.
|
114
|
+
calculate!
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
# Evaluates the S-expression given by this tree.
|
119
|
+
#
|
120
|
+
# *Parameters:*
|
121
|
+
# * args, the arguments to the function / program (as a hash).
|
122
|
+
def eval(args)
|
123
|
+
@root.eval(args)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Converts this tree into a Ruby program string.
|
127
|
+
def to_ruby
|
128
|
+
@root.to_ruby
|
129
|
+
end
|
130
|
+
alias :to_s :to_ruby
|
131
|
+
|
132
|
+
# Compiles a given Koza Tree into a Ruby lambda function.
|
133
|
+
#
|
134
|
+
# *Parameters:*
|
135
|
+
# * args, an array of the arguments to the function.
|
136
|
+
# * tree, the tree to compile to a lambda function.
|
137
|
+
#
|
138
|
+
# *Returns:*
|
139
|
+
# the compiled lambda function.
|
140
|
+
#
|
141
|
+
# *NOTE:*
|
142
|
+
# Trying to call 'eval' in an object method causes huge problems!
|
143
|
+
# Totally different semantics.
|
144
|
+
def self.to_lambda(args, tree)
|
145
|
+
eval("lambda { |#{args.join(',')}| #{tree.to_ruby} }")
|
146
|
+
end
|
147
|
+
|
148
|
+
# Compiles this Koza Tree into a Ruby lambda function.
|
149
|
+
#
|
150
|
+
# *Parameters:*
|
151
|
+
# * args, an array of the arguments to the function.
|
152
|
+
#
|
153
|
+
# *Returns:*
|
154
|
+
# the compiled lambda function.
|
155
|
+
def to_lambda(args)
|
156
|
+
self.class.to_lambda(args, self)
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# The subtree crossover operator takes two parent koza trees, selects a sub-tree from each
|
2
|
+
# of them at random and swaps them to give two offspring.
|
3
|
+
class Wallace::Koza::Operators::SubtreeCrossoverOperator < Wallace::Operator
|
4
|
+
|
5
|
+
name = :subtree_crossover
|
6
|
+
|
7
|
+
# Constructs a new subtree crossover operator.
|
8
|
+
#
|
9
|
+
# *Parameters:*
|
10
|
+
# * id, the unique identifier for this operator.
|
11
|
+
# * inputs, an array of inputs (OperatorInput) to this operator.
|
12
|
+
# * max_tries, the maximum number of tries at recombining individuals before the inputs are returned.
|
13
|
+
def initialize(id, inputs, max_tries)
|
14
|
+
super(id, inputs)
|
15
|
+
@max_tries = max_tries
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
def produce(rng, individuals)
|
20
|
+
|
21
|
+
species = individuals[0].species
|
22
|
+
tries = 0
|
23
|
+
tx, ty = nil
|
24
|
+
loop do
|
25
|
+
tx = individuals[0].data.clone
|
26
|
+
ty = individuals[1].data.clone
|
27
|
+
nx = tx.sample(random: rng)
|
28
|
+
ny = ty.sample(random: rng)
|
29
|
+
tx.replace(nx, ny)
|
30
|
+
ty.replace(ny, nx)
|
31
|
+
tries += 1
|
32
|
+
break if (species.valid_tree?(tx) and species.valid_tree?(ty)) or tries == @max_tries
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return the recombined individuals so long as they are legal.
|
36
|
+
# If they are not legal then return the input individuals.
|
37
|
+
individuals[0].data = tx if species.valid_tree?(tx)
|
38
|
+
individuals[1].data = ty if species.valid_tree?(ty)
|
39
|
+
return individuals
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# The subtree mutation operation selects a random sub-tree within a given tree
|
2
|
+
# and replaces it with a valid sub-tree constructed using the builder attached to the
|
3
|
+
# individual's species.
|
4
|
+
class Wallace::Koza::Operators::SubtreeMutationOperator < Wallace::Operator
|
5
|
+
|
6
|
+
name = :subtree_mutation
|
7
|
+
|
8
|
+
# Idea: We could add a 'prepare' method to this operator so that
|
9
|
+
# it can cache the tree builder.
|
10
|
+
|
11
|
+
protected
|
12
|
+
def produce(rng, individuals)
|
13
|
+
|
14
|
+
# Select a random sub-tree from the individual.
|
15
|
+
node = individuals[0].data.sample(random: rng)
|
16
|
+
|
17
|
+
# Determine the depth of the replacement sub-tree.
|
18
|
+
depth = individuals[0].species.depth_limits
|
19
|
+
depth = ([0, depth.min - node.depth].max .. (depth.max - node.depth))
|
20
|
+
depth = depth.sample(random: rng)
|
21
|
+
|
22
|
+
# Generate the replacement and swap it with the original.
|
23
|
+
replacement = individuals[0].species.builder.build_subtree(
|
24
|
+
depth,
|
25
|
+
random: rng
|
26
|
+
)
|
27
|
+
individuals[0].data.replace(node, replacement)
|
28
|
+
return individuals
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Wallace::Operators::BitFlipMutationOperator < Wallace::Operator
|
2
|
+
|
3
|
+
name = :bit_flip_mutation
|
4
|
+
|
5
|
+
# Allow the mutation probability to be dynamically adjusted.
|
6
|
+
attr_accessor :probability
|
7
|
+
|
8
|
+
# Constructs a new bit flip mutation operator.
|
9
|
+
#
|
10
|
+
# *Parameters:*
|
11
|
+
# * opts, a hash of keyword options for this method.
|
12
|
+
# -> id, the unique identifier for this operator.
|
13
|
+
# -> inputs, an array of inputs (OperatorInput) to this operator.
|
14
|
+
# -> probability, the probability that a given bit should be mutated. (default = 0.01).
|
15
|
+
def initialize(opts = {})
|
16
|
+
super(opts)
|
17
|
+
@probability = opts[:probability] || 0.01
|
18
|
+
end
|
19
|
+
|
20
|
+
def operate(rng, inputs)
|
21
|
+
(0...inputs[0].length).each do |i|
|
22
|
+
if rng.rand <= @probability
|
23
|
+
inputs[0][i] = inputs[0][i] == 0 ? 1 : 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
return inputs
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Wallace::Operators::BoundaryMutationOperator < Wallace::Operator
|
2
|
+
|
3
|
+
name = :boundary_mutation
|
4
|
+
|
5
|
+
# Constructs a new boundary mutation operator.
|
6
|
+
#
|
7
|
+
# *Parameters:*
|
8
|
+
# * opts, a hash of keyword options for this method.
|
9
|
+
# -> id, the unique identifier for this operator.
|
10
|
+
# -> inputs, an array of inputs (OperatorInput) to this operator.
|
11
|
+
# -> values, the collection of values that each gene can take.
|
12
|
+
def initialize(opts = {})
|
13
|
+
super(opts)
|
14
|
+
@values = opts[:values] # Extract this from the species?
|
15
|
+
end
|
16
|
+
|
17
|
+
def operate(rng, inputs)
|
18
|
+
# x = inputs[0]
|
19
|
+
|
20
|
+
# Select a gene to mutate.
|
21
|
+
#index = rng.rand(x.data.length)
|
22
|
+
|
23
|
+
#x.data[rng.rand(x.data.length)] =
|
24
|
+
|
25
|
+
return inputs
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# *Credit:*
|
2
|
+
# Oliver, I. M., Smith, D. J., & Holland, J. R. C. (1987). A
|
3
|
+
# study of permutation crossover operators on the traveling
|
4
|
+
# salesman problem. In Proceedings of the second
|
5
|
+
# international conference. on genetic algorithms (ICGA’87)
|
6
|
+
# (pp. 224–230). Cambridge, MA:Massachusetts Institute of
|
7
|
+
# Technology
|
8
|
+
class Wallace::Operators::CycleCrossoverOperator < Wallace::Operator
|
9
|
+
|
10
|
+
name = :cycle_crossover
|
11
|
+
|
12
|
+
HashPair = Struct.new(:v1, :v2, :i2)
|
13
|
+
|
14
|
+
def operate(rng, parents)
|
15
|
+
|
16
|
+
# http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx
|
17
|
+
length = parents[0].length
|
18
|
+
children = [
|
19
|
+
Array.new(length) { nil },
|
20
|
+
Array.new(length) { nil }
|
21
|
+
]
|
22
|
+
|
23
|
+
# Generate a hash to indicate when certain indices have been
|
24
|
+
# used in a cycle.
|
25
|
+
used = Hash[(0...length).map { |v| [v, false] }]
|
26
|
+
|
27
|
+
# REVERSE ENGINEER.
|
28
|
+
helper = Hash[(0...length).map { |v|
|
29
|
+
temp_pair = HashPair.new(
|
30
|
+
parents[0][v],
|
31
|
+
parents[1][v],
|
32
|
+
v
|
33
|
+
)
|
34
|
+
[parents[1][v], temp_pair]
|
35
|
+
}]
|
36
|
+
|
37
|
+
cycles = []
|
38
|
+
(0...length).each do |j|
|
39
|
+
cyc = []
|
40
|
+
unless used[j] then
|
41
|
+
|
42
|
+
cyc_start = j
|
43
|
+
|
44
|
+
temp_pair = helper[parents[0][cyc_start]]
|
45
|
+
cyc << temp_pair
|
46
|
+
used[temp_pair.i2] = true
|
47
|
+
|
48
|
+
until temp_pair.i2 == cyc_start
|
49
|
+
temp_pair = helper[parents[0][temp_pair.i2]]
|
50
|
+
cyc << temp_pair
|
51
|
+
used[temp_pair.i2] = true
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
cycles << cyc
|
56
|
+
end
|
57
|
+
|
58
|
+
# Alternating cycles.
|
59
|
+
cycles.each_with_index do |cyc, num|
|
60
|
+
if (num % 2) == 0
|
61
|
+
cyc.each do |temp_pair|
|
62
|
+
children[0][temp_pair.i2] = temp_pair.v1
|
63
|
+
children[1][temp_pair.i2] = temp_pair.v2
|
64
|
+
end
|
65
|
+
else
|
66
|
+
cyc.each do |temp_pair|
|
67
|
+
children[0][temp_pair.i2] = temp_pair.v2
|
68
|
+
children[1][temp_pair.i2] = temp_pair.v1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
return children
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|