Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs @ 17586

Last change on this file since 17586 was 17581, checked in by mkommend, 5 years ago

#2968: Merged formatter changes into trunk.

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