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,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
|
data/lib/fitness/init.rb
ADDED
|
@@ -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
|
data/lib/loggers/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module Wallace::Loggers; 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
|
data/lib/modules/init.rb
ADDED
|
@@ -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
|