| Path: | lib/charlie/tree/tree.rb |
| Last Update: | Sat Jan 26 01:08:38 +0100 2008 |
Tree genotype, crossover, mutation etc.
| TreeReplaceMutator | = | TreeReplaceMutator() |
| TreePruneMutator | = | TreeReplaceMutator(0) |
| TreeNumTerminalMutator | = | TreeNumTerminalMutator() |
| TreeEvalMutator | = | TreeEvalMutator() |
# File lib/charlie/tree/tree.rb, line 83
83: def initialize
84: self.genes = generate_random_tree(init_depth,init_type)
85: end
Replaces a random subtree by the result of its evaluation. value_hash is passed to eval_tree.
# File lib/charlie/tree/tree.rb, line 279
279: def TreeEvalMutator(value_hash=Hash.new{0})
280: Module.new{
281: self.name = "TreeEvalMutator(#{value_hash.inspect})"
282: define_method(:mutate!) {
283: st = random_subtree
284: st.replace [:term,eval_tree(st,value_hash)]
285: self
286: }
287: }
288: end
Tree genotype, for genetic programming etc.
# File lib/charlie/tree/tree.rb, line 73
73: def TreeGenotype(terminals, unary_ops, binary_ops, init_depth = 3, init_type = :half)
74: unary_ops = nil if unary_ops.empty?
75: Class.new(Genotype) {
76:
77: define_method(:unary_ops) {unary_ops }
78: define_method(:binary_ops) {binary_ops}
79: define_method(:terminals) {terminals }
80: define_method(:init_depth) {init_depth}
81: define_method(:init_type) {init_type }
82:
83: def initialize
84: self.genes = generate_random_tree(init_depth,init_type)
85: end
86:
87: def genes=(g)
88: class << g # ensures a genes.dup call is a deep copy
89: def dup
90: GPTreeHelper.dup_tree(self)
91: end
92: end
93: @genes = g
94: end
95:
96: def size
97: tree_size(@genes)
98: end
99:
100: def depth
101: tree_depth(@genes)
102: end
103:
104:
105: # Generates a random tree.
106: # * type = grow uses generate_random_tree_grow
107: # * type = full uses generate_random_tree_full
108: # * type = half uses one of them, with 50% probability each.
109: def generate_random_tree(depth, type=:half)
110: if type==:full || ( type == :half && rand < 0.5 )
111: generate_random_tree_full(depth)
112: else
113: generate_random_tree_grow(depth,true)
114: end
115: end
116:
117: # Generates a random tree of a certain maximum depth.
118: # * <tt>no_term=true</tt> makes sure the root node is a function.
119: def generate_random_tree_grow(depth,no_term=nil)
120: if depth.zero? || (rand(3).zero? && !no_term)
121: e = terminals.at_rand
122: [:term, e.is_a?(Proc) ? e.call : e]
123: else
124: if unary_ops.nil? || rand(2).zero?
125: [binary_ops.at_rand,generate_random_tree_grow(depth-1),generate_random_tree_grow(depth-1)]
126: else
127: [unary_ops.at_rand,generate_random_tree_grow(depth-1)]
128: end
129: end
130: end
131:
132: # Generates a random tree, with all terminals at 'depth'.
133: def generate_random_tree_full(depth)
134: if depth.zero?
135: e = terminals.at_rand
136: [:term, e.is_a?(Proc) ? e.call : e]
137: else
138: if unary_ops.nil? || rand(2).zero?
139: [binary_ops.at_rand,generate_random_tree_full(depth-1),generate_random_tree_full(depth-1)]
140: else
141: [unary_ops.at_rand,generate_random_tree_full(depth-1)]
142: end
143: end
144: end
145:
146: def eval_genes(terminals_value_hash = {})
147: eval_tree(@genes,terminals_value_hash)
148: end
149:
150: def to_s
151: @genes.inspect
152: end
153:
154: use PCross(0.7,TreeCrossover), PMutate(0.5,TreeReplaceMutator) # TODO: test what options are best -- benchmark show that these are ok for simple settings.
155:
156: # make helper functions available at both class and instance level
157: include GPTreeHelper
158: class << self
159: include GPTreeHelper
160: end
161: }
162: end
mutates a random numeric terminal using a point mutator (cf. ListMutator) or a block (e.g. {|x| x-rand+0.5}
# File lib/charlie/tree/tree.rb, line 255
255: def TreeNumTerminalMutator(mutate=:uniform[0.1], &b)
256: if block_given?
257: mutate_proc = b
258: else
259: mut_name, *mut_arg = mutate
260: mut_fn = PointMutators[mut_name]
261: mutate_proc = proc{|x| mut_fn.call(x,*mut_arg) }
262: end
263:
264: Module.new{
265: self.name = "TreeNumTerminalMutator(#{mutate.inspect})"
266: define_method(:mutate!) {
267: numterms = all_terminals.select{|x| x[1].is_a? Numeric }
268: unless numterms.empty?
269: random_term = numterms.at_rand
270: random_term[1] = mutate_proc.call(random_term[1])
271: end
272: self
273: }
274: }
275: end
TreeReplaceMutator replaces a randomly chosen subtree with a new, randomly generated, subtree.
# File lib/charlie/tree/tree.rb, line 183
183: def TreeReplaceMutator(depth=2,type=:half)
184: Module.new{
185: self.name = "TreeReplaceMutator(#{depth.inspect},#{type.inspect})"
186: if depth.is_a? Numeric
187: define_method(:mutate!) {
188: random_subtree.replace generate_random_tree(depth,type)
189: self
190: }
191: elsif depth==:same || (depth.is_a?(Array) && depth[0]==:same)
192: s, dd_min, dd_max = *depth
193: possible_deltas = (dd_min||0..dd_max||0).to_a
194: define_method(:mutate!) {
195: st = random_subtree
196: st.replace generate_random_tree([tree_depth(st) + possible_deltas.at_rand,0].max, type)
197: self
198: }
199: elsif depth.respond_to?(:to_a)
200: possible_depths = depth.to_a
201: define_method(:mutate!) {
202: random_subtree.replace generate_random_tree(possible_depths.at_rand,type)
203: self
204: }
205: else
206: raise ArgumentError, "invalid option for depth"
207: end
208: }
209: end
# File lib/charlie/tree/tree.rb, line 146
146: def eval_genes(terminals_value_hash = {})
147: eval_tree(@genes,terminals_value_hash)
148: end
Generates a random tree.
# File lib/charlie/tree/tree.rb, line 109
109: def generate_random_tree(depth, type=:half)
110: if type==:full || ( type == :half && rand < 0.5 )
111: generate_random_tree_full(depth)
112: else
113: generate_random_tree_grow(depth,true)
114: end
115: end
Generates a random tree, with all terminals at ‘depth’.
# File lib/charlie/tree/tree.rb, line 133
133: def generate_random_tree_full(depth)
134: if depth.zero?
135: e = terminals.at_rand
136: [:term, e.is_a?(Proc) ? e.call : e]
137: else
138: if unary_ops.nil? || rand(2).zero?
139: [binary_ops.at_rand,generate_random_tree_full(depth-1),generate_random_tree_full(depth-1)]
140: else
141: [unary_ops.at_rand,generate_random_tree_full(depth-1)]
142: end
143: end
144: end
Generates a random tree of a certain maximum depth.
# File lib/charlie/tree/tree.rb, line 119
119: def generate_random_tree_grow(depth,no_term=nil)
120: if depth.zero? || (rand(3).zero? && !no_term)
121: e = terminals.at_rand
122: [:term, e.is_a?(Proc) ? e.call : e]
123: else
124: if unary_ops.nil? || rand(2).zero?
125: [binary_ops.at_rand,generate_random_tree_grow(depth-1),generate_random_tree_grow(depth-1)]
126: else
127: [unary_ops.at_rand,generate_random_tree_grow(depth-1)]
128: end
129: end
130: end