wallace 0.0.0
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 +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
|