Changeset 17523


Ignore:
Timestamp:
04/28/20 10:15:43 (2 months ago)
Author:
chaider
Message:

#2968

  • Removed extra file for InfixStringFormatter (moved class to InfixExpressionFormatter file)
  • Extracted FormatRecursively into base class (reduce code duplication)
  • removed member variables constants and constantsCounter
Location:
branches/2968_infix_string_formatter/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4
Files:
1 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • branches/2968_infix_string_formatter/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs

    r17503 r17523  
    2121
    2222using System;
     23using System.Collections.Generic;
    2324using System.Globalization;
    2425using System.Linq;
     
    3031
    3132namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
     33  public abstract class BaseInfixExpressionFormatter {
     34    public static void FormatRecursively(ISymbolicExpressionTreeNode node, StringBuilder strBuilder,
     35                                          NumberFormatInfo numberFormat, string formatString, List<KeyValuePair<string, double>> constants = null) {
     36      if (node.SubtreeCount > 1) {
     37        var token = GetToken(node.Symbol);
     38        // operators
     39        if (token == "+" || token == "-" || token == "OR" || token == "XOR" ||
     40            token == "*" || token == "/" || token == "AND" ||
     41            token == "^") {
     42          strBuilder.Append("(");
     43          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
     44
     45          foreach (var subtree in node.Subtrees.Skip(1)) {
     46            strBuilder.Append(" ").Append(token).Append(" ");
     47            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
     48          }
     49
     50          strBuilder.Append(")");
     51        }
     52        else {
     53          // function with multiple arguments
     54          strBuilder.Append(token).Append("(");
     55          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
     56          foreach (var subtree in node.Subtrees.Skip(1)) {
     57            strBuilder.Append(", ");
     58            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
     59          }
     60
     61          strBuilder.Append(")");
     62        }
     63      }
     64      else if (node.SubtreeCount == 1) {
     65        var token = GetToken(node.Symbol);
     66        if (token == "-" || token == "NOT") {
     67          strBuilder.Append("(").Append(token).Append("(");
     68          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     69          strBuilder.Append("))");
     70        }
     71        else if (token == "/") {
     72          strBuilder.Append("1/");
     73          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     74        }
     75        else if (token == "+" || token == "*") {
     76          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     77        }
     78        else {
     79          // function with only one argument
     80          strBuilder.Append(token).Append("(");
     81          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     82          strBuilder.Append(")");
     83        }
     84      }
     85      else {
     86        // no subtrees
     87        if (node.Symbol is LaggedVariable) {
     88          var varNode = node as LaggedVariableTreeNode;
     89          if (!varNode.Weight.IsAlmost(1.0)) {
     90            strBuilder.Append("(");
     91            if (constants != null) {
     92              strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", varNode.Weight);
     93            } else {
     94              strBuilder.Append(varNode.Weight.ToString(formatString, numberFormat));
     95            }
     96            strBuilder.Append("*");
     97          }
     98
     99          strBuilder.Append("LAG(");
     100          if (varNode.VariableName.Contains("'"))
     101            strBuilder.AppendFormat("\"{0}\"", varNode.VariableName);
     102          else
     103            strBuilder.AppendFormat("'{0}'", varNode.VariableName);
     104
     105          strBuilder.Append(", ")
     106                    .AppendFormat(numberFormat, "{0}", varNode.Lag)
     107                    .Append(")");
     108        }
     109        else if (node.Symbol is Variable) {
     110          var varNode = node as VariableTreeNode;
     111          if (!varNode.Weight.IsAlmost(1.0)) {
     112            strBuilder.Append("(");
     113            if (constants != null) {
     114              string constantKey = $"c_{constants.Count}";
     115              strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", constantKey);
     116              constants.Add(new KeyValuePair<string, double>(constantKey, varNode.Weight));
     117            } else {
     118              strBuilder.Append(varNode.Weight.ToString(formatString, numberFormat));
     119            }
     120
     121            strBuilder.Append("*");
     122          }
     123
     124          if (varNode.VariableName.Contains("'"))
     125            strBuilder.AppendFormat("\"{0}\"", varNode.VariableName);
     126          else
     127            strBuilder.AppendFormat("'{0}'", varNode.VariableName);
     128
     129          if (!varNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
     130        }
     131        else if (node.Symbol is FactorVariable) {
     132          var factorNode = node as FactorVariableTreeNode;
     133          if (factorNode.VariableName.Contains("'"))
     134            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
     135          else
     136            strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
     137
     138          strBuilder.AppendFormat("[{0}]",
     139            string.Join(", ", factorNode.Weights.Select(w => w.ToString(formatString, numberFormat))));
     140        }
     141        else if (node.Symbol is BinaryFactorVariable) {
     142          var factorNode = node as BinaryFactorVariableTreeNode;
     143          if (!factorNode.Weight.IsAlmost(1.0)) {
     144            strBuilder.Append("(");
     145            if (constants != null) {
     146              strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", factorNode.Weight);
     147            } else {
     148              strBuilder.Append(factorNode.Weight.ToString(formatString, numberFormat));
     149            }
     150           
     151            strBuilder.Append("*");
     152          }
     153
     154          if (factorNode.VariableName.Contains("'"))
     155            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
     156          else
     157            strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
     158
     159          strBuilder.Append(" = ");
     160          if (factorNode.VariableValue.Contains("'"))
     161            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableValue);
     162          else
     163            strBuilder.AppendFormat("'{0}'", factorNode.VariableValue);
     164
     165          if (!factorNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
     166        } else if (node.Symbol is Constant) {
     167          var constNode = node as ConstantTreeNode;
     168          if (constants != null) {
     169            string constantKey = $"c_{constants.Count}";
     170
     171            strBuilder.AppendFormat(CultureInfo.InvariantCulture, constantKey);
     172            constants.Add(new KeyValuePair<string, double>(constantKey, constNode.Value));
     173          } else {
     174            if (constNode.Value >= 0.0)
     175              strBuilder.Append(constNode.Value.ToString(formatString, numberFormat));
     176            else
     177              strBuilder.Append("(").Append(constNode.Value.ToString(formatString, numberFormat))
     178                        .Append(")"); // (-1
     179          }
     180        }
     181      }
     182    }
     183
     184    private static string GetToken(ISymbol symbol) {
     185      var tok = InfixExpressionParser.knownSymbols.GetBySecond(symbol).FirstOrDefault();
     186      if (tok == null)
     187        throw new ArgumentException(string.Format("Unknown symbol {0} found.", symbol.Name));
     188      return tok;
     189    }
     190  }
     191
    32192  /// <summary>
    33193  /// Formats mathematical expressions in infix form. E.g. x1 * (3.0 * x2 + x3)
    34194  /// </summary>
    35195  [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 
     196  [Item("Infix Symbolic Expression Tree Formatter",
     197    "A string formatter that converts symbolic expression trees to infix expressions.")]
    38198  public sealed class InfixExpressionFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
    39 
    40 
    41199    [StorableConstructor]
    42200    private InfixExpressionFormatter(StorableConstructorFlag _) : base(_) { }
     201
    43202    private InfixExpressionFormatter(InfixExpressionFormatter original, Cloner cloner) : base(original, cloner) { }
     203
    44204    public InfixExpressionFormatter()
    45205      : base() {
     
    47207      Description = ItemDescription;
    48208    }
     209
    49210    public override IDeepCloneable Clone(Cloner cloner) {
    50211      return new InfixExpressionFormatter(this, cloner);
     
    58219    /// <param name="formatString">The format string for numeric parameters (e.g. \"G4\" to limit to 4 digits, default is \"G\")</param>
    59220    /// <returns>Infix expression</returns>
    60     public string Format(ISymbolicExpressionTree symbolicExpressionTree, NumberFormatInfo numberFormat, string formatString="G") {
     221    public string Format(ISymbolicExpressionTree symbolicExpressionTree, NumberFormatInfo numberFormat,
     222                         string formatString = "G") {
    61223      // skip root and start symbols
    62224      StringBuilder strBuilder = new StringBuilder();
    63       FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0), strBuilder, numberFormat, formatString);
     225      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
     226        strBuilder, numberFormat, formatString);
    64227      return strBuilder.ToString();
    65228    }
     
    68231      return Format(symbolicExpressionTree, NumberFormatInfo.InvariantInfo);
    69232    }
    70 
    71     private static void FormatRecursively(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, NumberFormatInfo numberFormat, string formatString) {
    72       if (node.SubtreeCount > 1) {
    73         var token = GetToken(node.Symbol);
    74         // operators
    75         if (token == "+" || token == "-" || token == "OR" || token == "XOR" ||
    76             token == "*" || token == "/" || token == "AND" ||
    77             token == "^") {
    78           strBuilder.Append("(");
    79           FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString);
    80 
    81           foreach (var subtree in node.Subtrees.Skip(1)) {
    82             strBuilder.Append(" ").Append(token).Append(" ");
    83             FormatRecursively(subtree, strBuilder, numberFormat, formatString);
    84           }
    85           strBuilder.Append(")");
    86         } else {
    87           // function with multiple arguments
    88           strBuilder.Append(token).Append("(");
    89           FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString);
    90           foreach (var subtree in node.Subtrees.Skip(1)) {
    91             strBuilder.Append(", ");
    92             FormatRecursively(subtree, strBuilder, numberFormat, formatString);
    93           }
    94           strBuilder.Append(")");
    95         }
    96       } else if (node.SubtreeCount == 1) {
    97         var token = GetToken(node.Symbol);
    98         if (token == "-" || token == "NOT") {
    99           strBuilder.Append("(").Append(token).Append("(");
    100           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
    101           strBuilder.Append("))");
    102         } else if (token == "/") {
    103           strBuilder.Append("1/");
    104           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
    105         } else if (token == "+" || token == "*") {
    106           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
    107         } else {
    108           // function with only one argument
    109           strBuilder.Append(token).Append("(");
    110           FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString);
    111           strBuilder.Append(")");
    112         }
    113       } else {
    114         // no subtrees
    115         if (node.Symbol is LaggedVariable) {
    116           var varNode = node as LaggedVariableTreeNode;
    117           if (!varNode.Weight.IsAlmost(1.0)) {
    118             strBuilder.Append("(");
    119             strBuilder.Append(varNode.Weight.ToString(formatString, numberFormat));
    120             strBuilder.Append("*");
    121           }
    122           strBuilder.Append("LAG(");
    123           if (varNode.VariableName.Contains("'")) {
    124             strBuilder.AppendFormat("\"{0}\"", varNode.VariableName);
    125           } else {
    126             strBuilder.AppendFormat("'{0}'", varNode.VariableName);
    127           }
    128           strBuilder.Append(", ")
    129             .AppendFormat(numberFormat, "{0}", varNode.Lag)
    130             .Append(")");
    131         } else if (node.Symbol is Variable) {
    132           var varNode = node as VariableTreeNode;
    133           if (!varNode.Weight.IsAlmost(1.0)) {
    134             strBuilder.Append("(");
    135             strBuilder.Append(varNode.Weight.ToString(formatString, numberFormat));
    136             strBuilder.Append("*");
    137           }
    138           if (varNode.VariableName.Contains("'")) {
    139             strBuilder.AppendFormat("\"{0}\"", varNode.VariableName);
    140           } else {
    141             strBuilder.AppendFormat("'{0}'", varNode.VariableName);
    142           }
    143           if (!varNode.Weight.IsAlmost(1.0)) {
    144             strBuilder.Append(")");
    145           }
    146         } else if (node.Symbol is FactorVariable) {
    147           var factorNode = node as FactorVariableTreeNode;
    148           if (factorNode.VariableName.Contains("'")) {
    149             strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
    150           } else {
    151             strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
    152           }
    153           strBuilder.AppendFormat("[{0}]",
    154             string.Join(", ", factorNode.Weights.Select(w => w.ToString(formatString, numberFormat))));
    155         } else if (node.Symbol is BinaryFactorVariable) {
    156           var factorNode = node as BinaryFactorVariableTreeNode;
    157           if (!factorNode.Weight.IsAlmost(1.0)) {
    158             strBuilder.Append("(");
    159             strBuilder.Append(factorNode.Weight.ToString(formatString, numberFormat));
    160             strBuilder.Append("*");
    161           }
    162           if (factorNode.VariableName.Contains("'")) {
    163             strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
    164           } else {
    165             strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
    166           }
    167           strBuilder.Append(" = ");
    168           if (factorNode.VariableValue.Contains("'")) {
    169             strBuilder.AppendFormat("\"{0}\"", factorNode.VariableValue);
    170           } else {
    171             strBuilder.AppendFormat("'{0}'", factorNode.VariableValue);
    172           }
    173 
    174           if (!factorNode.Weight.IsAlmost(1.0)) {
    175             strBuilder.Append(")");
    176           }
    177 
    178         } else if (node.Symbol is Constant) {
    179           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
    184         }
    185       }
    186     }
    187 
    188     private static string GetToken(ISymbol symbol) {
    189       var tok = InfixExpressionParser.knownSymbols.GetBySecond(symbol).FirstOrDefault();
    190       if (tok == null)
    191         throw new ArgumentException(string.Format("Unknown symbol {0} found.", symbol.Name));
    192       return tok;
     233  }
     234
     235  [StorableType("54D917E8-134E-4066-9A60-2737C12D81DC")]
     236  [Item("Infix String Formater", "Formatter for symbolic expressions, which produces an infix expression " +
     237                                 "as well as a list of all coefficient values")]
     238  public sealed class InfixExpressionStringFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
     239    [StorableConstructor]
     240    private InfixExpressionStringFormatter(StorableConstructorFlag _) : base(_) { }
     241
     242    private InfixExpressionStringFormatter(InfixExpressionStringFormatter original, Cloner cloner) : base(original, cloner) { }
     243
     244    public InfixExpressionStringFormatter() : base() {
     245      Name = ItemName;
     246      Description = ItemDescription;
     247    }
     248
     249    public override IDeepCloneable Clone(Cloner cloner) {
     250      return new InfixExpressionStringFormatter(this, cloner);
     251    }
     252
     253    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
     254      StringBuilder strBuilder = new StringBuilder();
     255      var constants = new List<KeyValuePair<string, double>>();
     256      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
     257        strBuilder, NumberFormatInfo.InvariantInfo, "G", constants);
     258      strBuilder.Append($"{Environment.NewLine}{Environment.NewLine}");
     259
     260      int maxDigits = GetDigits(constants.Count);
     261      int padding = constants.Max(x => x.Value.ToString("F12", CultureInfo.InvariantCulture).Length);
     262      foreach (var constant in constants) {
     263        int digits = GetDigits(Int32.Parse(constant.Key.Substring(2)));
     264        strBuilder.Append($"{constant.Key}{new String(' ', maxDigits - digits)} = " +
     265                          string.Format($"{{0,{padding}:F12}}", constant.Value, CultureInfo.InvariantCulture) +
     266                          Environment.NewLine);
     267      }
     268
     269      return strBuilder.ToString();
     270    }
     271
     272    private int GetDigits(int x) {
     273      if (x == 0) return 1;
     274      return (int) Math.Floor(Math.Log10(x) + 1);
    193275    }
    194276  }
  • branches/2968_infix_string_formatter/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj

    r17503 r17523  
    235235    <Compile Include="Interpreter\BatchInstruction.cs" />
    236236    <Compile Include="Interpreter\BatchOperations.cs" />
    237     <Compile Include="Formatters\InfixExpressionStringFormatter.cs" />
    238237    <Compile Include="Interpreter\IntervalInterpreter.cs" />
    239238    <Compile Include="Interpreter\SymbolicDataAnalysisExpressionCompiledTreeInterpreter.cs" />
Note: See TracChangeset for help on using the changeset viewer.