Changeset 17581


Ignore:
Timestamp:
06/03/20 17:09:09 (2 years ago)
Author:
mkommend
Message:

#2968: Merged formatter changes into trunk.

Location:
trunk/HeuristicLab.Problems.DataAnalysis.Symbolic
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/HeuristicLab.Problems.DataAnalysis.Symbolic

  • trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs

    r17180 r17581  
    2121
    2222using System;
     23using System.Collections.Generic;
    2324using System.Globalization;
    2425using System.Linq;
    2526using System.Text;
     27using HEAL.Attic;
    2628using HeuristicLab.Common;
    2729using HeuristicLab.Core;
    2830using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
    29 using HEAL.Attic;
    3031
    3132namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
    32   /// <summary>
    33   /// Formats mathematical expressions in infix form. E.g. x1 * (3.0 * x2 + x3)
    34   /// </summary>
    35   [StorableType("6FE2C83D-A594-4ABF-B101-5AEAEA6D3E3D")]
    36   [Item("Infix Symbolic Expression Tree Formatter", "A string formatter that converts symbolic expression trees to infix expressions.")]
    37 
    38   public sealed class InfixExpressionFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
    39 
    40 
    41     [StorableConstructor]
    42     private InfixExpressionFormatter(StorableConstructorFlag _) : base(_) { }
    43     private InfixExpressionFormatter(InfixExpressionFormatter original, Cloner cloner) : base(original, cloner) { }
    44     public InfixExpressionFormatter()
    45       : base() {
    46       Name = ItemName;
    47       Description = ItemDescription;
    48     }
    49     public override IDeepCloneable Clone(Cloner cloner) {
    50       return new InfixExpressionFormatter(this, cloner);
    51     }
    52 
    53     /// <summary>
    54     /// Produces an infix expression for a given expression tree.
    55     /// </summary>
    56     /// <param name="symbolicExpressionTree">The tree representation of the expression.</param>
    57     /// <param name="numberFormat">Number format that should be used for numeric parameters (e.g. NumberFormatInfo.InvariantInfo (default)).</param>
    58     /// <param name="formatString">The format string for numeric parameters (e.g. \"G4\" to limit to 4 digits, default is \"G\")</param>
    59     /// <returns>Infix expression</returns>
    60     public string Format(ISymbolicExpressionTree symbolicExpressionTree, NumberFormatInfo numberFormat, string formatString="G") {
    61       // skip root and start symbols
    62       StringBuilder strBuilder = new StringBuilder();
    63       FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0), strBuilder, numberFormat, formatString);
    64       return strBuilder.ToString();
    65     }
    66 
    67     public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
    68       return Format(symbolicExpressionTree, NumberFormatInfo.InvariantInfo);
    69     }
    70 
    71     private static void FormatRecursively(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, NumberFormatInfo numberFormat, string formatString) {
     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) {
    7236      if (node.SubtreeCount > 1) {
    7337        var token = GetToken(node.Symbol);
     
    7741            token == "^") {
    7842          strBuilder.Append("(");
    79           FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString);
     43          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
    8044
    8145          foreach (var subtree in node.Subtrees.Skip(1)) {
    8246            strBuilder.Append(" ").Append(token).Append(" ");
    83             FormatRecursively(subtree, strBuilder, numberFormat, formatString);
    84           }
     47            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
     48          }
     49
    8550          strBuilder.Append(")");
    8651        } else {
    8752          // function with multiple arguments
    8853          strBuilder.Append(token).Append("(");
    89           FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString);
     54          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
    9055          foreach (var subtree in node.Subtrees.Skip(1)) {
    9156            strBuilder.Append(", ");
    92             FormatRecursively(subtree, strBuilder, numberFormat, formatString);
    93           }
     57            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
     58          }
     59
    9460          strBuilder.Append(")");
    9561        }
     
    9864        if (token == "-" || token == "NOT") {
    9965          strBuilder.Append("(").Append(token).Append("(");
    100           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
     66          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
    10167          strBuilder.Append("))");
    10268        } else if (token == "/") {
    10369          strBuilder.Append("1/");
    104           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
     70          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
    10571        } else if (token == "+" || token == "*") {
    106           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
     72          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
    10773        } else {
    10874          // function with only one argument
    10975          strBuilder.Append(token).Append("(");
    110           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
     76          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
    11177          strBuilder.Append(")");
    11278        }
     
    11783          if (!varNode.Weight.IsAlmost(1.0)) {
    11884            strBuilder.Append("(");
    119             strBuilder.Append(varNode.Weight.ToString(formatString, numberFormat));
     85            if (constants != null) {
     86              strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", varNode.Weight);
     87            } else {
     88              strBuilder.Append(varNode.Weight.ToString(formatString, numberFormat));
     89            }
    12090            strBuilder.Append("*");
    12191          }
     92
    12293          strBuilder.Append("LAG(");
    123           if (varNode.VariableName.Contains("'")) {
     94          if (varNode.VariableName.Contains("'"))
    12495            strBuilder.AppendFormat("\"{0}\"", varNode.VariableName);
    125           } else {
     96          else
    12697            strBuilder.AppendFormat("'{0}'", varNode.VariableName);
    127           }
     98
    12899          strBuilder.Append(", ")
    129             .AppendFormat(numberFormat, "{0}", varNode.Lag)
    130             .Append(")");
     100                    .AppendFormat(numberFormat, "{0}", varNode.Lag)
     101                    .Append(")");
    131102        } else if (node.Symbol is Variable) {
    132103          var varNode = node as VariableTreeNode;
    133104          if (!varNode.Weight.IsAlmost(1.0)) {
    134105            strBuilder.Append("(");
    135             strBuilder.Append(varNode.Weight.ToString(formatString, numberFormat));
     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
    136114            strBuilder.Append("*");
    137115          }
    138           if (varNode.VariableName.Contains("'")) {
     116
     117          if (varNode.VariableName.Contains("'"))
    139118            strBuilder.AppendFormat("\"{0}\"", varNode.VariableName);
    140           } else {
     119          else
    141120            strBuilder.AppendFormat("'{0}'", varNode.VariableName);
    142           }
    143           if (!varNode.Weight.IsAlmost(1.0)) {
    144             strBuilder.Append(")");
    145           }
     121
     122          if (!varNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
    146123        } else if (node.Symbol is FactorVariable) {
    147124          var factorNode = node as FactorVariableTreeNode;
    148           if (factorNode.VariableName.Contains("'")) {
     125          if (factorNode.VariableName.Contains("'"))
    149126            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
    150           } else {
     127          else
    151128            strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
    152           }
     129
    153130          strBuilder.AppendFormat("[{0}]",
    154131            string.Join(", ", factorNode.Weights.Select(w => w.ToString(formatString, numberFormat))));
     
    157134          if (!factorNode.Weight.IsAlmost(1.0)) {
    158135            strBuilder.Append("(");
    159             strBuilder.Append(factorNode.Weight.ToString(formatString, numberFormat));
     136            if (constants != null) {
     137              strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", factorNode.Weight);
     138            } else {
     139              strBuilder.Append(factorNode.Weight.ToString(formatString, numberFormat));
     140            }
     141
    160142            strBuilder.Append("*");
    161143          }
    162           if (factorNode.VariableName.Contains("'")) {
     144
     145          if (factorNode.VariableName.Contains("'"))
    163146            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
    164           } else {
     147          else
    165148            strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
    166           }
     149
    167150          strBuilder.Append(" = ");
    168           if (factorNode.VariableValue.Contains("'")) {
     151          if (factorNode.VariableValue.Contains("'"))
    169152            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableValue);
    170           } else {
     153          else
    171154            strBuilder.AppendFormat("'{0}'", factorNode.VariableValue);
    172           }
    173 
    174           if (!factorNode.Weight.IsAlmost(1.0)) {
    175             strBuilder.Append(")");
    176           }
    177 
     155
     156          if (!factorNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
    178157        } else if (node.Symbol is Constant) {
    179158          var constNode = node as ConstantTreeNode;
    180           if (constNode.Value >= 0.0)
    181             strBuilder.Append(constNode.Value.ToString(formatString, numberFormat));
    182           else
    183             strBuilder.Append("(").Append(constNode.Value.ToString(formatString, numberFormat)).Append(")"); // (-1
     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          }
    184171        }
    185172      }
     
    193180    }
    194181  }
     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  }
    195268}
Note: See TracChangeset for help on using the changeset viewer.