Class: Operator::MutationSimplify
- Inherits:
-
Object
- Object
- Operator::MutationSimplify
- Defined in:
- ../lib/mutation_simplify.rb
Overview
Algebraic simplification inspired by: citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.96.7953
MutationSimplify checks if the genotype-phenotype mapping tree contains the specific patterns and if it finds one it replaces the according part of the genotype with a new part folowing certain rules. The motivation is that the phenotype which represents the algebraic expression can be often simplified by algebraic rules eg.:
A * 1 -> A
A * B + A * C -> A * (B + C)
sin(0) -> 0
...
etc.
This process incorporates a domain knowledge into the evolutionary process an may help to lower the average complexity of phenotypes. It can be seen as the kind of mutation so it is compatible with the mutation operator interface. The description of the patterns and replacements is stored in the yaml file with the syntax resembling ABNF grammar. MutationSimplify currently supports only DepthFirst and DepthLocus mappers.
The rules are stored in the YAML format file and read by the filename= method. The YAML file represents the array of modification rules, each rule is the YAML object which has the mandatory attributes (pattern, replacement) and can have optional attributes (comment, lambdas). Pattern and replacement attributes are arrays of derivation rules matching the grammar of the GE process. Symbols in pattern array have to be qualified in the form:
symbol.instance
The rule may have the right-hand side (it then represents a single derivation rule from the grammar) or the RHS may be omitted (the symbol.instance left-hand side then represents the subtree defined in the pattern section). If there are two occurences of the same subtree instances then both subtrees must match. See the example of the format: sample/toy_regression/simplify.yaml
Defined Under Namespace
Classes: Expansion, RuleCase, Subtree
Instance Attribute Summary (collapse)
-
- (Object) mapper_type
must be set to DepthFirst or DepthLocus.
-
- (Object) rules
Array of RuleCase structures representing the modification rules.
Instance Method Summary (collapse)
- - (Object) alt_idx2text(symbol, alt_idx)
- - (Object) exp_args(track_reloc, matching, ptm)
-
- (Object) filename=(fname)
Reads the modification rules from the YAML file.
-
- (MutationSimplify) initialize(grammar)
constructor
Create the simplification operator.
- - (Object) match(track_reloc, patterns)
- - (Object) match_patterns(track_reloc, patterns, node_idx)
-
- (Object) mutation(parent, track)
Perform the mutation according the modification rules.
- - (Object) nodes_equal(track_reloc, node1_idx, node2_idx)
- - (Object) parse_depths(patterns, refs, uses)
- - (Object) parse_equals(refs)
- - (Object) parse_lambdas(texts, patterns, refs)
- - (Object) parse_pattern(texts)
- - (Object) parse_replacement(texts, refs, lambdas)
- - (Object) parse_rules(input)
- - (Object) reloc(track)
- - (Object) replace(genome, ptm, patterns, replacement, track_reloc)
- - (Object) text2alt_idx(symbol, text, hash = @grammar_texts)
- - (Object) wrapped_copy(parent, track)
Constructor Details
- (MutationSimplify) initialize(grammar)
Create the simplification operator. grammar is the valid grammar compatible with the simplification rules.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File '../lib/mutation_simplify.rb', line 45 def initialize grammar @grammar = grammar @rules = [] @mapper_type = nil @grammar_texts = {} @grammar_texts2 = {} @grammar.each_pair do |symbol, alt| rule = {} rule2 = {} (0...alt.size).each do |i| rule[ alt_idx2text( symbol, i ) ] = i rule2[ alt2text( symbol, i ) ] = i end @grammar_texts[symbol] = rule @grammar_texts2[symbol] = rule2 end end |
Instance Attribute Details
- (Object) mapper_type
must be set to DepthFirst or DepthLocus. Other mapper types are not supported.
70 71 72 |
# File '../lib/mutation_simplify.rb', line 70 def mapper_type @mapper_type end |
- (Object) rules
Array of RuleCase structures representing the modification rules. See the class description for details.
67 68 69 |
# File '../lib/mutation_simplify.rb', line 67 def rules @rules end |
Instance Method Details
- (Object) alt_idx2text(symbol, alt_idx)
196 197 198 |
# File '../lib/mutation_simplify.rb', line 196 def alt_idx2text( symbol, alt_idx ) (@grammar[symbol][alt_idx].map {|t| t.type == :symbol ? "<#{t.data}>" : t.data }).join end |
- (Object) exp_args(track_reloc, matching, ptm)
185 186 187 188 189 190 191 192 193 194 |
# File '../lib/mutation_simplify.rb', line 185 def exp_args( track_reloc, matching, ptm ) res = [] ptm.each_with_index do |node_idx,patt_idx| patt = matching[patt_idx] next unless patt.respond_to? 'alt_idx' next unless patt.alt_idx.nil? res << alt_idx2text( patt.symbol, track_reloc[node_idx].alt_idx ) end res end |
- (Object) filename=(fname)
Reads the modification rules from the YAML file. See the class description for details.
74 75 76 |
# File '../lib/mutation_simplify.rb', line 74 def filename= fname @rules = parse_rules( YAML::load( File.open(fname) ) ) end |
- (Object) match(track_reloc, patterns)
101 102 103 104 105 106 107 108 |
# File '../lib/mutation_simplify.rb', line 101 def match( track_reloc, patterns ) track_reloc.each_with_index do |node, index| next unless match_node?( node, patterns.first ) ptm = match_patterns( track_reloc, patterns, index ) return ptm unless ptm.empty? end [] end |
- (Object) match_patterns(track_reloc, patterns, node_idx)
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File '../lib/mutation_simplify.rb', line 110 def match_patterns( track_reloc, patterns, node_idx ) ptx = [node_idx] stack = [node_idx] patterns[1...patterns.size].each do |pattern| found = track_reloc.find { |n| n.back == stack.last and n.loc_idx == pattern.parent_arg } return [] if found.nil? return [] if pattern.kind_of?(Expansion) and not match_node?( found, pattern ) node_idx = track_reloc.index( found ) ptx << node_idx case pattern.dir when -1 stack.push node_idx when 0 else pattern.dir.times { stack.pop } end end ptx end |
- (Object) mutation(parent, track)
Perform the mutation according the modification rules. See the class description for details. parent is the original genotype. track is the genotype-prenotype process track (see mappers descriptions). Return the mutated copy of the orig. genotype.
82 83 84 85 86 87 88 89 90 91 92 93 |
# File '../lib/mutation_simplify.rb', line 82 def mutation( parent, track ) mutant = wrapped_copy( parent, track ) track_reloc = reloc(track) @rules.each do |rule| ptm = match( track_reloc, rule.match ) next if ptm.empty? next unless check_equals( track_reloc, rule.equals, ptm ) return replace( mutant, ptm, rule.match, rule.outcome, track_reloc ) end mutant end |
- (Object) nodes_equal(track_reloc, node1_idx, node2_idx)
175 176 177 178 179 180 181 182 183 |
# File '../lib/mutation_simplify.rb', line 175 def nodes_equal( track_reloc, node1_idx, node2_idx ) tree1 = load_tree( track_reloc, node1_idx ) tree2 = load_tree( track_reloc, node2_idx ) return false if tree1.size != tree2.size tree1.each_with_index do |idx1,i| return false unless match_node?( track_reloc[idx1], track_reloc[tree2[i]] ) end true end |
- (Object) parse_depths(patterns, refs, uses)
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File '../lib/mutation_simplify.rb', line 250 def parse_depths( patterns, refs, uses ) stack = [ DepthStack.new( [refs.first], 0, 0 ) ] patterns.each_with_index do |pattern, index| raise "MutationSimplify: parsing error" if stack.empty? current_symb = stack.last.symbols.shift raise "MutationSimplify: wrong order: required '#{refs[index]}', declared '#{current_symb}'" if current_symb != refs[index] current_depth = stack.last.depth pattern.parent_arg = stack.last.loc stack.last.loc += 1 stack.pop if stack.last.symbols.empty? expansions = uses[index].clone stack << DepthStack.new( expansions, current_depth-1, 0 ) unless expansions.empty? pattern.dir = stack.empty? ? -current_depth : stack.last.depth - current_depth end raise "MutationSimplify: some symbols undefined" unless stack.empty? end |
- (Object) parse_equals(refs)
311 312 313 314 315 316 317 318 319 |
# File '../lib/mutation_simplify.rb', line 311 def parse_equals refs res = [] refs.each_with_index do |ref,idx| for i in idx+1 ... refs.size res << [idx, i] if ref == refs[i] end end res end |
- (Object) parse_lambdas(texts, patterns, refs)
271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File '../lib/mutation_simplify.rb', line 271 def parse_lambdas( texts, patterns, refs ) lambdas = {} var_idxs = [] patterns.each_with_index { |p,i| var_idxs << i if p.alt_idx.nil? } texts.each_pair do |key,value| text = "lambda do |a|\n#{value.clone}\nend" var_idxs.each_with_index { |v,i| text.gsub!( refs[v], "a[#{i}]" ) } lambdas[key] = eval(text) end lambdas end |
- (Object) parse_pattern(texts)
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File '../lib/mutation_simplify.rb', line 220 def parse_pattern texts patterns = [] refs = [] uses = [] texts.each_with_index do |text,index| symb_dot,rule = text.split('=') symb_dot.strip! symb,dot = symb_dot.split('.') raise "MutationSimplify: '#{symb_dot}' must follow symbol.var syntax" if dot.nil? if rule.nil? # Subtree patterns << Subtree.new() uses << [] else # Expansion raise "MutationSimplify: '#{symb_dot}' is defined more times" unless refs.index(symb_dot).nil? uses << rule.scan(/[a-zA-Z_\-][a-zA-Z0-9_\-]*\.[a-zA-Z_\-][a-zA-Z0-9_\-]*/) rule = rule.strip.gsub(/\.[a-zA-Z_\-][a-zA-Z0-9_\-]*/,'') alt_idx = (rule == '?') ? nil : text2alt( symb, rule ) patterns << Expansion.new( symb, alt_idx ) end refs << symb_dot end return [patterns, refs, uses] end |
- (Object) parse_replacement(texts, refs, lambdas)
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File '../lib/mutation_simplify.rb', line 285 def parse_replacement( texts, refs, lambdas ) replacement = [] texts.each do |line| idx = refs.index line.strip if idx.nil? symb,rule = line.split('=') raise "MutationSimplify: replacement '#{line}' unknown" if rule.nil? symb.strip! rule.strip! all,fn = /([a-zA-Z_\-][a-zA-Z0-9_\-]*)\(\)/.match(rule).to_a if fn.nil? alt_idx = text2alt( symb, rule ) else raise "MutationSimplify: unknown lambda '#{fn}()'" unless lambdas.has_key? fn alt_idx = lambdas[fn] end replacement << Expansion.new( symb, alt_idx ) else replacement << idx end end replacement end |
- (Object) parse_rules(input)
208 209 210 211 212 213 214 215 216 217 218 |
# File '../lib/mutation_simplify.rb', line 208 def parse_rules input rules = [] input.each do |rule_case| patterns, refs, uses = parse_pattern rule_case['pattern'] parse_depths( patterns, refs, uses ) lambdas = rule_case.has_key?('lambdas') ? parse_lambdas( rule_case['lambdas'], patterns, refs ) : [] replacement = parse_replacement( rule_case['replacement'], refs, lambdas ) rules << RuleCase.new(patterns,replacement,parse_equals(refs)) end rules end |
- (Object) reloc(track)
162 163 164 165 166 167 168 169 170 171 172 173 |
# File '../lib/mutation_simplify.rb', line 162 def reloc track case @mapper_type when 'DepthFirst' reloc_first track when 'DepthLocus' reloc_locus track when nil raise "MutationSimplify: mapper_type not selected" else raise "MutationSimplify: mapper_type not supported" end end |
- (Object) replace(genome, ptm, patterns, replacement, track_reloc)
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File '../lib/mutation_simplify.rb', line 135 def replace( genome, ptm, patterns, replacement, track_reloc ) args = exp_args( track_reloc, patterns, ptm ) root_node = track_reloc[ptm.first] res = genome[0 ... root_node.from].clone replacement.each_with_index do |replacing,i| if replacing.kind_of? Expansion res << (i==0 ? root_node.loc_idx : 0) if @mapper_type == 'DepthLocus' # TODO: different Codon types? if replacing.alt_idx.respond_to? 'call' out = replacing.alt_idx.call args return genome.clone if out.nil? res << text2alt_idx( replacing.symbol, out ) else res << replacing.alt_idx end else # replacing is the subtree index node = track_reloc[ ptm[replacing] ] subtree = genome[node.from .. node.to].clone subtree[0] = root_node.loc_idx if @mapper_type == 'DepthLocus' and i==0 # TODO: different Codon types? res.concat subtree end end res.concat genome[(root_node.to+1) ... genome.size] res end |
- (Object) text2alt_idx(symbol, text, hash = @grammar_texts)
200 201 202 203 204 205 206 |
# File '../lib/mutation_simplify.rb', line 200 def text2alt_idx( symbol, text, hash=@grammar_texts ) found = hash[symbol] raise "MutationSimplify: altidx for symbol '#{symbol}' not found" if found.nil? res = found[text] raise "MutationSimplify: altidx for RuleAlt '#{text}' (symbol '#{symbol}') not found" if res.nil? res end |
- (Object) wrapped_copy(parent, track)
95 96 97 98 99 |
# File '../lib/mutation_simplify.rb', line 95 def wrapped_copy( parent, track ) mutant = [] mutant += parent.clone while mutant.size < track.first.to+1 mutant end |