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