wallace 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/Gemfile +14 -0
  2. data/README.md +12 -0
  3. data/Rakefile +52 -0
  4. data/VERSION +1 -0
  5. data/bin/.gitkeep +0 -0
  6. data/lib/analysers/fitness_distribution_analyser.rb +28 -0
  7. data/lib/analysers/init.rb +1 -0
  8. data/lib/core/analyser.rb +63 -0
  9. data/lib/core/breeder.rb +68 -0
  10. data/lib/core/breeding_graph.rb +59 -0
  11. data/lib/core/breeding_graph/init.rb +3 -0
  12. data/lib/core/breeding_graph/input_node.rb +55 -0
  13. data/lib/core/breeding_graph/node.rb +68 -0
  14. data/lib/core/breeding_graph/node_input.rb +6 -0
  15. data/lib/core/evaluator.rb +52 -0
  16. data/lib/core/evolver.rb +47 -0
  17. data/lib/core/exceptions.rb +6 -0
  18. data/lib/core/experiment.rb +22 -0
  19. data/lib/core/fitness.rb +8 -0
  20. data/lib/core/fraction.rb +7 -0
  21. data/lib/core/individual.rb +65 -0
  22. data/lib/core/logger.rb +5 -0
  23. data/lib/core/migrator.rb +6 -0
  24. data/lib/core/operator.rb +54 -0
  25. data/lib/core/population.rb +56 -0
  26. data/lib/core/selector.rb +43 -0
  27. data/lib/core/species.rb +52 -0
  28. data/lib/core/state.rb +29 -0
  29. data/lib/core/subpopulation.rb +53 -0
  30. data/lib/core/termination.rb +39 -0
  31. data/lib/distributions/gaussian_distribution.rb +60 -0
  32. data/lib/distributions/init.rb +3 -0
  33. data/lib/fitness/init.rb +1 -0
  34. data/lib/fitness/raw_fitness.rb +30 -0
  35. data/lib/loggers/csv_logger.rb +20 -0
  36. data/lib/loggers/init.rb +1 -0
  37. data/lib/loggers/mongo_logger.rb +5 -0
  38. data/lib/loggers/sqlite_logger.rb +5 -0
  39. data/lib/modules/ge/backus_naur_form.rb +125 -0
  40. data/lib/modules/ge/grammar_derivation.rb +30 -0
  41. data/lib/modules/ge/grammar_species.rb +29 -0
  42. data/lib/modules/ge/init.rb +5 -0
  43. data/lib/modules/init.rb +2 -0
  44. data/lib/modules/koza/builder.rb +48 -0
  45. data/lib/modules/koza/builder/full_builder.rb +92 -0
  46. data/lib/modules/koza/builder/grow_builder.rb +103 -0
  47. data/lib/modules/koza/builder/half_builder.rb +70 -0
  48. data/lib/modules/koza/builder/init.rb +3 -0
  49. data/lib/modules/koza/ephemeral.rb +33 -0
  50. data/lib/modules/koza/init.rb +12 -0
  51. data/lib/modules/koza/koza_node.rb +108 -0
  52. data/lib/modules/koza/koza_node_value.rb +72 -0
  53. data/lib/modules/koza/koza_node_value_set.rb +51 -0
  54. data/lib/modules/koza/koza_species.rb +48 -0
  55. data/lib/modules/koza/koza_tree.rb +159 -0
  56. data/lib/modules/koza/operators/init.rb +4 -0
  57. data/lib/modules/koza/operators/subtree_crossover_operation.rb +43 -0
  58. data/lib/modules/koza/operators/subtree_mutation_operation.rb +32 -0
  59. data/lib/operators/bit_flip_mutation_operation.rb +29 -0
  60. data/lib/operators/boundary_mutation_operation.rb +28 -0
  61. data/lib/operators/cycle_crossover_operation.rb +77 -0
  62. data/lib/operators/gaussian_mutation_operation.rb +39 -0
  63. data/lib/operators/half_uniform_crossover_operation.rb +24 -0
  64. data/lib/operators/init.rb +26 -0
  65. data/lib/operators/merging_crossover_operation.rb +27 -0
  66. data/lib/operators/one_point_crossover_operation.rb +29 -0
  67. data/lib/operators/order_crossover_operation.rb +38 -0
  68. data/lib/operators/partially_mapped_crossover_operation.rb +44 -0
  69. data/lib/operators/point_mutation_operation.rb +31 -0
  70. data/lib/operators/position_crossover_operation.rb +50 -0
  71. data/lib/operators/reverse_sequence_mutation_operation.rb +13 -0
  72. data/lib/operators/shuffle_mutation_operation.rb +17 -0
  73. data/lib/operators/splice_crossover_operation.rb +42 -0
  74. data/lib/operators/subtour_exchange_crossover_operation.rb +54 -0
  75. data/lib/operators/swap_mutation_operation.rb +29 -0
  76. data/lib/operators/three_parent_crossover_operation.rb +16 -0
  77. data/lib/operators/two_point_crossover_operation.rb +31 -0
  78. data/lib/operators/twors_mutation_operation.rb +18 -0
  79. data/lib/operators/uniform_crossover_operation.rb +30 -0
  80. data/lib/operators/uniform_mutation_operation.rb +31 -0
  81. data/lib/operators/variable_one_point_crossover_operation.rb +80 -0
  82. data/lib/patches/enumerable.rb +85 -0
  83. data/lib/patches/init.rb +5 -0
  84. data/lib/patches/range.rb +13 -0
  85. data/lib/selectors/init.rb +5 -0
  86. data/lib/selectors/random_selector.rb +14 -0
  87. data/lib/selectors/roulette_selector.rb +23 -0
  88. data/lib/selectors/tournament_selector.rb +36 -0
  89. data/lib/species/array_species.rb +40 -0
  90. data/lib/species/bit_string_species.rb +18 -0
  91. data/lib/species/init.rb +4 -0
  92. data/lib/species/permutation_species.rb +22 -0
  93. data/lib/species/string_species.rb +29 -0
  94. data/lib/utility/init.rb +4 -0
  95. data/lib/utility/scaled_array.rb +88 -0
  96. data/lib/utility/sorted_array.rb +39 -0
  97. data/lib/wallace.rb +40 -0
  98. data/test/.gitkeep +0 -0
  99. 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,4 @@
1
+ module Wallace::Koza::Operators; end
2
+
3
+ require_relative 'subtree_crossover_operation.rb'
4
+ require_relative 'subtree_mutation_operation.rb'
@@ -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