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.
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