Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3105_PythonFormatter/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionPythonFormatter.cs @ 17855

Last change on this file since 17855 was 17855, checked in by dpiringe, 3 years ago

#3105

  • added a python formatter (implementation is based on the C# formatter)
File size: 10.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Globalization;
25using System.Linq;
26using System.Text;
27using HEAL.Attic;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
31
32namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
33  [Item("Python String Formatter", "String formatter for string representations of symbolic data analysis expressions in Python syntax.")]
34  [StorableType("37C1E1DD-437F-414B-AA96-9C6A0F6FEE46")]
35  public sealed class SymbolicDataAnalysisExpressionPythonFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
36
37    private int VariableCounter { get; set; } = 0;
38    private IDictionary<string, string> VariableMap = new Dictionary<string, string>();
39
40    [StorableConstructor]
41    private SymbolicDataAnalysisExpressionPythonFormatter(StorableConstructorFlag _) : base(_) { }
42    private SymbolicDataAnalysisExpressionPythonFormatter(SymbolicDataAnalysisExpressionPythonFormatter original, Cloner cloner) : base(original, cloner) { }
43    public SymbolicDataAnalysisExpressionPythonFormatter()
44      : base() {
45      Name = ItemName;
46      Description = ItemDescription;
47    }
48
49    public override IDeepCloneable Clone(Cloner cloner) => new SymbolicDataAnalysisExpressionPythonFormatter(this, cloner);
50
51    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
52      StringBuilder strBuilderModel = new StringBuilder();
53      FormatRecursively(symbolicExpressionTree.Root, strBuilderModel);
54      return $"{GenerateHeader()}{strBuilderModel}";
55    }
56
57    private string GenerateHeader() {
58      StringBuilder strBuilder = new StringBuilder();
59      GenerateImports(strBuilder);
60      GenerateIfThenElseSource(strBuilder);
61      GenerateModelComment(strBuilder);
62      GenerateModelEvaluationFunction(strBuilder);
63      return strBuilder.ToString();
64    }
65
66    private void GenerateImports(StringBuilder strBuilder) {
67      strBuilder.AppendLine("# imports");
68      strBuilder.AppendLine("import math");
69      strBuilder.AppendLine("import statistics");
70    }
71
72    private void GenerateIfThenElseSource(StringBuilder strBuilder) {
73      strBuilder.AppendLine("# condition helper function");
74      strBuilder.AppendLine("def evaluate_if(condition, then_path, else_path): ");
75      strBuilder.AppendLine("\tif condition:");
76      strBuilder.AppendLine("\t\treturn then_path");
77      strBuilder.AppendLine("\telse:");
78      strBuilder.AppendLine("\t\treturn else_path");
79    }
80
81    private void GenerateModelComment(StringBuilder strBuilder) {
82      strBuilder.AppendLine("# model");
83      strBuilder.AppendLine("\"\"\"");
84      foreach (var kvp in VariableMap) {
85        strBuilder.AppendLine($"{kvp.Key} = {kvp.Value}");
86      }
87      strBuilder.AppendLine("\"\"\"");
88    }
89
90    private void GenerateModelEvaluationFunction(StringBuilder strBuilder) {
91      strBuilder.Append("def evaluate(");
92      foreach (var kvp in VariableMap) {
93        strBuilder.Append($"{kvp.Value}");
94        if (kvp.Key != VariableMap.Last().Key)
95          strBuilder.Append(",");
96      }
97      strBuilder.AppendLine("):");
98      strBuilder.Append("\treturn ");
99    }
100
101    private void FormatRecursively(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
102      ISymbol symbol = node.Symbol;
103      if (symbol is ProgramRootSymbol) {
104        FormatRecursively(node.GetSubtree(0), strBuilder);
105      } else if (symbol is StartSymbol) {
106        FormatRecursively(node.GetSubtree(0), strBuilder);
107      } else if (symbol is Addition) {
108        FormatNode(node, strBuilder, infixSymbol: " + ");
109      } else if (symbol is And) {
110        FormatNode(node, strBuilder, infixSymbol: " and ");
111      } else if (symbol is Average) {
112        FormatNode(node, strBuilder, prefixSymbol: "statistics.mean", openingSymbol: "([", closingSymbol: "])");
113      } else if (symbol is Cosine) {
114        FormatNode(node, strBuilder, "math.cos");
115      } else if (symbol is Division) {
116        FormatDivision(node, strBuilder);
117      } else if (symbol is Exponential) {
118        FormatNode(node, strBuilder, "math.exp");
119      } else if (symbol is GreaterThan) {
120        FormatNode(node, strBuilder, infixSymbol: " > ");
121      } else if (symbol is IfThenElse) {
122        FormatNode(node, strBuilder, "evaluate_if");
123      } else if (symbol is LessThan) {
124        FormatNode(node, strBuilder, infixSymbol: " < ");
125      } else if (symbol is Logarithm) {
126        FormatNode(node, strBuilder, "math.log");
127      } else if (symbol is Multiplication) {
128        FormatNode(node, strBuilder, infixSymbol: " * ");
129      } else if (symbol is Not) {
130        FormatNode(node, strBuilder, "not");
131      } else if (symbol is Or) {
132        FormatNode(node, strBuilder, infixSymbol: " or ");
133      } else if (symbol is Xor) {
134        FormatNode(node, strBuilder, infixSymbol: " ^ ");
135      } else if (symbol is Sine) {
136        FormatNode(node, strBuilder, "math.sin");
137      } else if (symbol is Subtraction) {
138        FormatSubtraction(node, strBuilder);
139      } else if (symbol is Tangent) {
140        FormatNode(node, strBuilder, "math.tan");
141      } else if (symbol is HyperbolicTangent) {
142        FormatNode(node, strBuilder, "math.tanh");
143      } else if (symbol is Square) {
144        FormatPower(node, strBuilder, "2");
145      } else if (symbol is SquareRoot) {
146        FormatNode(node, strBuilder, "math.sqrt");
147      } else if (symbol is Cube) {
148        FormatPower(node, strBuilder, "3");
149      } else if (symbol is CubeRoot) {
150        FormatNode(node, strBuilder, closingSymbol: " ** (1. / 3))");
151      } else if (symbol is Power) {
152        FormatNode(node, strBuilder, "math.pow");
153      } else if (symbol is Root) {
154        FormatRoot(node, strBuilder);
155      } else if (symbol is Absolute) {
156        FormatNode(node, strBuilder, "abs");
157      } else if (symbol is AnalyticQuotient) {
158        strBuilder.Append("(");
159        FormatRecursively(node.GetSubtree(0), strBuilder);
160        strBuilder.Append(" / math.sqrt(1 + math.pow(");
161        FormatRecursively(node.GetSubtree(1), strBuilder);
162        strBuilder.Append(" , 2) ) )");
163      } else {
164        if (node is VariableTreeNode) {
165          FormatVariableTreeNode(node, strBuilder);
166        } else if (node is ConstantTreeNode) {
167          FormatConstantTreeNode(node, strBuilder);
168        } else {
169          throw new NotSupportedException("Formatting of symbol: " + symbol + " not supported for Python symbolic expression tree formatter.");
170        } 
171      }
172    }
173
174    private void FormatVariableTreeNode(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
175      var varNode = node as VariableTreeNode;
176      string variable;
177      if (!VariableMap.TryGetValue(varNode.VariableName, out variable)) {
178        variable = $"var{VariableCounter++}";
179        VariableMap.Add(varNode.VariableName, variable);
180      }
181      strBuilder.AppendFormat("{0} * {1}", variable, varNode.Weight.ToString("g17", CultureInfo.InvariantCulture));
182    }
183
184    private void FormatConstantTreeNode(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
185      var constNode = node as ConstantTreeNode;
186      strBuilder.Append(constNode.Value.ToString("g17", CultureInfo.InvariantCulture));
187    }
188
189    private void FormatPower(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, string exponent) {
190      strBuilder.Append("math.pow(");
191      FormatRecursively(node.GetSubtree(0), strBuilder);
192      strBuilder.Append($", {exponent})");
193    }
194
195    private void FormatRoot(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
196      strBuilder.Append("math.pow(");
197      FormatRecursively(node.GetSubtree(0), strBuilder);
198      strBuilder.Append(", 1.0 / (");
199      FormatRecursively(node.GetSubtree(1), strBuilder);
200      strBuilder.Append("))");
201    }
202
203    private void FormatNode(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, string prefixSymbol = "", string openingSymbol = "(", string closingSymbol = ")", string infixSymbol = ",") {
204      strBuilder.Append($"{prefixSymbol}{openingSymbol}");
205      foreach (var child in node.Subtrees) {
206        FormatRecursively(child, strBuilder);
207        if (child != node.Subtrees.Last())
208          strBuilder.Append(infixSymbol);
209      }
210      strBuilder.Append(closingSymbol);
211    }
212
213    private void FormatDivision(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
214      strBuilder.Append("(");
215      if (node.SubtreeCount == 1) {
216        strBuilder.Append("1.0 / ");
217        FormatRecursively(node.GetSubtree(0), strBuilder);
218      } else {
219        FormatRecursively(node.GetSubtree(0), strBuilder);
220        strBuilder.Append("/ (");
221        for (int i = 1; i < node.SubtreeCount; i++) {
222          if (i > 1) strBuilder.Append(" * ");
223          FormatRecursively(node.GetSubtree(i), strBuilder);
224        }
225        strBuilder.Append(")");
226      }
227      strBuilder.Append(")");
228    }
229
230    private void FormatSubtraction(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
231      if (node.SubtreeCount == 1) {
232        strBuilder.Append("-");
233        FormatRecursively(node.GetSubtree(0), strBuilder);
234        return;
235      }
236      //Default case: more than 1 child
237      FormatNode(node, strBuilder, infixSymbol: " - ");
238    }
239  }
240
241}
Note: See TracBrowser for help on using the repository browser.