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,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,3 @@
1
+ require_relative 'full_builder.rb'
2
+ require_relative 'grow_builder.rb'
3
+ require_relative 'half_builder.rb'
@@ -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