Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs @ 7695

Last change on this file since 7695 was 7695, checked in by gkronber, 12 years ago

#1810 merged patch to add square and square root function symbols by mkommend

File size: 15.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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 List<double> constants;
36    private int currentLag;
37
38    [StorableConstructor]
39    private SymbolicDataAnalysisExpressionLatexFormatter(bool deserializing) : base(deserializing) { }
40    private SymbolicDataAnalysisExpressionLatexFormatter(SymbolicDataAnalysisExpressionLatexFormatter original, Cloner cloner)
41      : base(original, cloner) {
42      constants = new List<double>(original.constants);
43    }
44    public SymbolicDataAnalysisExpressionLatexFormatter()
45      : base() {
46      Name = ItemName;
47      Description = ItemDescription;
48      constants = new List<double>();
49    }
50
51    public override IDeepCloneable Clone(Cloner cloner) {
52      return new SymbolicDataAnalysisExpressionLatexFormatter(this, cloner);
53    }
54
55    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
56      try {
57        StringBuilder strBuilder = new StringBuilder();
58        constants.Clear();
59        strBuilder.AppendLine(FormatRecursively(symbolicExpressionTree.Root));
60        return strBuilder.ToString();
61      }
62      catch (NotImplementedException ex) {
63        return ex.Message + Environment.NewLine + ex.StackTrace;
64      }
65    }
66
67    private string FormatRecursively(ISymbolicExpressionTreeNode node) {
68      StringBuilder strBuilder = new StringBuilder();
69      currentLag = 0;
70      FormatBegin(node, strBuilder);
71
72      if (node.SubtreeCount > 0) {
73        strBuilder.Append(FormatRecursively(node.GetSubtree(0)));
74      }
75      int i = 1;
76      foreach (SymbolicExpressionTreeNode subTree in node.Subtrees.Skip(1)) {
77        FormatSep(node, strBuilder, i);
78        // format the whole subtree
79        strBuilder.Append(FormatRecursively(subTree));
80        i++;
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 Square) {
115        strBuilder.Append(@"\left(");
116      } else if (node.Symbol is SquareRoot) {
117        strBuilder.Append(@"\sqrt{");
118      } else if (node.Symbol is Sine) {
119        strBuilder.Append(@"\sin \left( ");
120      } else if (node.Symbol is Cosine) {
121        strBuilder.Append(@"\cos \left( ");
122      } else if (node.Symbol is Tangent) {
123        strBuilder.Append(@"\tan \left( ");
124      } else if (node.Symbol is GreaterThan) {
125        strBuilder.Append(@"  \left( ");
126      } else if (node.Symbol is LessThan) {
127        strBuilder.Append(@"  \left( ");
128      } else if (node.Symbol is And) {
129        strBuilder.Append(@"  \left( \left( ");
130      } else if (node.Symbol is Or) {
131        strBuilder.Append(@"   \left( \left( ");
132      } else if (node.Symbol is Not) {
133        strBuilder.Append(@" \neg \left( ");
134      } else if (node.Symbol is IfThenElse) {
135        strBuilder.Append(@" \operatorname{if}  \left( ");
136      } else if (node.Symbol is Constant) {
137        strBuilder.Append("c_{" + constants.Count + "} ");
138        var constNode = node as ConstantTreeNode;
139        constants.Add(constNode.Value);
140      } else if (node.Symbol is LaggedVariable) {
141        var laggedVarNode = node as LaggedVariableTreeNode;
142        if (!laggedVarNode.Weight.IsAlmost(1.0)) {
143          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
144          constants.Add(laggedVarNode.Weight);
145        }
146        strBuilder.Append(EscapeLatexString(laggedVarNode.VariableName));
147        strBuilder.Append(LagToString(currentLag + laggedVarNode.Lag));
148
149      } else if (node.Symbol is Variable) {
150        var varNode = node as VariableTreeNode;
151        if (!varNode.Weight.IsAlmost((1.0))) {
152          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
153          constants.Add(varNode.Weight);
154        }
155        strBuilder.Append(EscapeLatexString(varNode.VariableName));
156        strBuilder.Append(LagToString(currentLag));
157      } else if (node.Symbol is ProgramRootSymbol) {
158        strBuilder
159          .AppendLine("\\begin{align*}")
160          .AppendLine("\\nonumber");
161      } else if (node.Symbol is Defun) {
162        var defunNode = node as DefunTreeNode;
163        strBuilder.Append(defunNode.FunctionName + " & = ");
164      } else if (node.Symbol is InvokeFunction) {
165        var invokeNode = node as InvokeFunctionTreeNode;
166        strBuilder.Append(invokeNode.Symbol.FunctionName + @" \left( ");
167      } else if (node.Symbol is StartSymbol) {
168        strBuilder.Append("Result & = ");
169      } else if (node.Symbol is Argument) {
170        var argSym = node.Symbol as Argument;
171        strBuilder.Append(" ARG+" + argSym.ArgumentIndex + " ");
172      } else if (node.Symbol is Derivative) {
173        strBuilder.Append(@" \cfrac{d \left( ");
174      } else if (node.Symbol is TimeLag) {
175        var laggedNode = node as ILaggedTreeNode;
176        currentLag += laggedNode.Lag;
177      } else if (node.Symbol is Power) {
178        strBuilder.Append(@" \left( ");
179      } else if (node.Symbol is Root) {
180        strBuilder.Append(@" \left( ");
181      } else if (node.Symbol is Integral) {
182        // actually a new variable for t is needed in all subtrees (TODO)
183        var laggedTreeNode = node as ILaggedTreeNode;
184        strBuilder.Append(@"\sum_{t=" + (laggedTreeNode.Lag + currentLag) + @"}^0 \left( ");
185      } else if (node.Symbol is VariableCondition) {
186        var conditionTreeNode = node as VariableConditionTreeNode;
187        string p = @"1 /  1 + \exp  - c_{" + constants.Count + "} ";
188        constants.Add(conditionTreeNode.Slope);
189        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"}   ";
190        constants.Add(conditionTreeNode.Threshold);
191        strBuilder.Append(@" \left( " + p + @"\cdot ");
192      } else {
193        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
194      }
195    }
196
197    private void FormatSep(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, int step) {
198      if (node.Symbol is Addition) {
199        strBuilder.Append(" + ");
200      } else if (node.Symbol is Subtraction) {
201        strBuilder.Append(" - ");
202      } else if (node.Symbol is Multiplication) {
203        strBuilder.Append(@" \cdot ");
204      } else if (node.Symbol is Division) {
205        if (step + 1 == node.SubtreeCount)
206          strBuilder.Append(@"}{");
207        else
208          strBuilder.Append(@" }{ \cfrac{ ");
209      } else if (node.Symbol is Average) {
210        strBuilder.Append(@" + ");
211      } else if (node.Symbol is Logarithm) {
212        throw new InvalidOperationException();
213      } else if (node.Symbol is Exponential) {
214        throw new InvalidOperationException();
215      } else if (node.Symbol is Square) {
216        throw new InvalidOperationException();
217      } else if (node.Symbol is SquareRoot) {
218        throw new InvalidOperationException();
219      } else if (node.Symbol is Sine) {
220        throw new InvalidOperationException();
221      } else if (node.Symbol is Cosine) {
222        throw new InvalidOperationException();
223      } else if (node.Symbol is Tangent) {
224        throw new InvalidOperationException();
225      } else if (node.Symbol is GreaterThan) {
226        strBuilder.Append(@" > ");
227      } else if (node.Symbol is LessThan) {
228        strBuilder.Append(@" < ");
229      } else if (node.Symbol is And) {
230        strBuilder.Append(@" > 0  \right) \land \left(");
231      } else if (node.Symbol is Or) {
232        strBuilder.Append(@" > 0  \right) \lor \left(");
233      } else if (node.Symbol is Not) {
234        throw new InvalidOperationException();
235      } else if (node.Symbol is IfThenElse) {
236        strBuilder.Append(@" , ");
237      } else if (node.Symbol is ProgramRootSymbol) {
238        strBuilder.Append(@"\\" + Environment.NewLine);
239      } else if (node.Symbol is Defun) {
240      } else if (node.Symbol is InvokeFunction) {
241        strBuilder.Append(" , ");
242      } else if (node.Symbol is StartSymbol) {
243        strBuilder.Append(@"\\" + Environment.NewLine + " & ");
244      } else if (node.Symbol is Power) {
245        strBuilder.Append(@"\right) ^ { \operatorname{round} \left(");
246      } else if (node.Symbol is Root) {
247        strBuilder.Append(@"\right) ^ {  \cfrac{1}{ \operatorname{round} \left(");
248      } else if (node.Symbol is VariableCondition) {
249        var conditionTreeNode = node as VariableConditionTreeNode;
250        string p = @"1 / \left( 1 + \exp \left( - c_{" + constants.Count + "} ";
251        constants.Add(conditionTreeNode.Slope);
252        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"} \right) \right) \right)   ";
253        constants.Add(conditionTreeNode.Threshold);
254        strBuilder.Append(@" +  \left( 1 - " + p + @" \right) \cdot ");
255      } else {
256        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
257      }
258    }
259
260    private void FormatEnd(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
261      if (node.Symbol is Addition) {
262        strBuilder.Append(@" \right) ");
263      } else if (node.Symbol is Subtraction) {
264        strBuilder.Append(@" \right) ");
265      } else if (node.Symbol is Multiplication) {
266      } else if (node.Symbol is Division) {
267        strBuilder.Append(" } ");
268        for (int i = 2; i < node.SubtreeCount; i++)
269          strBuilder.Append(" } ");
270      } else if (node.Symbol is Average) {
271        strBuilder.Append(@" \right) ");
272      } else if (node.Symbol is Logarithm) {
273        strBuilder.Append(@" \right) ");
274      } else if (node.Symbol is Exponential) {
275        strBuilder.Append(@" \right) ");
276      } else if (node.Symbol is Square) {
277        strBuilder.Append(@"\right)^2");
278      } else if (node.Symbol is SquareRoot) {
279        strBuilder.Append(@"}");
280      } else if (node.Symbol is Sine) {
281        strBuilder.Append(@" \right) ");
282      } else if (node.Symbol is Cosine) {
283        strBuilder.Append(@" \right) ");
284      } else if (node.Symbol is Tangent) {
285        strBuilder.Append(@" \right) ");
286      } else if (node.Symbol is GreaterThan) {
287        strBuilder.Append(@" \right) ");
288      } else if (node.Symbol is LessThan) {
289        strBuilder.Append(@" \right) ");
290      } else if (node.Symbol is And) {
291        strBuilder.Append(@" > 0 \right) \right) ");
292      } else if (node.Symbol is Or) {
293        strBuilder.Append(@" > 0 \right) \right) ");
294      } else if (node.Symbol is Not) {
295        strBuilder.Append(@" \right) ");
296      } else if (node.Symbol is IfThenElse) {
297        strBuilder.Append(@" \right) ");
298      } else if (node.Symbol is Constant) {
299      } else if (node.Symbol is LaggedVariable) {
300      } else if (node.Symbol is Variable) {
301      } else if (node.Symbol is ProgramRootSymbol) {
302        strBuilder
303          .AppendLine("\\end{align*}")
304          .AppendLine("\\begin{align*}")
305          .AppendLine("\\nonumber");
306        // output all constant values
307        if (constants.Count > 0) {
308          int i = 0;
309          foreach (var constant in constants) {
310            // replace "." with ".&" to align decimal points
311            var constStr = string.Format(System.Globalization.NumberFormatInfo.InvariantInfo, "{0:G5}", constant);
312            if (!constStr.Contains(".")) constStr = constStr + ".0";
313            constStr = constStr.Replace(".", "\\negthickspace&.");  // fix problem in rendering of aligned expressions
314            strBuilder.Append("c_{" + i + "}& = & " + constStr);
315            strBuilder.Append(@"\\");
316            i++;
317          }
318        }
319        strBuilder.AppendLine("\\end{align*}");
320      } else if (node.Symbol is Defun) {
321      } else if (node.Symbol is InvokeFunction) {
322        strBuilder.Append(@" \right) ");
323      } else if (node.Symbol is StartSymbol) {
324      } else if (node.Symbol is Argument) {
325      } else if (node.Symbol is Derivative) {
326        strBuilder.Append(@" \right) }{dt} ");
327      } else if (node.Symbol is TimeLag) {
328        var laggedNode = node as ILaggedTreeNode;
329        currentLag -= laggedNode.Lag;
330      } else if (node.Symbol is Power) {
331        strBuilder.Append(@" \right) } ");
332      } else if (node.Symbol is Root) {
333        strBuilder.Append(@" \right) } } ");
334      } else if (node.Symbol is Integral) {
335        strBuilder.Append(@" \right) ");
336      } else if (node.Symbol is VariableCondition) {
337        strBuilder.Append(@"\right) ");
338      } else {
339        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
340      }
341    }
342
343    private string LagToString(int lag) {
344      if (lag < 0) {
345        return "(t" + lag + ")";
346      } else if (lag > 0) {
347        return "(t+" + lag + ")";
348      } else return "";
349    }
350
351    private string EscapeLatexString(string s) {
352      return "\\text{" +
353        s
354         .Replace("\\", "\\\\")
355         .Replace("{", "\\{")
356         .Replace("}", "\\}")
357        + "}";
358    }
359  }
360}
Note: See TracBrowser for help on using the repository browser.