drp 0.0.6
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/AUTHORS +6 -0
- data/CHANGES +0 -0
- data/INTRO +309 -0
- data/LICENSE +281 -0
- data/README +77 -0
- data/Rakefile +99 -0
- data/TODO +13 -0
- data/examples/intro/README +2 -0
- data/examples/intro/canvas_example.rb +123 -0
- data/examples/intro/max_depths_example.rb +34 -0
- data/examples/intro/max_depths_example_2.rb +30 -0
- data/examples/intro/odds_and_ends.rb +96 -0
- data/examples/intro/parameterization_example.rb +32 -0
- data/examples/intro/toy_example.rb +22 -0
- data/examples/intro/weight_fcd_example.rb +44 -0
- data/examples/intro/weights_example.rb +33 -0
- data/examples/intro/weights_example_2.rb +30 -0
- data/examples/symbolic_regression.rb +127 -0
- data/lib/defaults.rb +30 -0
- data/lib/drp.rb +30 -0
- data/lib/error.rb +69 -0
- data/lib/info.rb +30 -0
- data/lib/instance_methods.rb +154 -0
- data/lib/pso.rb +178 -0
- data/lib/rule_engine.rb +324 -0
- data/lib/utils.rb +67 -0
- data/lib/weights_and_max_depths.rb +264 -0
- data/test/tc_instance_methods.rb +219 -0
- data/test/tc_max_depths.rb +98 -0
- data/test/tc_utils.rb +60 -0
- data/test/tc_weights.rb +372 -0
- data/test/ts_drp.rb +28 -0
- metadata +80 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
|
2
|
+
=begin
|
3
|
+
|
4
|
+
DRP, Genetic Programming + Grammatical Evolution = Directed Ruby Programming
|
5
|
+
Copyright (C) 2006, Christophe McKeon
|
6
|
+
|
7
|
+
This program is free software; you can redistribute it and/or
|
8
|
+
modify it under the terms of the GNU General Public License
|
9
|
+
as published by the Free Software Foundation; either version 2
|
10
|
+
of the License, or (at your option) any later version.
|
11
|
+
|
12
|
+
This program is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU General Public License
|
18
|
+
along with this program; if not, write to the Free Softwar Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
19
|
+
|
20
|
+
=end
|
21
|
+
|
22
|
+
module DRP
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
|
26
|
+
# this is called when all rule methods are exhausted
|
27
|
+
# during the selection process. by default returns nil.
|
28
|
+
# you can override this in your extended class,
|
29
|
+
# but as a regular method
|
30
|
+
# not a rule method to have non-nil value returned, but
|
31
|
+
# be sure to accept an array of *args or have the arity
|
32
|
+
# correct to handle all your rule methods.
|
33
|
+
def default_rule_method *args; end
|
34
|
+
|
35
|
+
=begin
|
36
|
+
# EXTRANEOUS, CAN JUST BE DONE IN :initialize METHOD
|
37
|
+
# this is called automatically for you after
|
38
|
+
# your objects are initialized but before any codons
|
39
|
+
# are used to set weights depths etc..
|
40
|
+
# it does nothing by default
|
41
|
+
def init_codons; end
|
42
|
+
=end
|
43
|
+
|
44
|
+
# you should reimplement this method in your
|
45
|
+
# extended class, that is unless you want the
|
46
|
+
# default behaviour of an endless random stream.
|
47
|
+
# this is included mostly for quick testing purposes
|
48
|
+
def next_codon
|
49
|
+
rand
|
50
|
+
end
|
51
|
+
|
52
|
+
# this is what weights and max_depths use to get codons.
|
53
|
+
# it defaults to just using next_codon. override it in
|
54
|
+
# your extended class to have them use a separate codon stream
|
55
|
+
def next_meta_codon
|
56
|
+
next_codon
|
57
|
+
end
|
58
|
+
|
59
|
+
# how deep is the current rule method's recursion
|
60
|
+
def depth
|
61
|
+
@__drp__depth__stack.last
|
62
|
+
end
|
63
|
+
|
64
|
+
# what is the maximum depth attainable by the current rule method.
|
65
|
+
# do not confuse this with the class method setter
|
66
|
+
def max_depth
|
67
|
+
@__drp__rule__method__stack.last.max_depth
|
68
|
+
end
|
69
|
+
|
70
|
+
# don't know if these two are really worth implementing
|
71
|
+
# how many time has the current rule method executed
|
72
|
+
# including this execution
|
73
|
+
#def count
|
74
|
+
# @__drp__count__stack.last
|
75
|
+
#end
|
76
|
+
# how many times has the current rule (all rule methods)
|
77
|
+
# executed including this execution
|
78
|
+
# def rule_count
|
79
|
+
# end
|
80
|
+
|
81
|
+
# uses next_codon to output to somewhere within the range
|
82
|
+
# specified using specified function, unless a block is given
|
83
|
+
# in which case it counts the formal parameters to the block,
|
84
|
+
# and yields appropriate number of codons using next_codon
|
85
|
+
# you may not pass both a block and a range, only one or the other
|
86
|
+
def map rng = nil, function = :linear, &b # :yields: next_codon ...
|
87
|
+
if block_given?
|
88
|
+
if rng
|
89
|
+
raise ArgumentError, "both block and #{rng} passed to map", caller
|
90
|
+
end
|
91
|
+
arity = b.arity
|
92
|
+
case arity
|
93
|
+
# these are here, and also ordered, for efficiencies sake
|
94
|
+
when 1
|
95
|
+
yield next_codon
|
96
|
+
when 2
|
97
|
+
yield next_codon, next_codon
|
98
|
+
when 0, -1
|
99
|
+
raise ArgumentError, 'block given to map must have 1 or more arguments', caller
|
100
|
+
else
|
101
|
+
yield *Array.new(arity) { next_codon }
|
102
|
+
end
|
103
|
+
else
|
104
|
+
Utils::map rng, next_codon, function
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def __drp__choose__method useable_methods
|
111
|
+
|
112
|
+
weights = useable_methods.collect do |meth|
|
113
|
+
meth.weight
|
114
|
+
end
|
115
|
+
scale_by = weights.inject(0) do |weight, prev|
|
116
|
+
prev + weight
|
117
|
+
end
|
118
|
+
|
119
|
+
weights = if scale_by == 0
|
120
|
+
sz = weights.size
|
121
|
+
weight = 1.0/sz
|
122
|
+
prev_weight = 0
|
123
|
+
Array.new(sz) do
|
124
|
+
prev_weight = weight + prev_weight
|
125
|
+
end
|
126
|
+
else
|
127
|
+
prev_weight = 0
|
128
|
+
weights.collect do |weight|
|
129
|
+
prev_weight = weight / scale_by + prev_weight
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
index, codon = -1, next_codon
|
134
|
+
weights.detect do |weight|
|
135
|
+
index += 1;
|
136
|
+
codon < weight
|
137
|
+
end
|
138
|
+
|
139
|
+
useable_methods[index]
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
def __drp__call__method meth, args
|
144
|
+
@__drp__depth__stack.push(meth.depth + 1)
|
145
|
+
@__drp__rule__method__stack.push meth
|
146
|
+
res = meth.call *args
|
147
|
+
@__drp__rule__method__stack.pop
|
148
|
+
@__drp__depth__stack.pop
|
149
|
+
res
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end # module DRP
|
data/lib/pso.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
|
2
|
+
=begin
|
3
|
+
|
4
|
+
DRP, Genetic Programming + Grammatical Evolution = Directed Ruby Programming
|
5
|
+
Copyright (C) 2006, Christophe McKeon
|
6
|
+
|
7
|
+
This program is free software; you can redistribute it and/or
|
8
|
+
modify it under the terms of the GNU General Public License
|
9
|
+
as published by the Free Software Foundation; either version 2
|
10
|
+
of the License, or (at your option) any later version.
|
11
|
+
|
12
|
+
This program is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU General Public License
|
18
|
+
along with this program; if not, write to the Free Softwar Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
19
|
+
|
20
|
+
=end
|
21
|
+
|
22
|
+
module DRP
|
23
|
+
module SearchAlgorithms
|
24
|
+
module PSO
|
25
|
+
|
26
|
+
VERY_LARGE_NUMBER = 2**29
|
27
|
+
|
28
|
+
class AbstractParticleSwarmOptimizer
|
29
|
+
|
30
|
+
attr_reader :global_best_error, :global_best_vector
|
31
|
+
|
32
|
+
def initialize swarm_size, vector_size
|
33
|
+
@swarm_size, @vector_size = swarm_size, vector_size
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# must be called by subclass initialize methods
|
39
|
+
def init_particles particle_class
|
40
|
+
@particles = Array.new(@swarm_size) { particle_class.new(self,@vector_size) }
|
41
|
+
set_as_global_best @particles[rand(@swarm_size)]
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_as_global_best particle
|
45
|
+
@global_best_vector = particle.vector.dup
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class InteractiveParticle
|
51
|
+
|
52
|
+
attr_accessor :vector
|
53
|
+
|
54
|
+
def initialize pso, vector_size
|
55
|
+
@pso, @vector_size = pso, vector_size
|
56
|
+
init_vector
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_as_best
|
60
|
+
@best_vector = @vector.dup
|
61
|
+
end
|
62
|
+
def set_as_global_best
|
63
|
+
@pso.set_as_global_best self
|
64
|
+
end
|
65
|
+
|
66
|
+
def roam
|
67
|
+
gbest = @pso.global_best_vector
|
68
|
+
@vector_size.times do |i|
|
69
|
+
c = @vector[i]
|
70
|
+
pbest = @best_vector[i]
|
71
|
+
@vector[i] = c + 2 * rand * pbest - c + 2 * rand * gbest[i] - c
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def init_vector
|
76
|
+
@vector = Array.new(@vector_size) { rand }
|
77
|
+
set_as_best
|
78
|
+
end
|
79
|
+
|
80
|
+
=begin
|
81
|
+
def save name
|
82
|
+
|
83
|
+
end
|
84
|
+
def load name
|
85
|
+
if @thawed
|
86
|
+
# ...
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def freeze; @thawed = false end
|
91
|
+
def thaw; @thawed = true end
|
92
|
+
def frozen? !@thawed end
|
93
|
+
=end
|
94
|
+
end
|
95
|
+
|
96
|
+
class InteractiveParticleSwarmOptimizer < AbstractParticleSwarmOptimizer
|
97
|
+
|
98
|
+
attr_reader :particles
|
99
|
+
|
100
|
+
def initialize swarm_size, vector_size, rebirth = 0.0
|
101
|
+
super
|
102
|
+
init_particles InteractiveParticle
|
103
|
+
end
|
104
|
+
def each
|
105
|
+
@particles.each do |p|
|
106
|
+
yield p
|
107
|
+
end
|
108
|
+
end
|
109
|
+
def roam_all
|
110
|
+
each { |p| p.roam }
|
111
|
+
end
|
112
|
+
def reinit_all
|
113
|
+
each { |p| p.init_vector }
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
class Particle < InteractiveParticle
|
119
|
+
def initialize pso, vector_size
|
120
|
+
@best_error = VERY_LARGE_NUMBER
|
121
|
+
super
|
122
|
+
end
|
123
|
+
def optimize error
|
124
|
+
if error < @best_error
|
125
|
+
@best_error = error
|
126
|
+
set_as_best
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class ParticleSwarmOptimizer < AbstractParticleSwarmOptimizer
|
132
|
+
|
133
|
+
def initialize swarm_size, vector_size, rebirth = 0.0
|
134
|
+
@global_best_error = VERY_LARGE_NUMBER
|
135
|
+
@num_reborn = (swarm_size * rebirth).to_i
|
136
|
+
@rebirth_index = 0
|
137
|
+
super swarm_size, vector_size
|
138
|
+
init_particles Particle
|
139
|
+
end
|
140
|
+
|
141
|
+
def each
|
142
|
+
rebirth
|
143
|
+
best_this_time = VERY_LARGE_NUMBER
|
144
|
+
@particles.each do |p|
|
145
|
+
v = p.vector
|
146
|
+
error = yield v
|
147
|
+
if error < @global_best_error
|
148
|
+
@global_best_vector = v.dup
|
149
|
+
@global_best_error = error
|
150
|
+
end
|
151
|
+
if error < best_this_time
|
152
|
+
best_this_time = error
|
153
|
+
end
|
154
|
+
p.optimize error
|
155
|
+
p.roam
|
156
|
+
end
|
157
|
+
#puts best_this_time
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
# my non-standard addition to pso algorithm
|
163
|
+
# the idea being brutal removal from local optima.
|
164
|
+
# cycles through particles to give the reborn
|
165
|
+
# a chance to roam a bit before being reborn yet again.
|
166
|
+
def rebirth
|
167
|
+
@num_reborn.times do
|
168
|
+
@rebirth_index = 0 if @rebirth_index == @swarm_size
|
169
|
+
@particles[@rebirth_index].init_vector
|
170
|
+
@rebirth_index += 1
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
end # module PSO
|
177
|
+
end # module SearchAlgoritms
|
178
|
+
end # module DRP
|
data/lib/rule_engine.rb
ADDED
@@ -0,0 +1,324 @@
|
|
1
|
+
|
2
|
+
=begin
|
3
|
+
|
4
|
+
DRP, Genetic Programming + Grammatical Evolution = Directed Ruby Programming
|
5
|
+
Copyright (C) 2006, Christophe McKeon
|
6
|
+
|
7
|
+
This program is free software; you can redistribute it and/or
|
8
|
+
modify it under the terms of the GNU General Public License
|
9
|
+
as published by the Free Software Foundation; either version 2
|
10
|
+
of the License, or (at your option) any later version.
|
11
|
+
|
12
|
+
This program is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU General Public License
|
18
|
+
along with this program; if not, write to the Free Softwar Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
19
|
+
|
20
|
+
=end
|
21
|
+
|
22
|
+
module DRP
|
23
|
+
|
24
|
+
class RuleMethod
|
25
|
+
|
26
|
+
attr_reader :depth, :max_depth
|
27
|
+
|
28
|
+
def initialize drp_instance, method_name, weight_factory, max_depth
|
29
|
+
@method = drp_instance.method method_name
|
30
|
+
@max_depth = max_depth.value drp_instance
|
31
|
+
@weight = weight_factory.call self, drp_instance
|
32
|
+
@depth = 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def expressed?
|
36
|
+
@depth < @max_depth
|
37
|
+
end
|
38
|
+
|
39
|
+
def call *args
|
40
|
+
@depth += 1
|
41
|
+
result = @method.call *args
|
42
|
+
@depth -= 1
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
def weight
|
47
|
+
@weight.value
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
module RuleEngine
|
53
|
+
|
54
|
+
def new *a, &b
|
55
|
+
super.__drp__init
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def self.extend_object klass
|
61
|
+
DRPNameClashError.test(klass) if DEFAULT[:test_for_extend_name_clashes]
|
62
|
+
klass.class_eval do
|
63
|
+
include InstanceMethods
|
64
|
+
end
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
def method_added name
|
69
|
+
# explicit boolean test because can also be :finished
|
70
|
+
if @__drp__defining_rules == true
|
71
|
+
|
72
|
+
rule = @__drp__rules[name] ||= []
|
73
|
+
|
74
|
+
# to stop recursion of method_added due to alias which calls it
|
75
|
+
@__drp__defining_rules = false
|
76
|
+
class_eval "private :#{name}; alias __drp__#{name}__#{rule.size} #{name}"
|
77
|
+
@__drp__defining_rules = true
|
78
|
+
|
79
|
+
# note this comes after class_eval cuz of ref to rule.size there
|
80
|
+
rule << [@__drp__weights.last, @__drp__max__depths.last]
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# TODO find way of making rdoc document these even if they are private
|
86
|
+
public
|
87
|
+
|
88
|
+
def begin_rules
|
89
|
+
# if it is true or :finished, see end_rules & method_added
|
90
|
+
if @__drp__defining_rules
|
91
|
+
raise DRPError, 'begin rules may only be called once'
|
92
|
+
else
|
93
|
+
@__drp__rules = {}
|
94
|
+
@__drp__defining_rules = true
|
95
|
+
@__drp__weights = []
|
96
|
+
@__drp__max__depths = []
|
97
|
+
# NB max_depth should come first here in case user changes
|
98
|
+
# the default to some weight which needs to know the max_depth
|
99
|
+
max_depth DEFAULT[:max_depth]
|
100
|
+
weight DEFAULT[:weight]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# NB the two methods defined via define_method in end_rules
|
105
|
+
# will be instance methods in instances of classes which
|
106
|
+
# are extended by this module. they are here because they
|
107
|
+
# are closures referencing the 'name' and 'all_methods'
|
108
|
+
# variables respectively.
|
109
|
+
# other non-closure methods are in module InstanceMethods
|
110
|
+
|
111
|
+
def end_rules
|
112
|
+
|
113
|
+
@__drp__defining_rules = :finished
|
114
|
+
|
115
|
+
all_methods = {}
|
116
|
+
|
117
|
+
@__drp__rules.each do |name, weights_and_max_depths|
|
118
|
+
|
119
|
+
methods = []
|
120
|
+
|
121
|
+
weights_and_max_depths.each_with_index do |w_md, i|
|
122
|
+
methods.push ["__drp__#{name}__#{i}"] + w_md
|
123
|
+
end
|
124
|
+
|
125
|
+
all_methods[name] = methods
|
126
|
+
|
127
|
+
define_method name do |*args|
|
128
|
+
|
129
|
+
useable_methods = @__drp__rule__methods[name].select do |meth|
|
130
|
+
meth.expressed?
|
131
|
+
end
|
132
|
+
|
133
|
+
case useable_methods.size
|
134
|
+
|
135
|
+
when 0
|
136
|
+
default_rule_method *args
|
137
|
+
|
138
|
+
when 1
|
139
|
+
__drp__call__method(useable_methods.first, args)
|
140
|
+
|
141
|
+
else
|
142
|
+
__drp__call__method(__drp__choose__method(useable_methods), args)
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end # define_method name do
|
147
|
+
|
148
|
+
|
149
|
+
end # @__drp__rules.each do
|
150
|
+
|
151
|
+
define_method :__drp__init do
|
152
|
+
|
153
|
+
@__drp__rule__methods = {}
|
154
|
+
@__drp__depth__stack = []
|
155
|
+
@__drp__rule__method__stack = []
|
156
|
+
|
157
|
+
all_methods.each do |name, arg_array|
|
158
|
+
@__drp__rule__methods[name] = arg_array.collect do |args|
|
159
|
+
RuleMethod.new self, *args
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
self
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end # end_rules
|
168
|
+
|
169
|
+
=begin rdoc
|
170
|
+
|
171
|
+
sets the maximum obtainable depth of the rule methods which follow it
|
172
|
+
in the listing. max_depths are set for each instance of the class you
|
173
|
+
extended with DRP::RuleEngine upon initialization (or when you call init_drp),
|
174
|
+
once and only once, and remains set to that static value. max depths are always
|
175
|
+
integer values.
|
176
|
+
|
177
|
+
it may be used in any of the following four ways:
|
178
|
+
|
179
|
+
*scalar*, all following rule_methods are set to given value
|
180
|
+
|
181
|
+
max_depth 1
|
182
|
+
|
183
|
+
*range*, each following rule_method is initialized to some integer
|
184
|
+
from range.start to range.end inclusive using a linear mapping
|
185
|
+
of a codon gotten via the method next_meta_codon.
|
186
|
+
|
187
|
+
max_depth 1..2
|
188
|
+
|
189
|
+
same as the previous but uses the function given, at present
|
190
|
+
the only function implemented is i_linear (integer linear),
|
191
|
+
which is the default action when no function is given, so at
|
192
|
+
present this is useless.
|
193
|
+
|
194
|
+
max_depth 1..2, :function
|
195
|
+
|
196
|
+
*block*, each following rule_method is initialized to some integer
|
197
|
+
given by your block. the formal parameters to your supplied
|
198
|
+
block are counted, and the appropriate number of codons harvested using
|
199
|
+
InstanceMethods#next_meta_codon are passed to the block.
|
200
|
+
|
201
|
+
max_depth { |next_meta_codon, ...| ... }
|
202
|
+
|
203
|
+
see example_2.rb
|
204
|
+
|
205
|
+
=end
|
206
|
+
|
207
|
+
def max_depth *args, &block
|
208
|
+
|
209
|
+
sz = args.size
|
210
|
+
are_args = sz > 0
|
211
|
+
md = nil
|
212
|
+
|
213
|
+
if block_given?
|
214
|
+
if are_args
|
215
|
+
raise ArgumentError, 'max_depth called with both arguments and a block', caller
|
216
|
+
else
|
217
|
+
md = MaxDepths::ProcMaxDepth.new(block)
|
218
|
+
end
|
219
|
+
else
|
220
|
+
case sz
|
221
|
+
when 1
|
222
|
+
arg = args[0]
|
223
|
+
case arg
|
224
|
+
when Numeric
|
225
|
+
md = MaxDepths::StaticMaxDepth.new arg.to_i
|
226
|
+
when Range
|
227
|
+
md = MaxDepths::MappedMaxDepth.new arg
|
228
|
+
else
|
229
|
+
raise ArgumentError, 'bad argument to max_depth', caller
|
230
|
+
end
|
231
|
+
when 2
|
232
|
+
arg1, arg2 = args
|
233
|
+
if (arg1.kind_of? Range) && (arg2.kind_of? Symbol)
|
234
|
+
md = MaxDepths::MappedMaxDepth.new arg1, arg2
|
235
|
+
else
|
236
|
+
raise ArgumentError, 'bad argument to max_depth', caller
|
237
|
+
end
|
238
|
+
else
|
239
|
+
raise ArgumentError, "too many (#{sz}) args passed to max_depth", caller
|
240
|
+
end # case sz
|
241
|
+
end # if block_given
|
242
|
+
|
243
|
+
@__drp__max__depths << md
|
244
|
+
|
245
|
+
end # def max_depth
|
246
|
+
|
247
|
+
=begin rdoc
|
248
|
+
|
249
|
+
sets the weight of the rule methods which follow it
|
250
|
+
in the listing, and may be used in any of the following four ways:
|
251
|
+
|
252
|
+
*scalar*, all following rule_methods are set to given value
|
253
|
+
|
254
|
+
max_depth 1
|
255
|
+
|
256
|
+
*range*, each following rule_method is initialized to some integer
|
257
|
+
from range.start to range.end inclusive using a linear mapping
|
258
|
+
of a codon gotten via the method next_meta_codon when your
|
259
|
+
extended object is initialized via drp_init.
|
260
|
+
|
261
|
+
max_depth 1..2
|
262
|
+
|
263
|
+
same as the previous but uses the function given, at present
|
264
|
+
the only function implemented is i_linear (integer linear),
|
265
|
+
which is the default action when no function is given, so at
|
266
|
+
present this is useless.
|
267
|
+
|
268
|
+
max_depth 1..2, :function
|
269
|
+
|
270
|
+
*block*, each following rule_method is initialized to some integer
|
271
|
+
given by your block upon a call to drp_init, i.e. when you initialize
|
272
|
+
your extended object instance. the formal parameters to your supplied
|
273
|
+
block are counted, and the appropriate number of codons harvested using
|
274
|
+
InstanceMethods#next_meta_codon are passed to the block.
|
275
|
+
|
276
|
+
max_depth { |next_meta_codon, ...| ... }
|
277
|
+
|
278
|
+
=end
|
279
|
+
|
280
|
+
|
281
|
+
def weight *args, &block
|
282
|
+
bg, are_args = block_given?, args.size > 0
|
283
|
+
if bg && are_args
|
284
|
+
raise ArgumentError, 'weight called with both arguments and a block', caller
|
285
|
+
elsif bg
|
286
|
+
@__drp__weights << Weights::ProcStaticWeight.factory(block)
|
287
|
+
elsif are_args
|
288
|
+
@__drp__weights << Weights::StaticWeight.factory(args)
|
289
|
+
else
|
290
|
+
raise ArgumentError, 'weight called with neither args nor block', caller
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
=begin NOT REALLY USEFUL
|
295
|
+
def weight_dyn *args, &block
|
296
|
+
bg, are_args = block_given?, args.size > 0
|
297
|
+
if bg && are_args
|
298
|
+
__drp__error "weight_dyn called with both args and block"
|
299
|
+
elsif bg
|
300
|
+
@__drp__weights << Weights::ProcDynamicWeight.factory(block)
|
301
|
+
elsif are_args
|
302
|
+
@__drp__weights << Weights::DynamicWeight.factory(args)
|
303
|
+
else
|
304
|
+
raise ArgumentError, 'weight_dyn called with neither args nor block', caller
|
305
|
+
end
|
306
|
+
end
|
307
|
+
=end
|
308
|
+
|
309
|
+
def weight_fcd *args, &block
|
310
|
+
bg, are_args = block_given?, args.size > 0
|
311
|
+
if bg && are_args
|
312
|
+
__drp__error "weight_fcd called with both args and block"
|
313
|
+
elsif bg
|
314
|
+
@__drp__weights << Weights::ProcWeightFromCurrentDepth.factory(block)
|
315
|
+
elsif are_args
|
316
|
+
@__drp__weights << Weights::WeightFromCurrentDepth.factory(args)
|
317
|
+
else
|
318
|
+
raise ArgumentError, 'weight_fcd called with neither args nor block', caller
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
end # class RuleEngine
|
323
|
+
|
324
|
+
end # module DRP
|