Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TimeSeries/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs @ 7463

Last change on this file since 7463 was 7463, checked in by gkronber, 13 years ago

#1081 improved formatter, line chart view, simplifier for time series prognosis solutions

File size: 14.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
30
31namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
32  [Item("LaTeX String Formatter", "Formatter for symbolic expression trees for import into LaTeX documents.")]
33  [StorableClass]
34  public sealed class SymbolicDataAnalysisExpressionLatexFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
35    private readonly List<double> constants;
36    private int targetCount;
37    private int currentLag;
38
39    [StorableConstructor]
40    private SymbolicDataAnalysisExpressionLatexFormatter(bool deserializing) : base(deserializing) { }
41    private SymbolicDataAnalysisExpressionLatexFormatter(SymbolicDataAnalysisExpressionLatexFormatter original, Cloner cloner)
42      : base(original, cloner) {
43      constants = new List<double>(original.constants);
44    }
45    public SymbolicDataAnalysisExpressionLatexFormatter()
46      : base() {
47      Name = ItemName;
48      Description = ItemDescription;
49      constants = new List<double>();
50    }
51
52    public override IDeepCloneable Clone(Cloner cloner) {
53      return new SymbolicDataAnalysisExpressionLatexFormatter(this, cloner);
54    }
55
56    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
57      try {
58        StringBuilder strBuilder = new StringBuilder();
59        constants.Clear();
60        targetCount = 1;
61        strBuilder.AppendLine(FormatRecursively(symbolicExpressionTree.Root));
62        return strBuilder.ToString();
63      }
64      catch (NotImplementedException ex) {
65        return ex.Message + Environment.NewLine + ex.StackTrace;
66      }
67    }
68
69    private string FormatRecursively(ISymbolicExpressionTreeNode node) {
70      StringBuilder strBuilder = new StringBuilder();
71      currentLag = 0;
72      FormatBegin(node, strBuilder);
73
74      if (node.SubtreeCount > 0) {
75        strBuilder.Append(FormatRecursively(node.GetSubtree(0)));
76      }
77      foreach (SymbolicExpressionTreeNode subTree in node.Subtrees.Skip(1)) {
78        FormatSep(node, strBuilder);
79        // format the whole subtree
80        strBuilder.Append(FormatRecursively(subTree));
81      }
82
83      FormatEnd(node, strBuilder);
84
85      return strBuilder.ToString();
86    }
87
88    private void FormatBegin(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
89      if (node.Symbol is Addition) {
90        strBuilder.Append(@" \left( ");
91      } else if (node.Symbol is Subtraction) {
92        if (node.SubtreeCount == 1) {
93          strBuilder.Append(@"- \left( ");
94        } else {
95          strBuilder.Append(@" \left( ");
96        }
97      } else if (node.Symbol is Multiplication) {
98      } else if (node.Symbol is Division) {
99        if (node.SubtreeCount == 1) {
100          strBuilder.Append(@" \cfrac{1");
101        } else {
102          strBuilder.Append(@" \cfrac{ ");
103        }
104      } else if (node.Symbol is Average) {
105        // skip output of (1/1) if only one subtree
106        if (node.SubtreeCount > 1) {
107          strBuilder.Append(@" \cfrac{1}{" + node.SubtreeCount + @"}");
108        }
109        strBuilder.Append(@" \left( ");
110      } else if (node.Symbol is Logarithm) {
111        strBuilder.Append(@"\log \left( ");
112      } else if (node.Symbol is Exponential) {
113        strBuilder.Append(@"\exp \left( ");
114      } else if (node.Symbol is Sine) {
115        strBuilder.Append(@"\sin \left( ");
116      } else if (node.Symbol is Cosine) {
117        strBuilder.Append(@"\cos \left( ");
118      } else if (node.Symbol is Tangent) {
119        strBuilder.Append(@"\tan \left( ");
120      } else if (node.Symbol is GreaterThan) {
121        strBuilder.Append(@"  \left( ");
122      } else if (node.Symbol is LessThan) {
123        strBuilder.Append(@"  \left( ");
124      } else if (node.Symbol is And) {
125        strBuilder.Append(@"  \left( \left( ");
126      } else if (node.Symbol is Or) {
127        strBuilder.Append(@"   \left( \left( ");
128      } else if (node.Symbol is Not) {
129        strBuilder.Append(@" \neg \left( ");
130      } else if (node.Symbol is IfThenElse) {
131        strBuilder.Append(@" \operatorname{if}  \left( ");
132      } else if (node.Symbol is Constant) {
133        strBuilder.Append("c_{" + constants.Count + "} ");
134        var constNode = node as ConstantTreeNode;
135        constants.Add(constNode.Value);
136      } else if (node.Symbol is LaggedVariable) {
137        var laggedVarNode = node as LaggedVariableTreeNode;
138        if (!laggedVarNode.Weight.IsAlmost(1.0)) {
139          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
140          constants.Add(laggedVarNode.Weight);
141        }
142        strBuilder.Append(EscapeLatexString(laggedVarNode.VariableName));
143        strBuilder.Append(LagToString(currentLag + laggedVarNode.Lag));
144
145      } else if (node.Symbol is Variable) {
146        var varNode = node as VariableTreeNode;
147        if (!varNode.Weight.IsAlmost((1.0))) {
148          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
149          constants.Add(varNode.Weight);
150        }
151        strBuilder.Append(EscapeLatexString(varNode.VariableName));
152        strBuilder.Append(LagToString(currentLag));
153      } else if (node.Symbol is ProgramRootSymbol) {
154        strBuilder
155          .AppendLine("\\begin{align*}")
156          .AppendLine("\\nonumber");
157      } else if (node.Symbol is Defun) {
158        var defunNode = node as DefunTreeNode;
159        strBuilder.Append(defunNode.FunctionName + " & = ");
160      } else if (node.Symbol is InvokeFunction) {
161        var invokeNode = node as InvokeFunctionTreeNode;
162        strBuilder.Append(invokeNode.Symbol.FunctionName + @" \left( ");
163      } else if (node.Symbol is StartSymbol) {
164        strBuilder.Append("target_" + (targetCount++) + "(t) & = ");
165      } else if (node.Symbol is Argument) {
166        var argSym = node.Symbol as Argument;
167        strBuilder.Append(" ARG+" + argSym.ArgumentIndex + " ");
168      } else if (node.Symbol is Derivative) {
169        strBuilder.Append(@" \cfrac{d \left( ");
170      } else if (node.Symbol is TimeLag) {
171        var laggedNode = node as ILaggedTreeNode;
172        currentLag += laggedNode.Lag;
173      } else if (node.Symbol is Power) {
174        strBuilder.Append(@" \left( ");
175      } else if (node.Symbol is Root) {
176        strBuilder.Append(@" \left( ");
177      } else if (node.Symbol is Integral) {
178        // actually a new variable for t is needed in all subtrees (TODO)
179        var laggedTreeNode = node as ILaggedTreeNode;
180        strBuilder.Append(@"\sum_{t=" + (laggedTreeNode.Lag + currentLag) + @"}^0 \left( ");
181      } else if (node.Symbol is VariableCondition) {
182        var conditionTreeNode = node as VariableConditionTreeNode;
183        string p = @"1 /  1 + \exp  - c_{" + constants.Count + "} ";
184        constants.Add(conditionTreeNode.Slope);
185        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"}   ";
186        constants.Add(conditionTreeNode.Threshold);
187        strBuilder.Append(@" \left( " + p + @"\cdot ");
188      } else {
189        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
190      }
191    }
192
193    private void FormatSep(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
194      if (node.Symbol is Addition) {
195        strBuilder.Append(" + ");
196      } else if (node.Symbol is Subtraction) {
197        strBuilder.Append(" - ");
198      } else if (node.Symbol is Multiplication) {
199        strBuilder.Append(@" \cdot ");
200      } else if (node.Symbol is Division) {
201        if (node.SubtreeCount <= 2)
202          strBuilder.Append(@" }{ ");
203        else
204          strBuilder.Append(@" }{ \cfrac{ ");
205      } else if (node.Symbol is Average) {
206        strBuilder.Append(@" + ");
207      } else if (node.Symbol is Logarithm) {
208        throw new InvalidOperationException();
209      } else if (node.Symbol is Exponential) {
210        throw new InvalidOperationException();
211      } else if (node.Symbol is Sine) {
212        throw new InvalidOperationException();
213      } else if (node.Symbol is Cosine) {
214        throw new InvalidOperationException();
215      } else if (node.Symbol is Tangent) {
216        throw new InvalidOperationException();
217      } else if (node.Symbol is GreaterThan) {
218        strBuilder.Append(@" > ");
219      } else if (node.Symbol is LessThan) {
220        strBuilder.Append(@" < ");
221      } else if (node.Symbol is And) {
222        strBuilder.Append(@" > 0  \right) \land \left(");
223      } else if (node.Symbol is Or) {
224        strBuilder.Append(@" > 0  \right) \lor \left(");
225      } else if (node.Symbol is Not) {
226        throw new InvalidOperationException();
227      } else if (node.Symbol is IfThenElse) {
228        strBuilder.Append(@" , ");
229      } else if (node.Symbol is ProgramRootSymbol) {
230        strBuilder.Append(@"\\" + Environment.NewLine);
231      } else if (node.Symbol is Defun) {
232      } else if (node.Symbol is InvokeFunction) {
233        strBuilder.Append(" , ");
234      } else if (node.Symbol is StartSymbol) {
235        strBuilder.Append(@"\\" + Environment.NewLine);
236        strBuilder.Append("target_" + (targetCount++) + "(t) & = ");
237      } else if (node.Symbol is Power) {
238        strBuilder.Append(@"\right) ^ { \operatorname{round} \left(");
239      } else if (node.Symbol is Root) {
240        strBuilder.Append(@"\right) ^ {  \cfrac{1}{ \operatorname{round} \left(");
241      } else if (node.Symbol is VariableCondition) {
242        var conditionTreeNode = node as VariableConditionTreeNode;
243        string p = @"1 / \left( 1 + \exp \left( - c_{" + constants.Count + "} ";
244        constants.Add(conditionTreeNode.Slope);
245        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"} \right) \right) \right)   ";
246        constants.Add(conditionTreeNode.Threshold);
247        strBuilder.Append(@" +  \left( 1 - " + p + @" \right) \cdot ");
248      } else {
249        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
250      }
251    }
252
253    private void FormatEnd(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
254      if (node.Symbol is Addition) {
255        strBuilder.Append(@" \right) ");
256      } else if (node.Symbol is Subtraction) {
257        strBuilder.Append(@" \right) ");
258      } else if (node.Symbol is Multiplication) {
259      } else if (node.Symbol is Division) {
260        strBuilder.Append("} ");
261        if (node.SubtreeCount == 1 || node.SubtreeCount == 2)
262          strBuilder.Append(" } ");
263        else
264          for (int i = 0; i < node.SubtreeCount; i++) {
265            strBuilder.Append(" } ");
266          }
267      } else if (node.Symbol is Average) {
268        strBuilder.Append(@" \right) ");
269      } else if (node.Symbol is Logarithm) {
270        strBuilder.Append(@" \right) ");
271      } else if (node.Symbol is Exponential) {
272        strBuilder.Append(@" \right) ");
273      } else if (node.Symbol is Sine) {
274        strBuilder.Append(@" \right) ");
275      } else if (node.Symbol is Cosine) {
276        strBuilder.Append(@" \right) ");
277      } else if (node.Symbol is Tangent) {
278        strBuilder.Append(@" \right) ");
279      } else if (node.Symbol is GreaterThan) {
280        strBuilder.Append(@" \right) ");
281      } else if (node.Symbol is LessThan) {
282        strBuilder.Append(@" \right) ");
283      } else if (node.Symbol is And) {
284        strBuilder.Append(@" > 0 \right) \right) ");
285      } else if (node.Symbol is Or) {
286        strBuilder.Append(@" > 0 \right) \right) ");
287      } else if (node.Symbol is Not) {
288        strBuilder.Append(@" \right) ");
289      } else if (node.Symbol is IfThenElse) {
290        strBuilder.Append(@" \right) ");
291      } else if (node.Symbol is Constant) {
292      } else if (node.Symbol is LaggedVariable) {
293      } else if (node.Symbol is Variable) {
294      } else if (node.Symbol is ProgramRootSymbol) {
295        strBuilder
296          .AppendLine("\\end{align*}")
297          .AppendLine("\\begin{align*}")
298          .AppendLine("\\nonumber");
299        // output all constant values
300        if (constants.Count > 0) {
301          int i = 0;
302          foreach (var constant in constants) {
303            // replace "." with ".&" to align decimal points
304            var constStr = string.Format(System.Globalization.NumberFormatInfo.InvariantInfo, "{0:G5}", constant);
305            if (!constStr.Contains(".")) constStr = constStr + ".0";
306            constStr = constStr.Replace(".", "\\negthickspace&.");  // fix problem in rendering of aligned expressions
307            strBuilder.Append("c_{" + i + "}& = & " + constStr);
308            strBuilder.Append(@"\\");
309            i++;
310          }
311        }
312        strBuilder.AppendLine("\\end{align*}");
313      } else if (node.Symbol is Defun) {
314      } else if (node.Symbol is InvokeFunction) {
315        strBuilder.Append(@" \right) ");
316      } else if (node.Symbol is StartSymbol) {
317      } else if (node.Symbol is Argument) {
318      } else if (node.Symbol is Derivative) {
319        strBuilder.Append(@" \right) }{dt} ");
320      } else if (node.Symbol is TimeLag) {
321        var laggedNode = node as ILaggedTreeNode;
322        currentLag -= laggedNode.Lag;
323      } else if (node.Symbol is Power) {
324        strBuilder.Append(@" \right) } ");
325      } else if (node.Symbol is Root) {
326        strBuilder.Append(@" \right) } } ");
327      } else if (node.Symbol is Integral) {
328        strBuilder.Append(@" \right) ");
329      } else if (node.Symbol is VariableCondition) {
330        strBuilder.Append(@"\right) ");
331      } else {
332        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
333      }
334    }
335
336    private string LagToString(int lag) {
337      if (lag < 0) {
338        return "(t" + lag + ")";
339      } else if (lag > 0) {
340        return "(t+" + lag + ")";
341      } else return "";
342    }
343
344    private string EscapeLatexString(string s) {
345      return "\\text{" +
346        s
347         .Replace("\\", "\\\\")
348         .Replace("{", "\\{")
349         .Replace("}", "\\}")
350         .Replace("%", "\\%")
351        + "}";
352    }
353  }
354}
Note: See TracBrowser for help on using the repository browser.