Changeset 10335 for branches/HeuristicLab.Problems.GPDL/CodeGenerator
- Timestamp:
- 01/14/14 12:40:26 (11 years ago)
- Location:
- branches/HeuristicLab.Problems.GPDL/CodeGenerator
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/HeuristicLab.Problems.GPDL/CodeGenerator/BruteForceCodeGen.cs
r10086 r10335 2 2 using System.Collections.Generic; 3 3 using System.Diagnostics; 4 using System.IO;5 4 using System.Linq; 6 using System.Text;7 5 using HeuristicLab.Grammars; 8 using Attribute = HeuristicLab.Grammars.Attribute;9 6 10 7 namespace CodeGenerator { 11 8 public class BruteForceCodeGen { 12 13 private string usings = @"14 using System.Collections.Generic;15 using System.Linq;16 using System;17 ";18 19 9 private string solverTemplate = @" 20 10 namespace ?PROBLEMNAME? { 21 11 public sealed class ?IDENT?Solver { 22 12 13 private static Dictionary<int, int[]> transition = new Dictionary<int, int[]>() { 14 ?TRANSITIONTABLE? 15 }; 16 private static Dictionary<int, int> subtreeCount = new Dictionary<int, int>() { 17 { -1, 0 }, // terminals 18 ?SUBTREECOUNTTABLE? 19 }; 20 21 private static string[] symb = new string[] { ?SYMBOLNAMES? }; 22 private static bool[] isTerminal = new bool[] { ?TERMINALTABLE? }; 23 private static bool[] hasAlternatives = new bool[] {?HASALTERNATIVESTABLE? }; 24 25 26 public sealed class SolverState : ISolverState { 27 private class Node { 28 public int state; 29 public int count; 30 public List<Node> nodes; 31 public bool done; 32 private int nextAlt; 33 public Node(int state) { 34 this.state = state; 35 nodes = new List<Node>(transition[state].Length); 36 if (!hasAlternatives[state]) { 37 foreach (var t in transition[state]) { nodes.Add(new Node(t)); } 38 } 39 } 40 41 public int GetNextAlternative() { 42 System.Diagnostics.Debug.Assert(hasAlternatives[state]); 43 if(nodes.Count == 0 && !isTerminal[state]) { 44 foreach(var targetState in transition[state]) nodes.Add(new Node(targetState)); 45 nextAlt = 0; 46 // begin with a terminal if possible 47 if(!isTerminal[nodes[nextAlt].state]) 48 do { nextAlt = (nextAlt + 1) % nodes.Count; } while(nextAlt != 0 /* full circle*/ && !isTerminal[nodes[nextAlt].state]); 49 } 50 51 throw new NotImplementedException(); // TODO: handle terminal classes correctly 52 return nextAlt; 53 } 54 55 public void Update() { 56 count++; 57 if(hasAlternatives[state] && !isTerminal[state]) { 58 nodes[GetNextAlternative()].Update(); 59 // check if all nodes done and set this node to done 60 if(nodes.All(n=>n.done)) { 61 this.done = true; 62 } else { 63 // must not walk nodes that are done 64 do { nextAlt = (nextAlt + 1) % nodes.Count; } while (nodes[nextAlt].done); 65 } 66 } else { 67 if(isTerminal[state]) { 68 throw new NotImplementedException(); // TODO: handle terminal classes correctly 69 done = (nextAlt + 1) >= GetAlternativesCount(symb[state]); 70 } else { 71 // update the smallest count. this basically means keeping all sub-trees the same and only altering the updated one 72 Node minNode = nodes.First(); 73 foreach(var node in nodes.Skip(1)) { 74 if(node.count < minNode.count) { 75 minNode = node; 76 } 77 } 78 minNode.Update(); 79 if(nodes.All(n=>n.done)) this.done = true; 80 } 81 } 82 } 83 } 84 85 86 ?ALTERNATIVESCOUNTMETHOD? 87 88 89 public int curDepth; 90 public int steps; 91 public int depth; 92 private readonly Stack<Node> nodes; 93 private readonly IGpdlProblem problem; 94 private readonly Node searchTree; 95 96 public SolverState(IGpdlProblem problem) { 97 this.problem = problem; 98 nodes = new Stack<Node>(); 99 searchTree = new Node(0); 100 nodes.Push(searchTree); 101 } 102 103 public void Reset() { 104 System.Diagnostics.Debug.Assert(nodes.Count == 1); 105 steps = 0; 106 depth = 0; 107 curDepth = 0; 108 } 109 110 public int PeekNextAlternative() { 111 var curNode = nodes.Peek(); 112 System.Diagnostics.Debug.Assert(hasAlternatives[curNode.state]); 113 return curNode.GetNextAlternative(); 114 } 115 116 public void Follow(int idx) { 117 steps++; 118 curDepth++; 119 depth = Math.Max(depth, curDepth); 120 var curNode = nodes.Peek(); 121 if(hasAlternatives[curNode.state]) { 122 nodes.Push(curNode.nodes[curNode.GetNextAlternative()]); 123 } else { 124 nodes.Push(curNode.nodes[idx]); 125 } 126 } 127 128 public void Unwind() { 129 nodes.Pop(); 130 curDepth--; 131 } 132 133 public void Update() { 134 searchTree.Update(); 135 } 136 } 137 23 138 public static void Main(string[] args) { 24 var solver = new ?IDENT?Solver(); 139 var problem = new ?IDENT?Problem(); 140 var solver = new ?IDENT?Solver(problem); 25 141 solver.Start(); 26 142 } 27 28 public ?IDENT?Solver() { 29 Initialize(); 30 } 31 32 private void Initialize() { 33 ?INITCODE? 143 144 private readonly ?IDENT?Problem problem; 145 public ?IDENT?Solver(?IDENT?Problem problem) { 146 this.problem = problem; 147 } 148 149 private void Start() { 150 var bestF = ?MAXIMIZATION? ? double.NegativeInfinity : double.PositiveInfinity; 151 int n = 0; 152 long sumDepth = 0; 153 long sumSize = 0; 154 var sumF = 0.0; 155 var sw = new System.Diagnostics.Stopwatch(); 156 sw.Start(); 157 var _state = new SolverState(problem); 158 while (true) { 159 160 var f = problem.Evaluate(_state); 161 _state.Update(); 162 163 n++; 164 sumSize += _state.steps; 165 sumDepth += _state.depth; 166 sumF += f; 167 if (IsBetter(f, bestF)) { 168 // evaluate again with tracing to console 169 // problem.Evaluate(new SolverState(_state.seed, true)); 170 bestF = f; 171 Console.WriteLine(""{0}\t{1}\t(size={2}, depth={3})"", n, bestF, _state.steps, _state.depth); 172 } 173 if (n % 1000 == 0) { 174 sw.Stop(); 175 Console.WriteLine(""{0}\tbest: {1:0.000}\t(avg: {2:0.000})\t(avg size: {3:0.0})\t(avg. depth: {4:0.0})\t({5:0.00} sols/ms)"", n, bestF, sumF/1000.0, sumSize/1000.0, sumDepth/1000.0, 1000.0 / sw.ElapsedMilliseconds); 176 sw.Reset(); 177 sumSize = 0; 178 sumDepth = 0; 179 sumF = 0.0; 180 sw.Start(); 181 } 182 } 34 183 } 35 184 … … 37 186 return ?MAXIMIZATION? ? a > b : a < b; 38 187 } 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 alternative78 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-trees85 foreach (var t in subTrees) t.UpdatePath();86 }87 // set node to done if all nodes are non-null and done88 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 tree103 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 path112 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 188 } 135 189 }"; 136 190 137 191 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)); 192 public void Generate(IGrammar grammar, bool maximization, SourceBuilder problemSourceCode) { 193 var solverSourceCode = new SourceBuilder(); 194 solverSourceCode.Append(solverTemplate) 195 .Replace("?MAXIMIZATION?", maximization.ToString().ToLowerInvariant()) 196 .Replace("?SYMBOLNAMES?", grammar.Symbols.Select(s => s.Name).Aggregate(string.Empty, (str, symb) => str + "\"" + symb + "\", ")) 197 .Replace("?TERMINALTABLE?", grammar.Symbols.Select(grammar.IsTerminal).Aggregate(string.Empty, (str, b) => str + b.ToString().ToLowerInvariant() + ", ")) 198 .Replace("?HASALTERNATIVESTABLE?", grammar.Symbols.Select(s => grammar.IsNonTerminal(s) && (grammar.NumberOfAlternatives(s) > 1)).Aggregate(string.Empty, (str, b) => str + b.ToString().ToLowerInvariant() + ", ")) 199 .Replace("?TRANSITIONTABLE?", GenerateTransitionTable(grammar)) 200 .Replace("?SUBTREECOUNTTABLE?", GenerateSubtreeCountTable(grammar)) 201 .Replace("?ALTERNATIVESCOUNTMETHOD?", GenerateAlternativesCountMethod(grammar)) 202 ; 203 204 problemSourceCode.Append(solverSourceCode.ToString()); 205 } 206 207 #region same as random search 208 private string GenerateTransitionTable(IGrammar grammar) { 209 Debug.Assert(grammar.Symbols.First().Equals(grammar.StartSymbol)); 210 var sb = new SourceBuilder(); 211 212 // state idx = idx of the corresponding symbol in the grammar 213 var allSymbols = grammar.Symbols.ToList(); 214 var attributes = new List<string>(); 215 foreach (var s in grammar.Symbols) { 216 var targetStates = new List<int>(); 217 if (grammar.IsTerminal(s)) { 218 foreach (var att in s.Attributes) { 219 targetStates.Add(allSymbols.Count + attributes.Count); 220 attributes.Add(s.Name + "_" + att); 221 } 222 } else { 223 if (grammar.NumberOfAlternatives(s) > 1) { 224 foreach (var alt in grammar.GetAlternatives(s)) { 225 // only single-symbol alternatives are supported 226 Debug.Assert(alt.Count() == 1); 227 targetStates.Add(allSymbols.IndexOf(alt.Single())); 228 } 229 } else { 230 // rule is a sequence of symbols 231 var seq = grammar.GetAlternatives(s).Single(); 232 targetStates.AddRange(seq.Select(symb => allSymbols.IndexOf(symb))); 233 } 234 } 235 236 var targetStateString = targetStates.Aggregate(string.Empty, (str, state) => str + state + ", "); 237 238 var idxOfSourceState = allSymbols.IndexOf(s); 239 sb.AppendFormat("// {0}", s).AppendLine(); 240 sb.AppendFormat("{{ {0} , new int[] {{ {1} }} }},", idxOfSourceState, targetStateString).AppendLine(); 241 } 242 for (int attIdx = 0; attIdx < attributes.Count; attIdx++) { 243 sb.AppendFormat("// {0}", attributes[attIdx]).AppendLine(); 244 sb.AppendFormat("{{ {0} , new int[] {{ }} }},", attIdx + allSymbols.Count).AppendLine(); 229 245 } 230 246 return sb.ToString(); 231 247 } 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); 248 private string GenerateSubtreeCountTable(IGrammar grammar) { 249 Debug.Assert(grammar.Symbols.First().Equals(grammar.StartSymbol)); 250 var sb = new SourceBuilder(); 251 252 // state idx = idx of the corresponding symbol in the grammar 253 var allSymbols = grammar.Symbols.ToList(); 254 var attributes = new List<string>(); 255 foreach (var s in grammar.Symbols) { 256 int subtreeCount; 257 if (grammar.IsTerminal(s)) { 258 subtreeCount = s.Attributes.Count(); 259 attributes.AddRange(s.Attributes.Select(att => s.Name + "_" + att.Name)); 260 } else { 261 if (grammar.NumberOfAlternatives(s) > 1) { 262 Debug.Assert(grammar.GetAlternatives(s).All(alt => alt.Count() == 1)); 263 subtreeCount = 1; 264 } else { 265 subtreeCount = grammar.GetAlternative(s, 0).Count(); 266 } 267 } 268 269 sb.AppendFormat("// {0}", s).AppendLine(); 270 sb.AppendFormat("{{ {0} , {1} }},", allSymbols.IndexOf(s), subtreeCount).AppendLine(); 271 } 272 273 for (int attIdx = 0; attIdx < attributes.Count; attIdx++) { 274 sb.AppendFormat("// {0}", attributes[attIdx]).AppendLine(); 275 sb.AppendFormat("{{ {0} , 1 }},", attIdx + allSymbols.Count).AppendLine(); 245 276 } 246 277 return sb.ToString(); 247 278 } 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 } 279 #endregion 280 private string GenerateAlternativesCountMethod(IGrammar grammar) { 281 Debug.Assert(grammar.Symbols.First().Equals(grammar.StartSymbol)); 282 var sb = new SourceBuilder(); 283 sb.Append("public int GetAlternativesCount(string symbol, string attribute = null) {").BeginBlock(); 284 sb.AppendLine("switch(symbol) {"); 285 foreach (var s in grammar.Symbols) { 286 sb.AppendFormat("case \"{0}\":", s.Name).AppendLine(); 287 int altCount; 288 if (grammar.IsTerminal(s)) { 289 if (s.Attributes.Any()) { 290 sb.Append("switch(attribute) {").BeginBlock(); 291 foreach (var att in s.Attributes) { 292 sb.AppendFormat("case \"{1}\": return problem.GetCardinality(\"{0}\", \"{1}\");", s.Name, att.Name).AppendLine(); 293 } 294 sb.Append("} // switch").EndBlock(); 295 } else { 296 sb.AppendLine("return 0;"); 297 } 298 } else { 299 sb.AppendFormat("return {0};", grammar.NumberOfAlternatives(s)).AppendLine(); 300 } 301 302 303 } 304 sb.AppendLine("} // switch"); 305 sb.Append("}").EndBlock(); 277 306 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 attributes283 if (!s.Attributes.Any())284 sb.AppendFormat("private void {0}(SearchTree tree) {{", s.Name);285 else286 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 parameter290 int i = 0;291 foreach (var element in s.Attributes) {292 // read next symbol293 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 else305 sb.AppendFormat("private void {0}(SearchTree tree, {1}) {{", s.Name, s.GetAttributeString());306 307 // generate local definitions308 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 alternative313 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 alternative322 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 tree328 // 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 methods352 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 else360 return string.Format("{0}(subtree, {1});", s.Name, s.GetAttributeString());361 }362 307 } 363 308 } -
branches/HeuristicLab.Problems.GPDL/CodeGenerator/ProblemCodeGen.cs
r10100 r10335 18 18 private const string problemTemplate = @" 19 19 namespace ?PROBLEMNAME? { 20 public interface IGpdlProblem { 21 int GetCardinality(string terminalSymbol, string attribute); 22 } 20 21 // generic interface for communication from problem interpretation to solver 23 22 public interface ISolverState { 24 23 void Reset(); 25 int PeekNextAlternative(); 26 void Follow(int idx); 27 void Unwind(); 24 int PeekNextAlternative(); // in alternative nodes returns the index of the alternative that should be followed 25 void Follow(int idx); // next: derive the NT symbol with index=idx; 26 void Unwind(); // finished with deriving the NT symbol 28 27 } 29 28 30 public sealed class ?IDENT?Problem : IGpdlProblem{29 public sealed class ?IDENT?Problem { 31 30 32 private readonly Dictionary<string, Dictionary<string, int>> cardinalities = new Dictionary<string, Dictionary<string, int>>();33 31 public ?IDENT?Problem() { 34 32 Initialize(); 35 36 ?CONSTRUCTORSOURCE?37 38 33 } 39 34 40 35 private void Initialize() { 36 // the following is the source code from the INIT section of the problem definition 37 #region INIT section 41 38 ?INITSOURCE? 39 #endregion 42 40 } 43 41 … … 45 43 public double Evaluate(ISolverState _state) { 46 44 this._state = _state; 45 #region objective function (MINIMIZE / MAXIMIZE section) 47 46 ?FITNESSFUNCTION? 48 } 49 47 #endregion 48 } 49 50 // additional code from the problem definition (CODE section) 51 #region additional code 50 52 ?ADDITIONALCODE? 51 53 #endregion 54 55 #region generated source for interpretation 52 56 ?INTERPRETERSOURCE? 53 57 #endregion 58 59 #region generated code for the constraints for terminals 54 60 ?CONSTRAINTSSOURCE? 55 56 public int GetCardinality(string terminal, string attribute) { 57 return cardinalities[terminal][attribute]; 58 } 61 #endregion 59 62 } 60 63 }"; … … 76 79 .Replace("?IDENT?", ast.Name); 77 80 78 79 81 // write the source file to disk 80 82 using (var stream = new StreamWriter(ast.Name + ".cs")) { … … 88 90 .AppendLine(problemTemplate) 89 91 .Replace("?FITNESSFUNCTION?", ast.FitnessFunctionNode.SrcCode) 90 .Replace("?CONSTRUCTORSOURCE?", GenerateConstructorSource(ast))91 92 .Replace("?INITSOURCE?", ast.InitCodeNode.SrcCode) 92 93 .Replace("?ADDITIONALCODE?", ast.ClassCodeNode.SrcCode) … … 99 100 var grammar = CreateGrammarFromAst(ast); 100 101 var randomSearchCodeGen = new RandomSearchCodeGen(); 101 randomSearchCodeGen.Generate(grammar, ast.FitnessFunctionNode.Maximization, ast.Terminals, solverSourceCode); 102 randomSearchCodeGen.Generate(grammar, ast.Terminals.OfType<TerminalNode>(), ast.FitnessFunctionNode.Maximization, solverSourceCode); 103 //var bruteForceSearchCodeGen = new BruteForceCodeGen(); 104 //bruteForceSearchCodeGen.Generate(grammar, ast.FitnessFunctionNode.Maximization, solverSourceCode); 102 105 } 103 106 104 107 #region create grammar instance from AST 108 // should be refactored so that we can directly query the AST 105 109 private AttributedGrammar CreateGrammarFromAst(GPDefNode ast) { 106 110 … … 131 135 private IEnumerable<IAttribute> GetSymbolAttributes(string formalParameters) { 132 136 return (from fieldDef in Util.ExtractParameters(formalParameters) 133 select new Attribute(fieldDef.Identifier, fieldDef.Type, AttributeType.Parse(fieldDef.RefOrOut))) .134 ToList();137 select new Attribute(fieldDef.Identifier, fieldDef.Type, AttributeType.Parse(fieldDef.RefOrOut))) 138 .ToList(); 135 139 } 136 140 … … 178 182 sb.AppendFormat("public {0} GetMax{1}_{2}() {{ return {3}; }}", fieldType, t.Ident, c.Ident, c.RangeMaxExpression).AppendLine(); 179 183 sb.AppendFormat("public {0} GetMin{1}_{2}() {{ return {3}; }}", fieldType, t.Ident, c.Ident, c.RangeMinExpression).AppendLine(); 180 // sb.AppendFormat("public {0} Get{1}_{2}(ISolverState _state) {{ return _state.random.NextDouble() * (GetMax{1}_{2}() - GetMin{1}_{2}()) + GetMin{1}_{2}(); }}", fieldType, t.Ident, c.Ident).AppendLine(); 181 sb.AppendFormat("public {0} Get{1}_{2}(ISolverState _state) {{ throw new NotSupportedException(\"range constraints for terminals are not supported.\"); }}", fieldType, t.Ident, c.Ident).AppendLine(); 184 //sb.AppendFormat("public {0} Get{1}_{2}(ISolverState _state) {{ _state. }}", fieldType, t.Ident, c.Ident, ) 182 185 } else if (c.Type == ConstraintNodeType.Set) { 183 186 sb.AppendFormat("public IEnumerable<{0}> GetAllowed{1}_{2}() {{ return {3}; }}", fieldType, t.Ident, c.Ident, c.SetExpression).AppendLine(); 184 sb.AppendFormat("private readonly {0}[] values_{1}_{2};", fieldType, t.Ident, c.Ident).AppendLine(); 185 sb.AppendFormat("public {0} Get{1}_{2}(ISolverState _state) {{ return values_{1}_{2}[_state.PeekNextAlternative()]; }}", fieldType, t.Ident, c.Ident).AppendLine(); 186 } 187 } 188 } 189 private string GenerateConstructorSource(GPDefNode ast) { 190 var sb = new SourceBuilder(); 191 // generate code to initialize the tables for terminals 192 foreach (var t in ast.Terminals.OfType<TerminalNode>()) { 193 if (t.Constraints.Any()) { 194 foreach (var constraint in t.Constraints) { 195 sb.AppendFormat("values_{0}_{1} = GetAllowed{0}_{1}().ToArray();", t.Ident, constraint.Ident); 196 } 197 sb.AppendFormat("cardinalities[\"{0}\"] = new Dictionary<string, int>() {{ ", t.Ident); 198 foreach (var constraint in t.Constraints) { 199 sb.AppendFormat("{{ \"{1}\", values_{0}_{1}.Length }}, ", t.Ident, constraint.Ident); 200 } 201 sb.Append("};").AppendLine(); 202 } 203 } 204 return sb.ToString(); 205 } 206 207 187 } 188 } 189 } 208 190 #endregion 209 191 210 192 private string GenerateInterpreterSource(AttributedGrammar grammar) { 211 193 var sb = new SourceBuilder(); 194 GenerateInterpreterStart(grammar, sb); 212 195 213 196 // generate methods for all nonterminals and terminals using the grammar instance … … 221 204 } 222 205 206 private void GenerateInterpreterStart(AttributedGrammar grammar, SourceBuilder sb) { 207 var s = grammar.StartSymbol; 208 // create the method which can be called from the fitness function 209 if (!s.Attributes.Any()) 210 sb.AppendFormat("private void {0}() {{", s.Name).BeginBlock(); 211 else 212 sb.AppendFormat("private void {0}({1}) {{", s.Name, s.GetAttributeString()).BeginBlock(); 213 214 // get formal parameters of start symbol 215 var attr = s.Attributes; 216 217 // actual parameter are the same as formalparameter only without type identifier 218 string actualParameter; 219 if (attr.Any()) 220 actualParameter = attr.Skip(1).Aggregate(attr.First().AttributeType + " " + attr.First().Name, (str, a) => str + ", " + a.AttributeType + " " + a.Name); 221 else 222 actualParameter = string.Empty; 223 sb.AppendLine("_state.Reset();"); 224 sb.AppendFormat("{0}(_state, {1});", s.Name, actualParameter).AppendLine(); 225 sb.AppendLine("}").EndBlock(); 226 } 227 223 228 private void GenerateInterpreterMethod(AttributedGrammar g, ISymbol s, SourceBuilder sb) { 224 // if this is the start symbol we additionally have to create the method which can be called from the fitness function225 if (g.StartSymbol.Equals(s)) {226 if (!s.Attributes.Any())227 sb.AppendFormat("private void {0}() {{", s.Name).BeginBlock();228 else229 sb.AppendFormat("private void {0}({1}) {{", s.Name, s.GetAttributeString()).BeginBlock();230 231 // get formal parameters of start symbol232 var attr = g.StartSymbol.Attributes;233 234 // actual parameter are the same as formalparameter only without type identifier235 string actualParameter;236 if (attr.Any())237 actualParameter = attr.Skip(1).Aggregate(attr.First().AttributeType + " " + attr.First().Name, (str, a) => str + ", " + a.AttributeType + " " + a.Name);238 else239 actualParameter = string.Empty;240 sb.AppendLine("_state.Reset();");241 sb.AppendFormat("{0}(_state, {1});", g.StartSymbol.Name, actualParameter).AppendLine();242 sb.AppendLine("}").EndBlock();243 }244 245 229 if (!s.Attributes.Any()) 246 230 sb.AppendFormat("private void {0}(ISolverState _state) {{", s.Name).BeginBlock(); … … 272 256 sb.AppendFormat("case {0}: {{ ", i).BeginBlock(); 273 257 274 // this only works for alternatives with a single non-terminal symbol (ignoring semantic symbols) so far!258 // this only works for alternatives with a single non-terminal symbol (ignoring semantic symbols)! 275 259 // a way to handle this is through grammar transformation (the examplary grammars all have the correct from) 276 260 Debug.Assert(alt.Count(symb => !(symb is SemanticSymbol)) == 1); … … 295 279 } 296 280 297 private stringGenerateTerminalInterpreterMethod(ISymbol s, SourceBuilder sb) {281 private void GenerateTerminalInterpreterMethod(ISymbol s, SourceBuilder sb) { 298 282 // if the terminal symbol has attributes then we must samples values for these attributes 299 283 if (!s.Attributes.Any()) … … 310 294 } 311 295 sb.Append("}").EndBlock(); 312 return sb.ToString();313 296 } 314 297 } -
branches/HeuristicLab.Problems.GPDL/CodeGenerator/RandomSearchCodeGen.cs
r10100 r10335 3 3 using System.Diagnostics; 4 4 using System.Linq; 5 using System.Text; 5 6 using HeuristicLab.Grammars; 6 7 … … 13 14 private static double baseTerminalProbability = 0.05; // 5% of all samples are only a terminal node 14 15 private static double terminalProbabilityInc = 0.05; // for each level the probability to sample a terminal grows by 5% 16 17 private static Dictionary<int, int[]> transition = new Dictionary<int, int[]>() { 18 ?TRANSITIONTABLE? 19 }; 20 private static Dictionary<int, int> subtreeCount = new Dictionary<int, int>() { 21 { -1, 0 }, // terminals 22 ?SUBTREECOUNTTABLE? 23 }; 24 private static string[] symb = new string[] { ?SYMBOLNAMES? }; 25 15 26 16 27 public sealed class SolverState : ISolverState { … … 24 35 } 25 36 } 37 38 ?TERMINALNODEDEFINITIONS? 39 26 40 public int curDepth; 27 41 public int steps; 28 42 public int depth; 29 43 private readonly Stack<Tree> nodes; 30 private readonly IGpdlProblem problem; 31 32 private static Dictionary<int, int[]> transition = new Dictionary<int, int[]>() { 33 ?TRANSITIONTABLE? 34 }; 35 private static Dictionary<int, int> subtreeCount = new Dictionary<int, int>() { 36 { -1, 0 }, // terminals 37 ?SUBTREECOUNTTABLE? 38 }; 39 private static string[] symb = new string[] { ?SYMBOLNAMES? }; 40 41 public SolverState(IGpdlProblem problem, int seed) { 44 private readonly ?IDENT?Problem problem; 45 46 public SolverState(?IDENT?Problem problem, int seed) { 42 47 this.problem = problem; 43 48 this.nodes = new Stack<Tree>(); … … 59 64 depth = Math.Max(depth, curDepth); 60 65 var t = new Tree(state, altIdx); 66 61 67 // t.symbol = symb.Length > state ? symb[state] : ""TERM""; 62 68 // if the symbol has alternatives then we must choose one randomly (only one sub-tree in this case) … … 173 179 sumF += f; 174 180 if (IsBetter(f, bestF)) { 175 // evaluate again with tracing to console176 // problem.Evaluate(new SolverState(_state.seed, true));177 181 bestF = f; 178 182 Console.WriteLine(""{0}\t{1}\t(size={2}, depth={3})"", n, bestF, _state.steps, _state.depth); … … 196 200 }"; 197 201 198 public void Generate(IGrammar grammar, bool maximization, IEnumerable<SymbolNode> terminalSymbols, SourceBuilder problemSourceCode) {202 public void Generate(IGrammar grammar, IEnumerable<TerminalNode> terminals, bool maximization, SourceBuilder problemSourceCode) { 199 203 var solverSourceCode = new SourceBuilder(); 200 204 solverSourceCode.Append(solverTemplate) 205 // .Replace("?TERMINALFIELDS?", GenerateTerminalFields(grammar)) 206 .Replace("?TERMINALNODEDEFINITIONS?", GenerateTerminalNodeDefinitions(terminals)) 207 // .Replace("?CONSTRUCTORCODE?", GenerateConstructorCode(grammar)) 201 208 .Replace("?MAXIMIZATION?", maximization.ToString().ToLowerInvariant()) 202 209 .Replace("?SYMBOLNAMES?", grammar.Symbols.Select(s => s.Name).Aggregate(string.Empty, (str, symb) => str + "\"" + symb + "\", ")) … … 204 211 .Replace("?SUBTREECOUNTTABLE?", GenerateSubtreeCountTable(grammar)) 205 212 .Replace("?SAMPLEALTERNATIVECODE?", GenerateSampleAlternativeSource(grammar)) 213 // .Replace("?SAMPLETERMINALCODE?", GenerateSampleTerminalSource(grammar)) 206 214 ; 207 215 … … 209 217 } 210 218 219 private string GenerateTerminalNodeDefinitions(IEnumerable<TerminalNode> terminals) { 220 var sb = new SourceBuilder(); 221 222 foreach (var terminal in terminals) { 223 GenerateTerminalNodeDefinitions(terminal, sb); 224 } 225 return sb.ToString(); 226 } 227 228 private void GenerateTerminalNodeDefinitions(TerminalNode terminal, SourceBuilder sb) { 229 sb.AppendFormat("private class {0}Tree {{", terminal.Ident).BeginBlock(); 230 foreach (var att in terminal.FieldDefinitions) { 231 sb.AppendFormat("{0} {1};", att.Type, att.Identifier).AppendLine(); 232 } 233 sb.AppendFormat(" static {0}Tree Create(Random random, ?IDENT?Problem problem) {{", terminal.Ident).BeginBlock(); 234 sb.AppendFormat("var t = new {0}Tree();", terminal.Ident); 235 foreach (var constr in terminal.Constraints) { 236 if (constr.Type == ConstraintNodeType.Set) { 237 sb.AppendLine("{").BeginBlock(); 238 sb.AppendFormat(" var elements = problem.GetAllowed{0}_{1}().ToArray();", terminal.Ident, constr.Ident); 239 sb.AppendFormat("t.{0} = elements[random.Next(elements.Length)]; ", constr.Ident).AppendLine(); 240 sb.AppendLine("}").EndBlock(); 241 } else { 242 sb.AppendLine("{").BeginBlock(); 243 sb.AppendFormat(" var max = problem.GetMax{0}_{1}();", terminal.Ident, constr.Ident); 244 sb.AppendFormat(" var min = problem.GetMin{0}_{1}();", terminal.Ident, constr.Ident); 245 sb.AppendFormat("t.{0} = random.NextDouble() * (max - min) + min ", constr.Ident).AppendLine(); 246 sb.AppendLine("}").EndBlock(); 247 } 248 } 249 sb.AppendLine("return t;"); 250 sb.AppendLine("}").EndBlock(); 251 sb.AppendLine("}").EndBlock(); 252 } 253 254 //private string GenerateSampleTerminalSource(IGrammar grammar) { 255 // StringBuilder sb = new StringBuilder(); 256 // foreach (var t in grammar.TerminalSymbols) { 257 // sb.AppendFormat("public void {0}(ISolverState _state, {1}) {{", t.Name, t.GetAttributeString()).AppendLine(); 258 // foreach (var att in t.Attributes) { 259 // // need constraints 260 // sb.AppendFormat("{0}", att.Name); 261 // } 262 // sb.AppendLine("}"); 263 // } 264 // return sb.ToString(); 265 //} 211 266 212 267 … … 287 342 var sb = new SourceBuilder(); 288 343 int stateCount = 0; 289 var attributes = new List<Tuple<string, string>>();290 344 foreach (var s in grammar.Symbols) { 291 345 sb.AppendFormat("case {0}: ", stateCount++); 292 346 if (grammar.IsTerminal(s)) { 293 GenerateReturnStatement(s.Attributes.Count(), sb); 294 attributes.AddRange(s.Attributes.Select(att => Tuple.Create(s.Name, att.Name))); 347 // ignore 295 348 } else { 296 349 var terminalAltIndexes = grammar.GetAlternatives(s) … … 315 368 } 316 369 } 317 for (int attIdx = 0; attIdx < attributes.Count; attIdx++) {318 var terminalName = attributes[attIdx].Item1;319 var attributeName = attributes[attIdx].Item2;320 sb.AppendFormat("case {0}: return random.Next(problem.GetCardinality(\"{1}\", \"{2}\"));", attIdx + stateCount, terminalName, attributeName).AppendLine();321 }322 370 return sb.ToString(); 323 371 }
Note: See TracChangeset
for help on using the changeset viewer.