Changeset 17581
- Timestamp:
- 06/03/20 17:09:09 (5 years ago)
- Location:
- trunk/HeuristicLab.Problems.DataAnalysis.Symbolic
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/HeuristicLab.Problems.DataAnalysis.Symbolic
- Property svn:mergeinfo changed
/branches/2968_infix_string_formatter/HeuristicLab.Problems.DataAnalysis.Symbolic (added) merged: 16337-16340,16411,17503,17523
- Property svn:mergeinfo changed
-
trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs
r17180 r17581 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 { 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) { 72 36 if (node.SubtreeCount > 1) { 73 37 var token = GetToken(node.Symbol); … … 77 41 token == "^") { 78 42 strBuilder.Append("("); 79 FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString );43 FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants); 80 44 81 45 foreach (var subtree in node.Subtrees.Skip(1)) { 82 46 strBuilder.Append(" ").Append(token).Append(" "); 83 FormatRecursively(subtree, strBuilder, numberFormat, formatString); 84 } 47 FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants); 48 } 49 85 50 strBuilder.Append(")"); 86 51 } else { 87 52 // function with multiple arguments 88 53 strBuilder.Append(token).Append("("); 89 FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString );54 FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, constants); 90 55 foreach (var subtree in node.Subtrees.Skip(1)) { 91 56 strBuilder.Append(", "); 92 FormatRecursively(subtree, strBuilder, numberFormat, formatString); 93 } 57 FormatRecursively(subtree, strBuilder, numberFormat, formatString, constants); 58 } 59 94 60 strBuilder.Append(")"); 95 61 } … … 98 64 if (token == "-" || token == "NOT") { 99 65 strBuilder.Append("(").Append(token).Append("("); 100 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString );66 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants); 101 67 strBuilder.Append("))"); 102 68 } else if (token == "/") { 103 69 strBuilder.Append("1/"); 104 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString );70 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants); 105 71 } else if (token == "+" || token == "*") { 106 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString );72 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants); 107 73 } else { 108 74 // function with only one argument 109 75 strBuilder.Append(token).Append("("); 110 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString );76 FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, constants); 111 77 strBuilder.Append(")"); 112 78 } … … 117 83 if (!varNode.Weight.IsAlmost(1.0)) { 118 84 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 } 120 90 strBuilder.Append("*"); 121 91 } 92 122 93 strBuilder.Append("LAG("); 123 if (varNode.VariableName.Contains("'")) {94 if (varNode.VariableName.Contains("'")) 124 95 strBuilder.AppendFormat("\"{0}\"", varNode.VariableName); 125 } else {96 else 126 97 strBuilder.AppendFormat("'{0}'", varNode.VariableName); 127 } 98 128 99 strBuilder.Append(", ") 129 .AppendFormat(numberFormat, "{0}", varNode.Lag)130 .Append(")");100 .AppendFormat(numberFormat, "{0}", varNode.Lag) 101 .Append(")"); 131 102 } else if (node.Symbol is Variable) { 132 103 var varNode = node as VariableTreeNode; 133 104 if (!varNode.Weight.IsAlmost(1.0)) { 134 105 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 136 114 strBuilder.Append("*"); 137 115 } 138 if (varNode.VariableName.Contains("'")) { 116 117 if (varNode.VariableName.Contains("'")) 139 118 strBuilder.AppendFormat("\"{0}\"", varNode.VariableName); 140 } else {119 else 141 120 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(")"); 146 123 } else if (node.Symbol is FactorVariable) { 147 124 var factorNode = node as FactorVariableTreeNode; 148 if (factorNode.VariableName.Contains("'")) {125 if (factorNode.VariableName.Contains("'")) 149 126 strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName); 150 } else {127 else 151 128 strBuilder.AppendFormat("'{0}'", factorNode.VariableName); 152 } 129 153 130 strBuilder.AppendFormat("[{0}]", 154 131 string.Join(", ", factorNode.Weights.Select(w => w.ToString(formatString, numberFormat)))); … … 157 134 if (!factorNode.Weight.IsAlmost(1.0)) { 158 135 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 160 142 strBuilder.Append("*"); 161 143 } 162 if (factorNode.VariableName.Contains("'")) { 144 145 if (factorNode.VariableName.Contains("'")) 163 146 strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName); 164 } else {147 else 165 148 strBuilder.AppendFormat("'{0}'", factorNode.VariableName); 166 } 149 167 150 strBuilder.Append(" = "); 168 if (factorNode.VariableValue.Contains("'")) {151 if (factorNode.VariableValue.Contains("'")) 169 152 strBuilder.AppendFormat("\"{0}\"", factorNode.VariableValue); 170 } else {153 else 171 154 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(")"); 178 157 } else if (node.Symbol is Constant) { 179 158 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 } 184 171 } 185 172 } … … 193 180 } 194 181 } 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 } 195 268 }
Note: See TracChangeset
for help on using the changeset viewer.