- Timestamp:
- 03/09/21 10:59:51 (4 years ago)
- Location:
- stable
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
stable
- Property svn:mergeinfo changed
/trunk merged: 17581,17685,17811
- Property svn:mergeinfo changed
-
stable/HeuristicLab.Problems.DataAnalysis.Symbolic
- Property svn:mergeinfo changed
-
stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs
r17181 r17869 21 21 22 22 using System; 23 using System.Collections.Generic; 23 24 using System.Globalization; 24 25 using System.Linq; 25 26 using System.Text; 27 using HEAL.Attic; 26 28 using HeuristicLab.Common; 27 29 using HeuristicLab.Core; 28 30 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; 29 using HEAL.Attic;30 31 31 32 namespace 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 32 180 /// <summary> 33 181 /// Formats mathematical expressions in infix form. E.g. x1 * (3.0 * x2 + x3) 34 182 /// </summary> 35 183 [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.")] 38 186 public sealed class InfixExpressionFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter { 39 40 41 187 [StorableConstructor] 42 188 private InfixExpressionFormatter(StorableConstructorFlag _) : base(_) { } 189 43 190 private InfixExpressionFormatter(InfixExpressionFormatter original, Cloner cloner) : base(original, cloner) { } 191 44 192 public InfixExpressionFormatter() 45 193 : base() { … … 47 195 Description = ItemDescription; 48 196 } 197 49 198 public override IDeepCloneable Clone(Cloner cloner) { 50 199 return new InfixExpressionFormatter(this, cloner); … … 58 207 /// <param name="formatString">The format string for numeric parameters (e.g. \"G4\" to limit to 4 digits, default is \"G\")</param> 59 208 /// <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") { 61 211 // skip root and start symbols 62 212 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); 64 215 return strBuilder.ToString(); 65 216 } … … 68 219 return Format(symbolicExpressionTree, NumberFormatInfo.InvariantInfo); 69 220 } 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); 185 255 } 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); 193 263 } 194 264 }
Note: See TracChangeset
for help on using the changeset viewer.