wallace 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,92 @@
|
|
1
|
+
# The Full builder constructs Koza trees according to a slight variant of Koza's
|
2
|
+
# FULL method. Unlike Koza's original FULL method, this FULL method allows single
|
3
|
+
# node trees to be constructed.
|
4
|
+
#
|
5
|
+
# A random integer d is selected as the depth of the tree, between the minimum
|
6
|
+
# and maximum depth limits inclusively. A full tree of exactly depth d is
|
7
|
+
# then generated.
|
8
|
+
class Wallace::Koza::Builder::FullBuilder < Wallace::Koza::Builder
|
9
|
+
|
10
|
+
# Builds a full subtree of a given depth using a variant
|
11
|
+
# of Koza's FULL method (where single element sub-trees are acceptable).
|
12
|
+
#
|
13
|
+
# *Parameters:*
|
14
|
+
# * terminals, the list of terminals.
|
15
|
+
# * non_terminals, the list of non-terminals.
|
16
|
+
# * depth, the depth of the sub-tree.
|
17
|
+
# * opts, a hash of keyword options for this method.
|
18
|
+
# -> random, the RNG to use for building the subtree.
|
19
|
+
#
|
20
|
+
# *Returns:*
|
21
|
+
# The root node of the built sub-tree.
|
22
|
+
def self.full_subtree(terminals, non_terminals, depth, opts = {})
|
23
|
+
|
24
|
+
opts[:random] ||= Random.new
|
25
|
+
|
26
|
+
# If we require a sub-tree of depth one then we simply return a node with a value
|
27
|
+
# sampled from the set of terminals.
|
28
|
+
return Wallace::Koza::KozaNode.new(terminals.sample(random: opts[:random])) if depth == 0
|
29
|
+
|
30
|
+
# Construct the children of each node in the queue until there are no nodes
|
31
|
+
# left to process. We assume that the node to be processed has children. This
|
32
|
+
# is fine to assume since sub-trees of length one are created and returned above, and
|
33
|
+
# terminal nodes aren't added to the queue for processing.
|
34
|
+
root = Wallace::Koza::KozaNode.new(non_terminals.sample(random: opts[:random]), depth: 0)
|
35
|
+
queue = [root]
|
36
|
+
until queue.empty?
|
37
|
+
|
38
|
+
parent = queue.shift
|
39
|
+
parent.children = Array.new(parent.arity) do
|
40
|
+
|
41
|
+
# To achieve a full tree we simply sample from the non-terminal set until the child
|
42
|
+
# is at the maximum depth.
|
43
|
+
if (parent.depth + 1) == depth
|
44
|
+
value = terminals.sample(random: opts[:random])
|
45
|
+
else
|
46
|
+
value = non_terminals.sample(random: opts[:random])
|
47
|
+
end
|
48
|
+
|
49
|
+
# Build the node and add it to the queue (provided it isn't terminal).
|
50
|
+
node = Wallace::Koza::KozaNode.new(value, parent: parent, depth: parent.depth + 1)
|
51
|
+
queue << node unless node.terminal?
|
52
|
+
node
|
53
|
+
|
54
|
+
end unless parent.terminal?
|
55
|
+
|
56
|
+
end
|
57
|
+
return root
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# Builds a full tree whose depth is between the minimum and maximum depths
|
62
|
+
# inclusively and every sub-tree is full.
|
63
|
+
#
|
64
|
+
# *Parameters:*
|
65
|
+
# * opts, a hash of keyword options for this method.
|
66
|
+
# -> random, the RNG to use for building the tree.
|
67
|
+
#
|
68
|
+
# *Returns:*
|
69
|
+
# The built tree.
|
70
|
+
def build_tree(opts = {})
|
71
|
+
opts[:random] ||= Random.new
|
72
|
+
depth = opts[:random].rand(@depth_limits)
|
73
|
+
Wallace::Koza::KozaTree.new(build_subtree(depth, random: opts[:random]))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Builds a full subtree of a given maximum depth.
|
77
|
+
#
|
78
|
+
# *Parameters:*
|
79
|
+
# * depth, the maximum depth of the sub-tree.
|
80
|
+
# * opts, a hash of keyword options for this method.
|
81
|
+
# -> random, the RNG to use for building the subtree.
|
82
|
+
#
|
83
|
+
# *Returns:*
|
84
|
+
# The root node of the built sub-tree.
|
85
|
+
#
|
86
|
+
# *See*
|
87
|
+
# FullBuilder.full_subtree
|
88
|
+
def build_subtree(depth, opts = {})
|
89
|
+
self.class.full_subtree(@terminals, @non_terminals, depth, opts)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# The Grow builder constructs Koza trees according to a slight variant of Koza's
|
2
|
+
# GROW method. Unlike Koza's original GROW method, this GROW method allows single
|
3
|
+
# node trees to be constructed.
|
4
|
+
#
|
5
|
+
# A random integer d is selected as the depth of the tree, between the minimum
|
6
|
+
# and maximum depth limits inclusively. A tree of up to depth d is then generated.
|
7
|
+
# At each stage we determine whether a node should be terminal or not by checking if
|
8
|
+
# the maximum depth has been reached, or some random number falls within the bounds of
|
9
|
+
# probability that a terminal should be selected.
|
10
|
+
class Wallace::Koza::Builder::GrowBuilder < Wallace::Koza::Builder
|
11
|
+
|
12
|
+
# The probability that a terminal will be selected when constructing a node using the GROW method.
|
13
|
+
attr_accessor :probability_terminal
|
14
|
+
|
15
|
+
# Builds a full subtree of a given maximum depth using a variant
|
16
|
+
# of Koza's GROW method (where single element sub-trees are acceptable).
|
17
|
+
#
|
18
|
+
# *Parameters:*
|
19
|
+
# * probability_terminal, the probability that a terminal node will be created at each stage.
|
20
|
+
# * opts, a hash of keyword options for this method.
|
21
|
+
# -> random, the RNG to use for building the subtree.
|
22
|
+
#
|
23
|
+
# *Returns:*
|
24
|
+
# The root node of the built sub-tree.
|
25
|
+
def self.grow_subtree(terminals, non_terminals, max_depth, probability_terminal, opts = {})
|
26
|
+
|
27
|
+
opts[:random] ||= Random.new
|
28
|
+
|
29
|
+
# If the maximum depth of the sub-tree is zero then we simply return a node with a value
|
30
|
+
# sampled from the set of terminals. If the maximum depth of the sub-tree is greater than one
|
31
|
+
# then we check if the first node is a terminal and if so, we also return a single terminal
|
32
|
+
# node.
|
33
|
+
if max_depth == 0 or opts[:random].rand <= probability_terminal
|
34
|
+
return Wallace::Koza::KozaNode.new(terminals.sample(random: opts[:random]))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Construct the children of each node in the queue until there are no nodes
|
38
|
+
# left to process. We decide whether each child is terminal or non-terminal according to the
|
39
|
+
# associated probability of a terminal being selected. If the maximum depth has been reached then
|
40
|
+
# we force the child to be a terminal.
|
41
|
+
root = Wallace::Koza::KozaNode.new(non_terminals.sample(random: opts[:random]), depth: 0)
|
42
|
+
queue = [root]
|
43
|
+
until queue.empty?
|
44
|
+
|
45
|
+
parent = queue.shift
|
46
|
+
parent.children = Array.new(parent.arity) do
|
47
|
+
|
48
|
+
if opts[:random].rand <= probability_terminal or (parent.depth + 1) == max_depth
|
49
|
+
value = terminals.sample(random: opts[:random])
|
50
|
+
else
|
51
|
+
value = non_terminals.sample(random: opts[:random])
|
52
|
+
end
|
53
|
+
|
54
|
+
node = Wallace::Koza::KozaNode.new(value, parent: parent, depth: parent.depth + 1)
|
55
|
+
queue << node unless node.terminal?
|
56
|
+
node
|
57
|
+
|
58
|
+
end unless parent.terminal?
|
59
|
+
|
60
|
+
end
|
61
|
+
return root
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
# Constructs a new GrowBuilder.
|
66
|
+
#
|
67
|
+
# *Parameters:*
|
68
|
+
# * probability_terminal, the probability that a terminal node will be built when using the GROW method.
|
69
|
+
def initialize(probability_terminal)
|
70
|
+
@probability_terminal = probability_terminal
|
71
|
+
end
|
72
|
+
|
73
|
+
# Selects a random integer d, where d is between the minimum and maximum tree depth inclusively, and creates
|
74
|
+
# a tree of up to depth d.
|
75
|
+
#
|
76
|
+
# *Parameters:*
|
77
|
+
# * opts, a hash of keyword options for this method.
|
78
|
+
# -> random, the RNG to use for building the tree.
|
79
|
+
#
|
80
|
+
# *Returns:*
|
81
|
+
# The built tree.
|
82
|
+
def build_tree(opts = {})
|
83
|
+
opts[:random] ||= Random.new
|
84
|
+
Wallace::Koza::KozaTree.new(build_subtree(opts[:random].rand(@depth_limits), random: opts[:random]))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Builds a full subtree of a given maximum depth.
|
88
|
+
#
|
89
|
+
# *Parameters:*
|
90
|
+
# * max_depth, the maximum depth of the sub-tree.
|
91
|
+
# * opts, a hash of keyword options for this method.
|
92
|
+
# -> random, the RNG to use for building the subtree.
|
93
|
+
#
|
94
|
+
# *Returns:*
|
95
|
+
# The root node of the built sub-tree.
|
96
|
+
#
|
97
|
+
# *See*
|
98
|
+
# FullBuilder.full_subtree
|
99
|
+
def build_subtree(max_depth, opts = {})
|
100
|
+
self.class.grow_subtree(@terminals, @non_terminals, max_depth, @probability_terminal, opts)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# The Half Builder is a (slightly modified) implementation of Koza's Ramped Half-and-Half builder.
|
2
|
+
#
|
3
|
+
# It works by selecting a random integer d, between the minimum and maximum depth inclusively.
|
4
|
+
# probability_grow of the time the grow method is used to generate a tree of up to depth d using the
|
5
|
+
# GROW builder. (1-probability_grow) of the time the FULL method is used to generate trees of exactly
|
6
|
+
# depth d.
|
7
|
+
class Wallace::Koza::Builder::HalfBuilder < Wallace::Koza::Builder
|
8
|
+
|
9
|
+
# The probability that the GROW method will be used to generate a tree.
|
10
|
+
attr_reader :probability_grow
|
11
|
+
|
12
|
+
# The probability that a terminal will be selected when constructing a node using the GROW method.
|
13
|
+
attr_accessor :probability_terminal
|
14
|
+
|
15
|
+
# Constructs a new HalfBuilder.
|
16
|
+
#
|
17
|
+
# *Parameters:*
|
18
|
+
# * probability_grow, the probability that the GROW method will be used when generating a tree.
|
19
|
+
# * probability_terminal, the probability that a terminal node will be built when using the GROW method.
|
20
|
+
def initialize(probability_grow, probability_terminal)
|
21
|
+
@probability_grow = probability_grow
|
22
|
+
@probability_terminal = probability_terminal
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the probability that the FULL method will be used to generate a tree.
|
26
|
+
def probability_full
|
27
|
+
1.0 - @probability_grow
|
28
|
+
end
|
29
|
+
|
30
|
+
# Builds a new Koza Tree of depth d, where d is within the allowed minimum and maximum
|
31
|
+
# tree depths, using either the GROW or FULL method.
|
32
|
+
#
|
33
|
+
# *Parameters:*
|
34
|
+
# * opts, a hash of keyword options for this method.
|
35
|
+
# -> random, the RNG to use for the process.
|
36
|
+
#
|
37
|
+
# *Returns:*
|
38
|
+
# A new Koza tree.
|
39
|
+
def build_tree(opts = {})
|
40
|
+
Wallace::Koza::KozaTree.new(build_subtree(@depth_limits.sample, random: opts[:random]))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Builds a new subtree of a maximum given depth using either the GROW or FULL method.
|
44
|
+
#
|
45
|
+
# *Parameters:*
|
46
|
+
# * depth, the (maximum) depth of the subtree.
|
47
|
+
# * opts, a hash of keyword options for this method.
|
48
|
+
# -> random, the RNG to use for the process.
|
49
|
+
def build_subtree(depth, opts = {})
|
50
|
+
|
51
|
+
opts[:random] ||= Random.new
|
52
|
+
|
53
|
+
return Wallace::Koza::Builder::GrowBuilder.grow_subtree(
|
54
|
+
@terminals,
|
55
|
+
@non_terminals,
|
56
|
+
depth,
|
57
|
+
@probability_terminal,
|
58
|
+
random: opts[:random]
|
59
|
+
) if @probability_grow <= opts[:random].rand
|
60
|
+
|
61
|
+
return Wallace::Koza::Builder::FullBuilder.full_subtree(
|
62
|
+
@terminals,
|
63
|
+
@non_terminals,
|
64
|
+
depth,
|
65
|
+
random: opts[:random]
|
66
|
+
)
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# An ephemeral instance is a special type of terminal symbol which only assumes its value (from a set
|
2
|
+
# of possible values) once it is selected. The Ephemeral class is used to store the set of possible values
|
3
|
+
# that a given ephemeral instance can take.
|
4
|
+
#
|
5
|
+
# It is possible to bias the selection of values by the Ephemeral by using a weighted collection or a distribution.
|
6
|
+
#
|
7
|
+
# *Parameters:*
|
8
|
+
# * values, the range, set or collection of values instances of the ephemeral can assume.
|
9
|
+
class Wallace::Koza::Ephemeral
|
10
|
+
|
11
|
+
# Constructs a new Ephemeral.
|
12
|
+
#
|
13
|
+
# *Parameters:*
|
14
|
+
# * values, the collection of values that instances of this ephemeral can take.
|
15
|
+
def initialize(values)
|
16
|
+
@values = values
|
17
|
+
end
|
18
|
+
|
19
|
+
# Samples a possible value for this ephemeral.
|
20
|
+
#
|
21
|
+
# *Parameters:*
|
22
|
+
# * opts, a hash of keyword parameters to this method.
|
23
|
+
# -> random, the RNG to use when sampling a value.
|
24
|
+
def sample(opts = {})
|
25
|
+
Wallace::Koza::EphemeralInstance.new(@values.sample)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# Holds the value of an ephemeral.
|
31
|
+
#
|
32
|
+
# Unlike standard terminal and non-terminal node values, ephemeral values can be manipulated during breeding.
|
33
|
+
class Wallace::Koza::EphemeralInstance < Wallace::Koza::Literal; end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Wallace::Koza; end
|
2
|
+
|
3
|
+
require_relative 'koza_node.rb'
|
4
|
+
require_relative 'koza_node_value.rb'
|
5
|
+
require_relative 'koza_node_value_set.rb'
|
6
|
+
require_relative 'ephemeral.rb'
|
7
|
+
require_relative 'builder.rb'
|
8
|
+
require_relative 'koza_tree.rb'
|
9
|
+
require_relative 'koza_species.rb'
|
10
|
+
|
11
|
+
require_relative 'builder/init.rb'
|
12
|
+
require_relative 'operators/init.rb'
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# This class is used to represent nodes within a Koza tree.
|
2
|
+
class Wallace::Koza::KozaNode
|
3
|
+
|
4
|
+
# We allow all attributes to be manipulated from outside but
|
5
|
+
# trust that all modification to trees is performed through the
|
6
|
+
# KozaTree interfaces (which fix depths and calculate the list
|
7
|
+
# of nodes in the tree).
|
8
|
+
attr_accessor :contents,
|
9
|
+
:depth,
|
10
|
+
:parent,
|
11
|
+
:children
|
12
|
+
|
13
|
+
# Constructs a new node for a Koza tree.
|
14
|
+
#
|
15
|
+
# *Parameters:*
|
16
|
+
# * contents, the contents of this node.
|
17
|
+
# * opts, a hash of keyword options for this tree node.
|
18
|
+
# -> parent, the parent of this node.
|
19
|
+
# -> children, an array of the children of this node.
|
20
|
+
def initialize(contents, opts = {})
|
21
|
+
@contents = contents
|
22
|
+
@parent = opts[:parent]
|
23
|
+
@children = opts[:children] || []
|
24
|
+
@depth = opts[:depth]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sets the parent node of this node.
|
28
|
+
#
|
29
|
+
# *Parameters:*
|
30
|
+
# * node, the node to set as the parent of this node.
|
31
|
+
def parent=(node)
|
32
|
+
@parent = node
|
33
|
+
end
|
34
|
+
|
35
|
+
# Checks to see if this node is a root node (i.e. it has no parent).
|
36
|
+
def root?
|
37
|
+
@parent.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Checks to see if this node is a leaf (i.e. it has no children).
|
41
|
+
#
|
42
|
+
# *Returns*
|
43
|
+
# true if leaf, false if not.
|
44
|
+
def leaf?
|
45
|
+
@children.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks whether this node contains a terminal value.
|
49
|
+
def terminal?
|
50
|
+
@contents.is_a? Wallace::Koza::Terminal
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks whether the contents of this node are an ephemeral instance.
|
54
|
+
#
|
55
|
+
# *Returns:*
|
56
|
+
# True if the contents are an ephemeral instance, false if otherwise.
|
57
|
+
def ephemeral?
|
58
|
+
@contents.is_a? Wallace::Koza::EphemeralInstance
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the label at this node.
|
62
|
+
def label
|
63
|
+
@contents.label
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the value of this node (or the label in the case of non-terminals).
|
67
|
+
def value
|
68
|
+
terminal? ? @contents.value : @contents.label
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the arity (number of inputs) to this node.
|
72
|
+
def arity
|
73
|
+
terminal? ? 0 : @contents.arity
|
74
|
+
end
|
75
|
+
|
76
|
+
# Creates a clone of this node (and its children).
|
77
|
+
# Parent node is discarded in the clone (unless specified not to do so).
|
78
|
+
#
|
79
|
+
# *Parameters:*
|
80
|
+
# * opts, a hash of keyword options for this method.
|
81
|
+
# -> keep_parent, if true maintains depth and parent information.
|
82
|
+
# otherwise this information is lost.
|
83
|
+
#
|
84
|
+
# *Returns:*
|
85
|
+
# A clone of this node (and its children).
|
86
|
+
def clone(opts = {})
|
87
|
+
cloned = Wallace::Koza::KozaNode.new(
|
88
|
+
@contents,
|
89
|
+
parent: opts[:keep_parent] ? @parent : nil ,
|
90
|
+
children: @children.map { |c| c.clone })
|
91
|
+
cloned.children.each { |c| c.parent = cloned }
|
92
|
+
return cloned
|
93
|
+
end
|
94
|
+
|
95
|
+
# Evaluates the S-expression given by the sub-tree at this node.
|
96
|
+
#
|
97
|
+
# *Parameters:*
|
98
|
+
# * args, the arguments to the function / program (as a hash).
|
99
|
+
def eval(args)
|
100
|
+
terminal? ? @contents.eval(args) : @contents.eval(@children.map { |c| c.eval(args) })
|
101
|
+
end
|
102
|
+
|
103
|
+
# Converts the sub-tree at this node into a Ruby program string.
|
104
|
+
def to_ruby
|
105
|
+
terminal? ? @contents.value.to_s : "#{@contents.label}(#{@children.map{ |c| c.to_ruby }.join(',')})"
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Used to represent an option from either the terminal or non-terminal set.
|
2
|
+
class Wallace::Koza::KozaNodeValue; end
|
3
|
+
|
4
|
+
# Holds an option from the terminal set.
|
5
|
+
class Wallace::Koza::Terminal < Wallace::Koza::KozaNodeValue
|
6
|
+
|
7
|
+
attr_reader :value
|
8
|
+
|
9
|
+
# Constructs a terminal.
|
10
|
+
#
|
11
|
+
# *Parameters:*
|
12
|
+
# * value, the value of the node.
|
13
|
+
def initialize(value)
|
14
|
+
@value = value
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
# Holds a variable option from the terminal set.
|
20
|
+
class Wallace::Koza::Variable < Wallace::Koza::Terminal
|
21
|
+
|
22
|
+
# Evaluates this terminal.
|
23
|
+
#
|
24
|
+
# *Parameters:*
|
25
|
+
# * args, the arguments to the program.
|
26
|
+
def eval(args)
|
27
|
+
args[@value.to_sym]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# Holds a literal option from the terminal set.
|
33
|
+
class Wallace::Koza::Literal < Wallace::Koza::Terminal
|
34
|
+
|
35
|
+
# Evaluates this terminal.
|
36
|
+
#
|
37
|
+
# *Parameters:*
|
38
|
+
# * args, the arguments to the program.
|
39
|
+
def eval(args)
|
40
|
+
@value
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# Holds an option from the non-terminal set.
|
46
|
+
class Wallace::Koza::NonTerminal < Wallace::Koza::KozaNodeValue
|
47
|
+
|
48
|
+
attr_reader :arity,
|
49
|
+
:label,
|
50
|
+
:function
|
51
|
+
|
52
|
+
# Constructs a non-terminal.
|
53
|
+
#
|
54
|
+
# *Parameters:*
|
55
|
+
# * label, the label of the non-terminal.
|
56
|
+
# * arity, the number of inputs to the non-terminal.
|
57
|
+
# * function, the function object for this non-terminal.
|
58
|
+
def initialize(label, arity, function)
|
59
|
+
@label = label
|
60
|
+
@arity = arity
|
61
|
+
@function = function
|
62
|
+
end
|
63
|
+
|
64
|
+
# Evaluates this non-terminal.
|
65
|
+
#
|
66
|
+
# *Parameters:*
|
67
|
+
# * inputs, the input values to this non-terminal function.
|
68
|
+
def eval(inputs)
|
69
|
+
@function.call(*inputs)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|