Changeset 17869


Ignore:
Timestamp:
03/09/21 10:59:51 (18 months ago)
Author:
gkronber
Message:

#2968: merged r17581,r17685,r17811 from trunk to stable

Location:
stable
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • stable

  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic

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

    r17181 r17869  
    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 {
     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) {
     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          strBuilder.Append("(");
     42          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
     43
     44          foreach (var subtree in node.Subtrees.Skip(1)) {
     45            strBuilder.Append(" ").Append(token).Append(" ");
     46            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
     47          }
     48
     49          strBuilder.Append(")");
     50        } else if (token == "^") {
     51          // handle integer powers directly
     52          strBuilder.Append("(");
     53          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
     54
     55          var power = node.GetSubtree(1);
     56          if(power is ConstantTreeNode constNode && Math.Truncate(constNode.Value) == constNode.Value) {
     57            strBuilder.Append(" ").Append(token).Append(" ").Append(constNode.Value.ToString(formatString, numberFormat));
     58          } else {
     59            strBuilder.Append(" ").Append(token).Append(" ");
     60            FormatRecursively(power, strBuilder, numberFormat, formatString, constants);
     61          }
     62
     63          strBuilder.Append(")");
     64        } else {
     65          // function with multiple arguments
     66          strBuilder.Append(token).Append("(");
     67          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants);
     68          foreach (var subtree in node.Subtrees.Skip(1)) {
     69            strBuilder.Append(", ");
     70            FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants);
     71          }
     72
     73          strBuilder.Append(")");
     74        }
     75      } else if (node.SubtreeCount == 1) {
     76        var token = GetToken(node.Symbol);
     77        if (token == "-" || token == "NOT") {
     78          strBuilder.Append("(").Append(token).Append("(");
     79          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     80          strBuilder.Append("))");
     81        } else if (token == "/") {
     82          strBuilder.Append("1/");
     83          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     84        } else if (token == "+" || token == "*") {
     85          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     86        } else {
     87          // function with only one argument
     88          strBuilder.Append(token).Append("(");
     89          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants);
     90          strBuilder.Append(")");
     91        }
     92      } else {
     93        // no subtrees
     94        if (node.Symbol is LaggedVariable) {
     95          var varNode = node as LaggedVariableTreeNode;
     96          if (!varNode.Weight.IsAlmost(1.0)) {
     97            strBuilder.Append("(");
     98            AppendConstant(strBuilder, constants, varNode.Weight, formatString, numberFormat);
     99            strBuilder.Append("*");
     100          }
     101
     102          strBuilder.Append("LAG(");
     103          AppendVariableName(strBuilder, varNode.VariableName);
     104          strBuilder.Append(", ")
     105                    .AppendFormat(numberFormat, "{0}", varNode.Lag)
     106                    .Append(")");
     107          if (!varNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
     108        } else if (node.Symbol is Variable) {
     109          var varNode = node as VariableTreeNode;
     110          if (!varNode.Weight.IsAlmost(1.0)) {
     111            strBuilder.Append("(");
     112            AppendConstant(strBuilder, constants, varNode.Weight, formatString, numberFormat);
     113            strBuilder.Append("*");
     114          }
     115
     116          AppendVariableName(strBuilder, varNode.VariableName);
     117
     118          if (!varNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
     119        } else if (node.Symbol is FactorVariable) {
     120          var factorNode = node as FactorVariableTreeNode;
     121          AppendVariableName(strBuilder, factorNode.VariableName);
     122
     123          strBuilder.Append("[");
     124          for (int i = 0; i < factorNode.Weights.Length; i++) {
     125            if (i > 0) strBuilder.Append(", ");
     126            AppendConstant(strBuilder, constants, factorNode.Weights[i], formatString, numberFormat);
     127          }
     128          strBuilder.Append("]");
     129        } else if (node.Symbol is BinaryFactorVariable) {
     130          var factorNode = node as BinaryFactorVariableTreeNode;
     131          if (!factorNode.Weight.IsAlmost(1.0)) {
     132            strBuilder.Append("(");
     133            AppendConstant(strBuilder, constants, factorNode.Weight, formatString, numberFormat);
     134
     135            strBuilder.Append("*");
     136          }
     137
     138          AppendVariableName(strBuilder, factorNode.VariableName);
     139          strBuilder.Append(" = ");
     140          AppendVariableName(strBuilder, factorNode.VariableValue);
     141
     142          if (!factorNode.Weight.IsAlmost(1.0)) strBuilder.Append(")");
     143        } else if (node.Symbol is Constant) {
     144          var constNode = node as ConstantTreeNode;
     145          if (constants == null && constNode.Value < 0) {
     146            strBuilder.Append("(").Append(constNode.Value.ToString(formatString, numberFormat))
     147                      .Append(")"); // (-1
     148          } else {
     149            AppendConstant(strBuilder, constants, constNode.Value, formatString, numberFormat);
     150          }
     151        }
     152      }
     153    }
     154
     155    private static void AppendConstant(StringBuilder strBuilder, List<KeyValuePair<string, double>> constants, double value, string formatString, NumberFormatInfo numberFormat) {
     156      if (constants != null) {
     157        string constantKey = $"c_{constants.Count}";
     158        strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", constantKey);
     159        constants.Add(new KeyValuePair<string, double>(constantKey, value));
     160      } else {
     161        strBuilder.Append(value.ToString(formatString, numberFormat));
     162      }
     163    }
     164
     165    private static void AppendVariableName(StringBuilder strBuilder, string name) {
     166      if (name.Contains("'"))
     167        strBuilder.AppendFormat("\"{0}\"", name);
     168      else
     169        strBuilder.AppendFormat("'{0}'", name);
     170    }
     171
     172    private static string GetToken(ISymbol symbol) {
     173      var tok = InfixExpressionParser.knownSymbols.GetBySecond(symbol).FirstOrDefault();
     174      if (tok == null)
     175        throw new ArgumentException(string.Format("Unknown symbol {0} found.", symbol.Name));
     176      return tok;
     177    }
     178  }
     179
    32180  /// <summary>
    33181  /// Formats mathematical expressions in infix form. E.g. x1 * (3.0 * x2 + x3)
    34182  /// </summary>
    35183  [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 
     184  [Item("Infix Symbolic Expression Tree Formatter",
     185    "A string formatter that converts symbolic expression trees to infix expressions.")]
    38186  public sealed class InfixExpressionFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
    39 
    40 
    41187    [StorableConstructor]
    42188    private InfixExpressionFormatter(StorableConstructorFlag _) : base(_) { }
     189
    43190    private InfixExpressionFormatter(InfixExpressionFormatter original, Cloner cloner) : base(original, cloner) { }
     191
    44192    public InfixExpressionFormatter()
    45193      : base() {
     
    47195      Description = ItemDescription;
    48196    }
     197
    49198    public override IDeepCloneable Clone(Cloner cloner) {
    50199      return new InfixExpressionFormatter(this, cloner);
     
    58207    /// <param name="formatString">The format string for numeric parameters (e.g. \"G4\" to limit to 4 digits, default is \"G\")</param>
    59208    /// <returns>Infix expression</returns>
    60     public string Format(ISymbolicExpressionTree symbolicExpressionTree, NumberFormatInfo numberFormat, string formatString="G") {
     209    public string Format(ISymbolicExpressionTree symbolicExpressionTree, NumberFormatInfo numberFormat,
     210                         string formatString = "G") {
    61211      // skip root and start symbols
    62212      StringBuilder strBuilder = new StringBuilder();
    63       FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0), strBuilder, numberFormat, formatString);
     213      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
     214        strBuilder, numberFormat, formatString);
    64215      return strBuilder.ToString();
    65216    }
     
    68219      return Format(symbolicExpressionTree, NumberFormatInfo.InvariantInfo);
    69220    }
    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         }
     221  }
     222
     223  [StorableType("54D917E8-134E-4066-9A60-2737C12D81DC")]
     224  [Item("Infix String Formater", "Formatter for symbolic expressions, which produces an infix expression " +
     225                                 "as well as a list of all coefficient values")]
     226  public sealed class InfixExpressionStringFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
     227    [StorableConstructor]
     228    private InfixExpressionStringFormatter(StorableConstructorFlag _) : base(_) { }
     229
     230    private InfixExpressionStringFormatter(InfixExpressionStringFormatter original, Cloner cloner) : base(original, cloner) { }
     231
     232    public InfixExpressionStringFormatter() : base() {
     233      Name = ItemName;
     234      Description = ItemDescription;
     235    }
     236
     237    public override IDeepCloneable Clone(Cloner cloner) {
     238      return new InfixExpressionStringFormatter(this, cloner);
     239    }
     240
     241    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
     242      StringBuilder strBuilder = new StringBuilder();
     243      var constants = new List<KeyValuePair<string, double>>();
     244      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
     245        strBuilder, NumberFormatInfo.InvariantInfo, "G", constants);
     246      strBuilder.Append($"{Environment.NewLine}{Environment.NewLine}");
     247
     248      int maxDigits = GetDigits(constants.Count);
     249      int padding = constants.Max(x => x.Value.ToString("F12", CultureInfo.InvariantCulture).Length);
     250      foreach (var constant in constants) {
     251        int digits = GetDigits(Int32.Parse(constant.Key.Substring(2)));
     252        strBuilder.Append($"{constant.Key}{new String(' ', maxDigits - digits)} = " +
     253                          string.Format($"{{0,{padding}:F12}}", constant.Value, CultureInfo.InvariantCulture) +
     254                          Environment.NewLine);
    185255      }
    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;
     256
     257      return strBuilder.ToString();
     258    }
     259
     260    private int GetDigits(int x) {
     261      if (x == 0) return 1;
     262      return (int)Math.Floor(Math.Log10(x) + 1);
    193263    }
    194264  }
Note: See TracChangeset for help on using the changeset viewer.