Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3040_VectorBasedGP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs @ 17837

Last change on this file since 17837 was 17825, checked in by pfleck, 4 years ago

#3040 Merged trunk into branch.

File size: 11.9 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
32using DoubleVector = MathNet.Numerics.LinearAlgebra.Vector<double>;
33
34namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
35  public static class BaseInfixExpressionFormatter {
36    public static void FormatRecursively(ISymbolicExpressionTreeNode node, StringBuilder strBuilder,
37                                          NumberFormatInfo numberFormat, string formatString, List<KeyValuePair<string, double>> constants = null) {
38      if (node.SubtreeCount > 1) {
39        var token = GetToken(node.Symbol);
40        // operators
41        if (token == "+" || token == "-" || token == "OR" || token == "XOR" ||
42            token == "*" || token == "/" || token == "AND") {
43          strBuilder.Append("(");
44          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
45
46          foreach (var subtree in node.Subtrees.Skip(1)) {
47            strBuilder.Append(" ").Append(token).Append(" ");
48            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
49          }
50
51          strBuilder.Append(")");
52        } else if (token == "^") {
53          // handle integer powers directly
54          strBuilder.Append("(");
55          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
56
57          var power = node.GetSubtree(1);
58          if (power is ConstantTreeNode constNode && Math.Truncate(constNode.Value) == constNode.Value) {
59            strBuilder.Append(" ").Append(token).Append(" ").Append(constNode.Value.ToString(formatString, numberFormat));
60          } else {
61            strBuilder.Append(" ").Append(token).Append(" ");
62            FormatRecursively(power, strBuilder, numberFormat, formatString, constants);
63          }
64
65          strBuilder.Append(")");
66        } else {
67          // function with multiple arguments
68          strBuilder.Append(token).Append("(");
69          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
70          foreach (var subtree in node.Subtrees.Skip(1)) {
71            strBuilder.Append(", ");
72            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
73          }
74
75          strBuilder.Append(")");
76        }
77      } else if (node.SubtreeCount == 1) {
78        var token = GetToken(node.Symbol);
79        if (token == "-" || token == "NOT") {
80          strBuilder.Append("(").Append(token).Append("(");
81          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
82          strBuilder.Append("))");
83        } else if (token == "/") {
84          strBuilder.Append("1/");
85          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
86        } else if (token == "+" || token == "*") {
87          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
88        } else {
89          // function with only one argument
90          strBuilder.Append(token).Append("(");
91          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
92          strBuilder.Append(")");
93        }
94      } else {
95        // no subtrees
96        if (node.Symbol is LaggedVariable) {
97          var varNode = node as LaggedVariableTreeNode;
98          if (!varNode.Weight.IsAlmost(1.0)) {
99            strBuilder.Append("(");
100            AppendConstant(strBuilder, constants, varNode.Weight, formatString, numberFormat);
101            strBuilder.Append("*");
102          }
103
104          strBuilder.Append("LAG(");
105          AppendVariableName(strBuilder, varNode.VariableName);
106          strBuilder.Append(", ")
107                    .AppendFormat(numberFormat, "{0}", varNode.Lag)
108                    .Append(")");
109          if (!varNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
110        } else if (node.Symbol is Variable) {
111          var varNode = node as VariableTreeNode;
112          if (!varNode.Weight.IsAlmost(1.0)) {
113            strBuilder.Append("(");
114            AppendConstant(strBuilder, constants, varNode.Weight, formatString, numberFormat);
115            strBuilder.Append("*");
116          }
117          AppendVariableName(strBuilder, varNode.VariableName);
118          if (varNode.DataType == typeof(DoubleVector)) strBuilder.Append("{}");
119          if (!varNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
120        } else if (node.Symbol is FactorVariable) {
121          var factorNode = node as FactorVariableTreeNode;
122          AppendVariableName(strBuilder, factorNode.VariableName);
123
124          strBuilder.Append("[");
125          for (int i = 0; i < factorNode.Weights.Length; i++) {
126            if (i > 0) strBuilder.Append(", ");
127            AppendConstant(strBuilder, constants, factorNode.Weights[i], formatString, numberFormat);
128          }
129          strBuilder.Append("]");
130        } else if (node.Symbol is BinaryFactorVariable) {
131          var factorNode = node as BinaryFactorVariableTreeNode;
132          if (!factorNode.Weight.IsAlmost(1.0)) {
133            strBuilder.Append("(");
134            AppendConstant(strBuilder, constants, factorNode.Weight, formatString, numberFormat);
135
136            strBuilder.Append("*");
137          }
138
139          AppendVariableName(strBuilder, factorNode.VariableName);
140          strBuilder.Append(" = ");
141          AppendVariableName(strBuilder, factorNode.VariableValue);
142
143          if (!factorNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
144        } else if (node.Symbol is Constant) {
145          var constNode = node as ConstantTreeNode;
146          if (constants == null && constNode.Value < 0) {
147            strBuilder.Append("(").Append(constNode.Value.ToString(formatString, numberFormat))
148                      .Append(")"); // (-1
149          } else {
150            AppendConstant(strBuilder, constants, constNode.Value, formatString, numberFormat);
151          }
152        }
153      }
154    }
155
156    private static void AppendConstant(StringBuilder strBuilder, List<KeyValuePair<string, double>> constants, double value, string formatString, NumberFormatInfo numberFormat) {
157      if (constants != null) {
158        string constantKey = $"c_{constants.Count}";
159        strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", constantKey);
160        constants.Add(new KeyValuePair<string, double>(constantKey, value));
161      } else {
162        strBuilder.Append(value.ToString(formatString, numberFormat));
163      }
164    }
165
166    private static void AppendVariableName(StringBuilder strBuilder, string name) {
167      if (name.Contains("'"))
168        strBuilder.AppendFormat("\"{0}\"", name);
169      else
170        strBuilder.AppendFormat("'{0}'", name);
171    }
172
173    private static string GetToken(ISymbol symbol) {
174      var tok = InfixExpressionParser.knownSymbols.GetBySecond(symbol).FirstOrDefault();
175      if (tok == null)
176        throw new ArgumentException(string.Format("Unknown symbol {0} found.", symbol.Name));
177      return tok;
178    }
179  }
180
181  /// <summary>
182  /// Formats mathematical expressions in infix form. E.g. x1 * (3.0 * x2 + x3)
183  /// </summary>
184  [StorableType("6FE2C83D-A594-4ABF-B101-5AEAEA6D3E3D")]
185  [Item("Infix Symbolic Expression Tree Formatter",
186    "A string formatter that converts symbolic expression trees to infix expressions.")]
187  public sealed class InfixExpressionFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
188    [StorableConstructor]
189    private InfixExpressionFormatter(StorableConstructorFlag _) : base(_) { }
190
191    private InfixExpressionFormatter(InfixExpressionFormatter original, Cloner cloner) : base(original, cloner) { }
192
193    public InfixExpressionFormatter()
194      : base() {
195      Name = ItemName;
196      Description = ItemDescription;
197    }
198
199    public override IDeepCloneable Clone(Cloner cloner) {
200      return new InfixExpressionFormatter(this, cloner);
201    }
202
203    /// <summary>
204    /// Produces an infix expression for a given expression tree.
205    /// </summary>
206    /// <param name="symbolicExpressionTree">The tree representation of the expression.</param>
207    /// <param name="numberFormat">Number format that should be used for numeric parameters (e.g. NumberFormatInfo.InvariantInfo (default)).</param>
208    /// <param name="formatString">The format string for numeric parameters (e.g. \"G4\" to limit to 4 digits, default is \"G\")</param>
209    /// <returns>Infix expression</returns>
210    public string Format(ISymbolicExpressionTree symbolicExpressionTree, NumberFormatInfo numberFormat,
211                         string formatString = "G") {
212      // skip root and start symbols
213      StringBuilder strBuilder = new StringBuilder();
214      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
215        strBuilder, numberFormat, formatString);
216      return strBuilder.ToString();
217    }
218
219    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
220      return Format(symbolicExpressionTree, NumberFormatInfo.InvariantInfo);
221    }
222  }
223
224  [StorableType("54D917E8-134E-4066-9A60-2737C12D81DC")]
225  [Item("Infix String Formater", "Formatter for symbolic expressions, which produces an infix expression " +
226                                 "as well as a list of all coefficient values")]
227  public sealed class InfixExpressionStringFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
228    [StorableConstructor]
229    private InfixExpressionStringFormatter(StorableConstructorFlag _) : base(_) { }
230
231    private InfixExpressionStringFormatter(InfixExpressionStringFormatter original, Cloner cloner) : base(original, cloner) { }
232
233    public InfixExpressionStringFormatter() : base() {
234      Name = ItemName;
235      Description = ItemDescription;
236    }
237
238    public override IDeepCloneable Clone(Cloner cloner) {
239      return new InfixExpressionStringFormatter(this, cloner);
240    }
241
242    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
243      StringBuilder strBuilder = new StringBuilder();
244      var constants = new List<KeyValuePair<string, double>>();
245      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
246        strBuilder, NumberFormatInfo.InvariantInfo, "G", constants);
247      strBuilder.Append($"{Environment.NewLine}{Environment.NewLine}");
248
249      int maxDigits = GetDigits(constants.Count);
250      int padding = constants.Max(x => x.Value.ToString("F12", CultureInfo.InvariantCulture).Length);
251      foreach (var constant in constants) {
252        int digits = GetDigits(Int32.Parse(constant.Key.Substring(2)));
253        strBuilder.Append($"{constant.Key}{new String(' ', maxDigits - digits)} = " +
254                          string.Format($"{{0,{padding}:F12}}", constant.Value, CultureInfo.InvariantCulture) +
255                          Environment.NewLine);
256      }
257
258      return strBuilder.ToString();
259    }
260
261    private int GetDigits(int x) {
262      if (x == 0) return 1;
263      return (int)Math.Floor(Math.Log10(x) + 1);
264    }
265  }
266}
Note: See TracBrowser for help on using the repository browser.