Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Problems.GPDL/CodeGenerator/BruteForceCodeGen.cs @ 10074

Last change on this file since 10074 was 10074, checked in by gkronber, 11 years ago

#2026: finished tree searcher for artifical ant, multiplexer and even parity examples. (+ performance tweak in Artificial Ant.txt)

File size: 14.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.IO;
5using System.Linq;
6using System.Text;
7using System.Threading.Tasks;
8using HeuristicLab.Grammars;
9using Attribute = HeuristicLab.Grammars.Attribute;
10
11namespace CodeGenerator {
12  public class BruteForceCodeGen {
13
14    private string usings = @"
15using System.Collections.Generic;
16using System.Linq;
17using System;
18";
19
20    private string solverTemplate = @"
21namespace ?PROBLEMNAME? {
22  public sealed class ?IDENT?Solver {
23
24    public static void Main(string[] args) {
25      var solver = new ?IDENT?Solver();
26      solver.Start();
27    }
28
29    public ?IDENT?Solver() {
30      Initialize();
31    }   
32
33    private void Initialize() {
34      ?INITCODE?
35    }
36
37    private bool IsBetter(double a, double b) {
38      return ?MAXIMIZATION? ? a > b : a < b;
39    }
40
41    private class SearchTree {
42      private SearchTree[] subTrees;
43      public bool Done { get; private set; }
44      private int nextAlternative;
45
46      public SearchTree() {
47        Done = false;
48      }
49
50      public SearchTree GetSubtree(int i) {
51        // if (subTrees[i] == null) subTrees[i] = new SearchTree();
52        return subTrees[i];
53      }
54
55      public int GetNextAlternative() {
56        System.Diagnostics.Debug.Assert(IsAlternativeNode);
57        return nextAlternative;
58      }
59
60      public bool IsUninitialized {
61        get { return subTrees == null; }
62      }
63
64      public void SetNumberOfSubtrees(int n) {
65        this.subTrees = new SearchTree[n];
66        for(int i=0;i<n;i++) this.subTrees[i] = new SearchTree();
67        nextAlternative = 0;
68      }
69
70      public void UpdatePath() {
71        System.Diagnostics.Debug.Assert(!Done);
72        if (IsUninitialized) { return; }
73        Count++;
74        if (subTrees.Length == 0) {
75          Done = true;
76        }
77        if (IsAlternativeNode) {
78          // update only the active alternative and calculate a new alternative
79          subTrees[nextAlternative].UpdatePath();
80
81          do {
82            nextAlternative = (nextAlternative + 1) % subTrees.Length;
83          } while (subTrees[nextAlternative] != null && subTrees[nextAlternative].Done);
84
85          // from the nodes which are not done take the first with minimum count
86          //nextAlternative = (from i in Enumerable.Range(0, subTrees.Length)
87          //                   where subTrees[i] == null || !subTrees[i].Done
88          //                   orderby subTrees[i]==null?0:subTrees[i].Count ascending
89          //                   select i).FirstOrDefault(); // possibly there are no children left that can be tried
90        } else {
91          // for sequenceNodes update all sub-trees
92          foreach (var t in subTrees) t.UpdatePath();
93        }
94        // set node to done if all nodes are non-null and done
95        this.Done = true;
96        foreach(var t in subTrees) {
97          if(t == null || !t.Done)
98            { this.Done = false; break; }
99        }       
100      }
101
102      public bool IsAlternativeNode { get; set; }
103      public bool IsSequenceNode { get; set; }
104      public int Count { get; private set; }
105    }
106
107    private SearchTree tree;
108    private void Start() {
109      // start with empty tree
110      tree = new SearchTree();
111      var bestF = ?MAXIMIZATION? ? double.NegativeInfinity : double.PositiveInfinity;
112      int n = 0;
113      var sw = new System.Diagnostics.Stopwatch();
114      sw.Start();
115      while (!tree.Done) {
116
117        var f = Calculate();
118        // do one more run through the tree to update the path
119        tree.UpdatePath();
120
121        n++;
122        if (IsBetter(f, bestF)) bestF = f;
123        if (n % 100 == 0) {
124          sw.Stop();
125          Console.WriteLine(""{0}\t{1}\t{2}\t({3:0.00} sols/ms)"", n, bestF, f, 100.0 / sw.ElapsedMilliseconds);
126          sw.Reset();
127          sw.Start();
128        }
129      }
130    }
131
132    public double Calculate() {
133      ?FITNESSFUNCTION?
134    }
135
136    ?ADDITIONALCODE?
137
138    ?INTERPRETERSOURCE?
139
140    ?CONSTRAINTSSOURCE?
141  }
142}";
143
144
145    /// <summary>
146    /// Generates the source code for a brute force searcher that can be compiled with a C# compiler
147    /// </summary>
148    /// <param name="ast">An abstract syntax tree for a GPDL file</param>
149    public void Generate(GPDefNode ast) {
150      var problemSourceCode = new StringBuilder();
151      problemSourceCode.AppendLine(usings);
152
153      GenerateProblem(ast, problemSourceCode);
154
155      problemSourceCode.Replace("?PROBLEMNAME?", ast.Name);
156
157      // write the source file to disk
158      using (var stream = new StreamWriter(ast.Name + ".cs")) {
159        stream.WriteLine(problemSourceCode.ToString());
160      }
161    }
162
163    private void GenerateProblem(GPDefNode ast, StringBuilder problemSourceCode) {
164      var grammar = CreateGrammarFromAst(ast);
165      var problemClassCode =
166        solverTemplate
167          .Replace("?MAXIMIZATION?", ast.FitnessFunctionNode.Maximization.ToString().ToLowerInvariant())
168          .Replace("?IDENT?", ast.Name)
169          .Replace("?FITNESSFUNCTION?", ast.FitnessFunctionNode.SrcCode)
170          .Replace("?INTERPRETERSOURCE?", GenerateInterpreterSource(grammar))
171          .Replace("?INITCODE?", ast.InitCodeNode.SrcCode)
172          .Replace("?ADDITIONALCODE?", ast.ClassCodeNode.SrcCode)
173          .Replace("?CONSTRAINTSSOURCE?", GenerateConstraintMethods(ast.Terminals))
174          ;
175
176      problemSourceCode.AppendLine(problemClassCode).AppendLine();
177    }
178
179    private AttributedGrammar CreateGrammarFromAst(GPDefNode ast) {
180      string startSymbolName = ast.Rules.First().NtSymbol;
181      var startSymbolNode = ast.NonTerminals.Single(nt => nt.Ident == startSymbolName);
182      // create startSymbol
183      var g = new AttributedGrammar(new Symbol(startSymbolName, ParseSymbolAttributes(startSymbolNode.FormalParameters)));
184      foreach (var rule in ast.Rules) {
185        // create nt-symbol
186        var ntSymbolName = rule.NtSymbol;
187        var ntSymbolNode = ast.NonTerminals.Single(nt => nt.Ident == ntSymbolName);
188        var attributes = ParseSymbolAttributes(ntSymbolNode.FormalParameters);
189        var ntSymbol = new Symbol(ntSymbolName, attributes);
190        foreach (var alt in GetAlternatives(rule.Alternatives)) {
191          g.AddProductionRule(ntSymbol, alt);
192        }
193        // local initialization code
194        if (!string.IsNullOrEmpty(rule.LocalCode)) g.AddLocalDefinitions(ntSymbol, rule.LocalCode);
195      }
196      return g;
197    }
198
199    private IEnumerable<IAttribute> ParseSymbolAttributes(string formalParameters) {
200      return (from fieldDef in Util.ExtractParameters(formalParameters)
201              select new Attribute(fieldDef.Identifier, fieldDef.Type, AttributeType.Parse(fieldDef.RefOrOut))).
202        ToList();
203    }
204
205    private IEnumerable<Sequence> GetAlternatives(AlternativesNode altNode) {
206      foreach (var alt in altNode.Alternatives) {
207        yield return GetSequence(alt.Sequence);
208      }
209    }
210
211    private Sequence GetSequence(IEnumerable<RuleExprNode> sequence) {
212      Debug.Assert(sequence.All(s => s is CallSymbolNode || s is RuleActionNode));
213      var l = new List<ISymbol>();
214      foreach (var node in sequence) {
215        var callSymbolNode = node as CallSymbolNode;
216        var actionNode = node as RuleActionNode;
217        if (callSymbolNode != null) {
218          l.Add(new Symbol(callSymbolNode.Ident, ParseSymbolAttributes(callSymbolNode.ActualParameter)));
219        } else if (actionNode != null) {
220          l.Add(new SemanticSymbol("SEM", actionNode.SrcCode));
221        }
222      }
223      return new Sequence(l);
224    }
225
226    // produces helper methods for the attributes of all terminal nodes
227    private string GenerateConstraintMethods(List<SymbolNode> symbols) {
228      var sb = new StringBuilder();
229      var terminals = symbols.OfType<TerminalNode>();
230      foreach (var t in terminals) {
231        sb.AppendLine(GenerateConstraintMethods(t));
232      }
233      return sb.ToString();
234    }
235
236    // generates helper methods for the attributes of a given terminal node
237    private string GenerateConstraintMethods(TerminalNode t) {
238      var sb = new StringBuilder();
239      foreach (var c in t.Constraints) {
240        var fieldType = t.FieldDefinitions.First(d => d.Identifier == c.Ident).Type;
241        if (c.Type == ConstraintNodeType.Range) {
242          sb.AppendFormat("public {0} GetMax{1}_{2}() {{ return {3}; }}", fieldType, t.Ident, c.Ident, c.RangeMaxExpression).AppendLine();
243          sb.AppendFormat("public {0} GetMin{1}_{2}() {{ return {3}; }}", fieldType, t.Ident, c.Ident, c.RangeMinExpression).AppendLine();
244        } else if (c.Type == ConstraintNodeType.Set) {
245          sb.AppendFormat("public {0}[] GetAllowed{1}_{2}() {{ return {3}.ToArray(); }}", fieldType, t.Ident, c.Ident, c.SetExpression).AppendLine();
246        }
247        sb.AppendFormat("public {0} Get{1}_{2}Element(int i) {{ return GetAllowed{1}_{2}()[i]; }}", fieldType, t.Ident, c.Ident);
248      }
249      return sb.ToString();
250    }
251
252    private string GenerateInterpreterSource(AttributedGrammar grammar) {
253      var sb = new StringBuilder();
254
255
256      // find formal parameters of root node
257      var attr = grammar.StartSymbol.Attributes;
258
259      var formalParameter = grammar.StartSymbol.GetAttributeString();
260      // actual parameter are the same as formalparameter only without type identifier
261      string actualParameter;
262      if (attr.Any())
263        actualParameter = attr.Skip(1).Aggregate(attr.First().AttributeType + " " + attr.First().Name, (str, a) => str + ", " + a.AttributeType + " " + a.Name);
264      else
265        actualParameter = string.Empty;
266
267      // generate entry method for evaluation. This is called from the min/max function
268      // e.g.: ProgramRoot(ref int a0) { ProgramRoot(rootNode , ref a0); }
269      sb.AppendFormat("void {0}({1}) {{", grammar.StartSymbol.Name, formalParameter).AppendLine();
270      sb.AppendFormat("{0}(tree, {1});", grammar.StartSymbol.Name, actualParameter).AppendLine();
271      sb.AppendLine("}");
272
273      // generate methods for all nonterminals and terminals using the grammar instance
274      foreach (var s in grammar.NonTerminalSymbols) {
275        sb.AppendLine(GenerateInterpreterMethod(grammar, s));
276      }
277      foreach (var s in grammar.TerminalSymbols) {
278        sb.AppendLine(GenerateTerminalInterpreterMethod(s));
279      }
280      return sb.ToString();
281    }
282
283    private string GenerateTerminalInterpreterMethod(ISymbol s) {
284      var sb = new StringBuilder();
285      // if the terminal symbol has attributes then we must create values for these attributes
286      if (!s.Attributes.Any())
287        sb.AppendFormat("private void {0}(SearchTree tree) {{", s.Name);
288      else
289        sb.AppendFormat("private void {0}(SearchTree tree, {1}) {{", s.Name, s.GetAttributeString());
290
291      sb.AppendFormat("if (tree.IsUninitialized) {{ tree.SetNumberOfSubtrees({0}); tree.IsSequenceNode = true; }}", s.Attributes.Count());
292      // each field must match a formal parameter, assign a value for each parameter
293      int i = 0;
294      foreach (var element in s.Attributes) {
295        // read next symbol
296        sb.AppendLine("throw new NotImplementedException(); // this is not yet supported ");
297        sb.AppendFormat("{0} = Get{1}_{0}Element(tree.GetSubTree({2}).GetAlternative())", element.Name, s.Name, i++).AppendLine(";");
298      }
299      sb.AppendLine("}");
300      return sb.ToString();
301    }
302
303    private string GenerateInterpreterMethod(AttributedGrammar g, ISymbol s) {
304      var sb = new StringBuilder();
305      if (!s.Attributes.Any())
306        sb.AppendFormat("private void {0}(SearchTree tree) {{", s.Name);
307      else
308        sb.AppendFormat("private void {0}(SearchTree tree, {1}) {{", s.Name, s.GetAttributeString());
309
310      // generate local definitions
311      sb.AppendLine(g.GetLocalDefinitions(s));
312
313
314
315      // make sure we first descent into terminal alternatives -> we just order by number of non-terminals in the alternative
316      var altsWithSemActions = g.GetAlternativesWithSemanticActions(s).OrderBy(alt => alt.Count(symb => g.IsNonTerminal(symb))).ToArray();
317
318      if (altsWithSemActions.Length > 1) {
319        sb.AppendFormat("      if (tree.IsUninitialized) {{ tree.SetNumberOfSubtrees({0}); tree.IsAlternativeNode = true; }}", altsWithSemActions.Length);
320
321        int i = 0;
322        sb.AppendLine("switch(tree.GetNextAlternative()) {");
323        // generate a case for each alternative
324        foreach (var alt in altsWithSemActions) {
325          sb.AppendFormat("case {0}: {{ ", i).AppendLine();
326          sb.AppendLine("SearchTree subtree = null; ");
327
328          // this only works for alternatives with a single non-terminal symbol (ignoring semantic symbols) so far!
329          // handling multiple non-terminals here would require a change in the structure of the search tree
330          // another way to handle this is through grammar transformation (the examplary grammars all have the correct from)
331          Debug.Assert(alt.Count(symb => !(symb is SemanticSymbol)) == 1);
332          foreach (var altSymb in alt) {
333            if (!(altSymb is SemanticSymbol)) sb.AppendFormat("subtree = tree.GetSubtree({0}); ", i);
334            sb.AppendLine(GenerateSourceForAction(altSymb));
335          }
336          i++;
337          sb.AppendLine("break;").AppendLine("}");
338        }
339        sb.AppendLine("default: throw new System.InvalidOperationException();").AppendLine("}");
340      } else {
341        sb.AppendFormat("      if (tree.IsUninitialized) {{ tree.SetNumberOfSubtrees({0}); tree.IsSequenceNode = true; }}", altsWithSemActions.Single().Count(symb => !(symb is SemanticSymbol)));
342        int j = 0;
343        sb.AppendLine("SearchTree subtree = null; ");
344        foreach (var altSymb in altsWithSemActions.Single()) {
345          if (!(altSymb is SemanticSymbol)) sb.AppendFormat("subtree = tree.GetSubtree({0}); ", j++);
346          sb.AppendLine(GenerateSourceForAction(altSymb));
347        }
348      }
349      sb.AppendLine("}");
350      return sb.ToString();
351    }
352
353    // helper for generating calls to other symbol methods
354    private string GenerateSourceForAction(ISymbol s) {
355      var action = s as SemanticSymbol;
356      if (action != null) {
357        return action.Code + ";";
358      } else {
359        if (!s.Attributes.Any())
360          return string.Format("{0}(subtree);", s.Name);
361        else
362          return string.Format("{0}(subtree, {1});", s.Name, s.GetAttributeString());
363      }
364    }
365  }
366}
Note: See TracBrowser for help on using the repository browser.