Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 10086 was 10086, checked in by gkronber, 9 years ago

#2026 worked on random search solver (now all examples are working)

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