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,53 @@
1
+ # This class is used to hold sub-populations of individuals. For at least
2
+ # some length of time, subpopulations evolve independently, only interacting
3
+ # with the rest of the population (other subpopulations) during migration.
4
+ class Wallace::Subpopulation
5
+
6
+ attr_accessor :contents
7
+ attr_reader :capacity,
8
+ :species
9
+
10
+ # Constructs a new subpopulation.
11
+ #
12
+ # *Arguments*
13
+ # * opts, a hash of keyword options for this method.
14
+ # -> size, the size of this subpopulation.
15
+ # -> species, the species of individuals within this sub-population.
16
+ def initialize(opts = {})
17
+ @capacity = opts[:size]
18
+ @contents = []
19
+ @species = opts[:species]
20
+ end
21
+
22
+ # Initialises a fresh sub-population of individuals.
23
+ #
24
+ # *Parameters:*
25
+ # * rng, the random number generator to use.
26
+ def fresh!(rng)
27
+ @contents = Array.new(@capacity) { @species.spawn(random: rng) }
28
+ end
29
+
30
+ # Clears the contents of this sub-population.
31
+ def clear!
32
+ @contents = []
33
+ end
34
+
35
+ # Returns the best individual within this sub-population.
36
+ def best
37
+ @contents.min
38
+ end
39
+ alias :min :best
40
+
41
+ # Returns the worst individual within this sub-population.
42
+ def worst
43
+ @contents.max
44
+ end
45
+ alias :max :worst
46
+
47
+ # Returns the actual number of individuals within this population.
48
+ def size
49
+ @contents.size
50
+ end
51
+ alias_method :length, :size
52
+
53
+ end
@@ -0,0 +1,39 @@
1
+ # This class is used to store and check against the termination criteria of the evolutionary
2
+ # process.
3
+ class Wallace::Termination
4
+
5
+ attr_reader :generation_limit,
6
+ :stagnation_limit,
7
+ :fitness_limit,
8
+ :error
9
+
10
+ # Constructs a new termination criteria.
11
+ #
12
+ # *Parameters:*
13
+ # * opts, a hash of keyword options for this method.
14
+ # -> generations, limit on the total number of generations to perform.
15
+ # -> stagnation, limit on the successive number of generations without change to the best solution.
16
+ # -> fitness, target fitness value.
17
+ # -> error, the maximum allowed error in the target fitness value.
18
+ def initialize(opts = {})
19
+ @generation_limit = opts[:generations] || 200
20
+ @stagnation_limit = opts[:stagnation]
21
+ @fitness_limit = opts[:fitness]
22
+ @error = opts[:error] || 0
23
+ end
24
+
25
+ # Determines whether the evolutionary process has reached its termination
26
+ # criteria or not.
27
+ #
28
+ # *Parameters:*
29
+ # * state, the current state of the evolution.
30
+ #
31
+ # Returns true if it has finished and false if otherwise.
32
+ def finished?(state)
33
+ return true if not @generation_limit.nil? and state.generations == @generation_limit
34
+ return true if not @fitness_limit.nil? and state.best.fitness.value <= @fitness_limit + @error
35
+ return true if not @stagnation_limit.nil? and state.stagnation == @stagnation_limit
36
+ return false
37
+ end
38
+
39
+ end
@@ -0,0 +1,60 @@
1
+ # Used to represent and sample values from a given Gaussian distribution.
2
+ #
3
+ # Uses the popular Box-Muller transformation to generate two values,
4
+ # one of which is returned, the other which is cached (and returned
5
+ # upon the next request).
6
+ #
7
+ # Since the Box-Muller transform samples two values from the distribution,
8
+ # rather than discarding the extra value, we cache it and return it
9
+ # upon the next request.
10
+ #
11
+ # *Credit to:*
12
+ # http://stackoverflow.com/questions/5825680/code-to-generate-gaussian-normally-distributed-random-numbers-in-ruby
13
+ class Wallace::Distributions::GaussianDistribution
14
+
15
+ # Constructs a new Gaussian distribution.
16
+ #
17
+ # *Parameters:*
18
+ # * mean, the mean of this distribution.
19
+ # * std_dev, the standard deviation of this distribution.
20
+ def initialize(mean, std_dev)
21
+ @mean = mean
22
+ @std_dev = std_dev
23
+ @cached = nil
24
+ end
25
+
26
+ # Samples a value from this distribution.
27
+ #
28
+ # Uses the popular Box-Muller transformation to generate two values,
29
+ # one of which is returned, the other which is cached (and returned
30
+ # upon the next request).
31
+ #
32
+ # *Parameters:*
33
+ # * opts, keyword arguments for this method.
34
+ # * -> random, the random number generation to use to seed the selection process.
35
+ def sample(opts = {})
36
+
37
+ # Create a RNG if one isn't already provided.
38
+ random = opts[:random] || Random.new
39
+
40
+ # Return a cached value if there is one.
41
+ # We could collapse these 4 lines into a much nicer:
42
+ # return @values.pop if @values.empty?
43
+ # However .empty? is much more expensive!
44
+ unless @cached.nil?
45
+ value = @cached
46
+ @cached = nil
47
+ return value
48
+ end
49
+
50
+ # Otherwise calculate two new values, caching one and
51
+ # returning the other.
52
+ theta = 2 * Math::PI * random.rand
53
+ rho = Math.sqrt(-2 * Math.log(1 - random.rand))
54
+ scale = @std_dev * rho
55
+ @cached = @mean + scale * Math.cos(theta)
56
+ return @mean + scale * Math.sin(theta)
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,3 @@
1
+ module Wallace::Distributions; end
2
+
3
+ require_relative 'gaussian_distribution.rb'
@@ -0,0 +1 @@
1
+ require_relative 'raw_fitness.rb'
@@ -0,0 +1,30 @@
1
+ # The raw fitness model uses a single numeric type to describe the fitness of a given individual.
2
+ # Ordering of fitnesses, and thus of individuals, is achieved via ordering on these simple numeric
3
+ # types.
4
+ class Wallace::Fitness::RawFitness < Wallace::Fitness
5
+
6
+ # Allow the fitness value to be read but not changed.
7
+ attr_reader :value
8
+
9
+ # Constructs a new raw fitness.
10
+ #
11
+ # *Parameters:*
12
+ # * value, the raw fitness value.
13
+ def initialize(value)
14
+ @value = value
15
+ end
16
+
17
+ # Compares this raw fitness to another.
18
+ #
19
+ # *Parameters:*
20
+ # * other, the fitness to compare against.
21
+ #
22
+ # *Warning:*
23
+ # It is assumed that a RawFitness object is provided for comparison.
24
+ def <=>(other)
25
+ return -1 if other.value.is_a? Float and other.value.nan?
26
+ return 1 if @value.is_a? Float and @value.nan?
27
+ return @value <=> other.value
28
+ end
29
+
30
+ end
@@ -0,0 +1,20 @@
1
+ # The CSV logger records information in a
2
+ class Wallace::Loggers::CsvLogger < Wallace::Logger
3
+
4
+ # Constructs a new CSV logger.
5
+ #
6
+ # *Parameters:*
7
+ # * file, the CSV file to log to.
8
+ # * opts, a hash of keyword options for this logger.
9
+ # -> columns, an ordered list of columns in this CSV.
10
+ # -> headers, an optional hash of headers for the columns in the CSV file.
11
+ def initialize(file)
12
+
13
+ end
14
+
15
+
16
+ def write(data)
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1 @@
1
+ module Wallace::Loggers; end
@@ -0,0 +1,5 @@
1
+ class Wallace::Loggers::MongoLogger < Wallace::Logger
2
+
3
+
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ class Wallace::Loggers::SQLiteLogger < Wallace::Logger
2
+
3
+
4
+
5
+ end
@@ -0,0 +1,125 @@
1
+ # Used to hold token values.
2
+ Wallace::GE::Literal = Struct.new(:value)
3
+ Wallace::GE::Symbol = Struct.new(:value)
4
+
5
+ # This class holds the details of a BNF grammar and provides methods for producing
6
+ # a derivation from a given sequence of integers.
7
+ #
8
+ # Rather than having a special class for grammar sequences, we simply use arrays to
9
+ # store grammar sequences, ensuring the best possible compatibility with existing
10
+ # array-based operators.
11
+ class Wallace::GE::BackusNaurForm
12
+
13
+ # The root symbol for this grammar (where all derivations begin).
14
+ attr_reader :root
15
+
16
+ # Constructs a new BNF Grammar.
17
+ #
18
+ # *Parameters:*
19
+ # * root, the root symbol in the grammar (at which all derivations start).
20
+ # * rules, the rules of this grammar, given as a hash, indexed by symbol.
21
+ def initialize(root, rules)
22
+
23
+ # Convert each rule into a sequence of symbol derivations.
24
+ # Transform each string-based derivation into a sequence of tokens.
25
+ @rules = Hash[rules.map { |rule, derivations|
26
+ derivations.map! { |derivation|
27
+ tokens = []
28
+ until derivation.empty?
29
+ left, tag, right = derivation.partition(/{\w+}/)
30
+ tokens << Wallace::GE::Literal.new(left) unless left.empty?
31
+ tokens << Wallace::GE::Symbol.new(tag[1...tag.length-1]) unless tag.empty?
32
+ derivation = right
33
+ end
34
+ tokens
35
+ }
36
+ [rule.to_s, derivations]
37
+ }].freeze
38
+
39
+ # Could ensure that the root is a valid symbol.
40
+ @root = root.to_s.freeze
41
+
42
+ end
43
+
44
+ # Returns the number of symbols in the BNF.
45
+ def size
46
+ @rules.size
47
+ end
48
+ alias :length :size
49
+
50
+ # Returns the list of possible derivations for a given symbol.
51
+ def [](symbol)
52
+ @rules[symbol]
53
+ end
54
+ alias :derivations :[]
55
+
56
+ # Produces a grammar derivation from a given sequence of integers.
57
+ #
58
+ # *Parameters:*
59
+ # * sequence, the sequence of integers to produce a derivation from.
60
+ # * opts, a hash of keyword options for this method.
61
+ # -> random, the RNG to use during the derivation process.
62
+ # -> wrap, flag indicating whether the sequence should be wrapped when
63
+ # all genetic material has been used.
64
+ # -> max_wraps, the maximum number of times the sequence should be
65
+ # wrapped.
66
+ #
67
+ # *Returns:*
68
+ # A grammar derivation object for the produced derivation.
69
+ def derive(sequence, opts = {})
70
+
71
+ # For now wrapping is forced.
72
+ opts[:wrap] = true
73
+ opts[:max_wraps] ||= 1 if opts[:wrap]
74
+
75
+ derivation = Wallace::GE::GrammarDerivation.new
76
+
77
+ # Keep processing the sequence of tokens until none remain.
78
+ #
79
+ # * Literal tokens are appended to the end of the derivation.
80
+ # * Symbol tokens are converted into a given symbol derivation,
81
+ # using the next codon as the index if there is more than a single
82
+ # choice.
83
+ # * When there are no codons left to consume then we either:
84
+ # a) Add a new codon to the sequence (until the limit is reached). [DISABLED]
85
+ # b) Reset the codon index to zero, wrapping the sequence round.
86
+ queue = [Wallace::GE::Symbol.new(@root)]
87
+ codon_index = 0
88
+ sequence_length = sequence.length
89
+ num_wraps = 0
90
+
91
+ until queue.empty?
92
+ token = queue.shift
93
+ if token.is_a? Wallace::GE::Literal
94
+ derivation << token.value
95
+ else
96
+ options = @rules[token.value]
97
+ if options.length == 1
98
+ queue += options[0]
99
+ else
100
+
101
+ # Check if there are no remaining codons in the sequence.
102
+ if (codon_index >= sequence_length)
103
+
104
+ # If wrapping is enabled, reset the codon pointer to the
105
+ # start of the sequence, unless the maximum number of wraps
106
+ # has been encountered.
107
+ if opts[:wrap]
108
+ raise Wallace::Errors::GenotypeExhaustedError if num_wraps != opts[:max_wraps] # throw error?
109
+ codon_index = 0
110
+ num_wraps += 1
111
+ end
112
+
113
+ end
114
+
115
+ queue = options[sequence[codon_index] % options.length] + queue
116
+ codon_index += 1
117
+ end
118
+ end
119
+ end
120
+
121
+ return derivation.freeze
122
+
123
+ end
124
+
125
+ end
@@ -0,0 +1,30 @@
1
+ # The GrammarDerivation class is used to hold a derivation of a grammar produced by some provided
2
+ # sequence of integers. This class implements methods for transforming the derivation into other
3
+ # forms.
4
+ class Wallace::GE::GrammarDerivation < String
5
+
6
+ # Produces a lambda function from a given grammar derivation and a set
7
+ # of arguments.
8
+ #
9
+ # *Parameters:*
10
+ # * args, an ordered array of the arguments to the lambda function.
11
+ # * derivation, the grammar derivation to use as the body of the function.
12
+ #
13
+ # *Returns:*
14
+ # The corresponding lambda function.
15
+ def self.to_lambda(args, derivation)
16
+ eval("lambda { |#{args.join(',')}| #{derivation} }")
17
+ end
18
+
19
+ # Converts this derivation into a usable lambda function.
20
+ #
21
+ # *Parameters:*
22
+ # * args, an ordered array of the arguments to the lambda function.
23
+ #
24
+ # *Returns:*
25
+ # The corresponding lambda function.
26
+ def to_lambda(args)
27
+ Wallace::GE::GrammarDerivation.to_lambda(args, self)
28
+ end
29
+
30
+ end
@@ -0,0 +1,29 @@
1
+ # The grammar species represents each individual in the species as an array of integers which
2
+ # map to a derivation of the grammar attached to the species.
3
+ class Wallace::GE::GrammarSpecies < Wallace::Species::ArraySpecies
4
+
5
+ name = :grammar
6
+
7
+ attr_reader :grammar
8
+
9
+ # Constructs a new grammar-based species.
10
+ #
11
+ # *Parameters:*
12
+ # * opts, hash of keyword options used by this method.
13
+ # -> id, the unique identifier for this species.
14
+ # -> grammar, the grammar used by individuals of this species.
15
+ # -> length, the length constraints on individuals of the species.
16
+ def initialize(opts = {})
17
+
18
+ # By default grammar derivations are between 10 and 200 symbols and contain
19
+ # integers in the range 0 to 2147483647 inclusive.
20
+ opts[:length] ||= 10..200
21
+ opts[:values] ||= 0..2147483647
22
+
23
+ super(opts)
24
+
25
+ @grammar = opts[:grammar]
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,5 @@
1
+ module Wallace::GE; end
2
+
3
+ require_relative 'backus_naur_form.rb'
4
+ require_relative 'grammar_species.rb'
5
+ require_relative 'grammar_derivation.rb'
@@ -0,0 +1,2 @@
1
+ require_relative 'koza/init.rb'
2
+ require_relative 'ge/init.rb'
@@ -0,0 +1,48 @@
1
+ # The base class used by all Koza tree builders.
2
+ class Wallace::Koza::Builder
3
+
4
+ # The range of depths that trees within this species may have.
5
+ attr_accessor :depth_limits
6
+
7
+ attr_reader :terminals,
8
+ :non_terminals
9
+
10
+ # Prepares the builder.
11
+ #
12
+ # *Parameters:*
13
+ # * terminals, the list of terminals.
14
+ # * non_terminals, the list of non-terminals.
15
+ # * depth_limits, the range of depths that trees within the species may have.
16
+ def prepare(terminals, non_terminals, depth_limits)
17
+ @terminals = terminals
18
+ @non_terminals = non_terminals
19
+ @depth_limits = depth_limits
20
+ end
21
+
22
+ # Builds a new tree according to this build method that meets the depth
23
+ # constraints of the species.
24
+ #
25
+ # *Parameters:*
26
+ # * opts, a hash of keyword options for this method.
27
+ # -> random, the RNG to use for building the tree.
28
+ #
29
+ # *Returns:*
30
+ # The built tree.
31
+ def build_tree(opts = {})
32
+ raise NotImplementedError, 'No "build_tree" function was implemented by this builder.'
33
+ end
34
+
35
+ # Builds a new subtree of a maximum given depth.
36
+ #
37
+ # *Parameters:*
38
+ # * max_depth, the maximum depth of the sub-tree.
39
+ # * opts, a hash of keyword options for this method.
40
+ # -> random, the RNG to use for building the subtree.
41
+ #
42
+ # *Returns:*
43
+ # The root node of the built sub-tree.
44
+ def build_subtree(max_depth, opts = {})
45
+ raise NotImplementedError, 'No "build_subtree" function was implemented by this builder.'
46
+ end
47
+
48
+ end