Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 7136 was 7136, checked in by mkommend, 12 years ago

#1686: Corrected the usage of parenthesis in the latex formatter.

File size: 13.8 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 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("% needs \\usepackage{amsmath}");
60        strBuilder.AppendLine("\\begin{align*}");
61        strBuilder.AppendLine("\\nonumber");
62        strBuilder.AppendLine(FormatRecursively(symbolicExpressionTree.Root));
63        strBuilder.AppendLine("\\end{align*}");
64        return strBuilder.ToString();
65      }
66      catch (NotImplementedException ex) {
67        return ex.Message + Environment.NewLine + ex.StackTrace;
68      }
69    }
70
71    private string FormatRecursively(ISymbolicExpressionTreeNode node) {
72      StringBuilder strBuilder = new StringBuilder();
73      currentLag = 0;
74      FormatBegin(node, strBuilder);
75
76      if (node.SubtreeCount > 0) {
77        strBuilder.Append(FormatRecursively(node.GetSubtree(0)));
78      }
79      foreach (SymbolicExpressionTreeNode subTree in node.Subtrees.Skip(1)) {
80        FormatSep(node, strBuilder);
81        // format the whole subtree
82        strBuilder.Append(FormatRecursively(subTree));
83      }
84
85      FormatEnd(node, strBuilder);
86
87      return strBuilder.ToString();
88    }
89
90    private void FormatBegin(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
91      if (node.Symbol is Addition) {
92        strBuilder.Append(@" ( ");
93      } else if (node.Symbol is Subtraction) {
94        if (node.SubtreeCount == 1) {
95          strBuilder.Append(@"- ( ");
96        } else {
97          strBuilder.Append(@" ( ");
98        }
99      } else if (node.Symbol is Multiplication) {
100      } else if (node.Symbol is Division) {
101        if (node.SubtreeCount == 1) {
102          strBuilder.Append(@" \cfrac{1}{");
103        } else {
104          strBuilder.Append(@" \cfrac{ ");
105        }
106      } else if (node.Symbol is Average) {
107        // skip output of (1/1) if only one subtree
108        if (node.SubtreeCount > 1) {
109          strBuilder.Append(@" \cfrac{1}{" + node.SubtreeCount + @"}");
110        }
111        strBuilder.Append(@" ( ");
112      } else if (node.Symbol is Logarithm) {
113        strBuilder.Append(@"\log ( ");
114      } else if (node.Symbol is Exponential) {
115        strBuilder.Append(@"\exp ( ");
116      } else if (node.Symbol is Sine) {
117        strBuilder.Append(@"\sin ( ");
118      } else if (node.Symbol is Cosine) {
119        strBuilder.Append(@"\cos ( ");
120      } else if (node.Symbol is Tangent) {
121        strBuilder.Append(@"\tan ( ");
122      } else if (node.Symbol is GreaterThan) {
123        strBuilder.Append(@"  ( ");
124      } else if (node.Symbol is LessThan) {
125        strBuilder.Append(@"  ( ");
126      } else if (node.Symbol is And) {
127        strBuilder.Append(@"   ( ");
128      } else if (node.Symbol is Or) {
129        strBuilder.Append(@"   ( ");
130      } else if (node.Symbol is Not) {
131        strBuilder.Append(@" \neg ( ");
132      } else if (node.Symbol is IfThenElse) {
133        strBuilder.Append(@" \operatorname{if}  ( 0 < ");
134      } else if (node.Symbol is Constant) {
135        strBuilder.Append("c_{" + constants.Count + "} ");
136        var constNode = node as ConstantTreeNode;
137        constants.Add(constNode.Value);
138      } else if (node.Symbol is LaggedVariable) {
139        var laggedVarNode = node as LaggedVariableTreeNode;
140        if (!laggedVarNode.Weight.IsAlmost(1.0)) {
141          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
142          constants.Add(laggedVarNode.Weight);
143        }
144        strBuilder.Append(EscapeLatexString(laggedVarNode.VariableName));
145        strBuilder.Append(LagToString(currentLag + laggedVarNode.Lag));
146
147      } else if (node.Symbol is Variable) {
148        var varNode = node as VariableTreeNode;
149        if (!varNode.Weight.IsAlmost((1.0))) {
150          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
151          constants.Add(varNode.Weight);
152        }
153        strBuilder.Append(EscapeLatexString(varNode.VariableName));
154        strBuilder.Append(LagToString(currentLag));
155      } else if (node.Symbol is ProgramRootSymbol) {
156      } else if (node.Symbol is Defun) {
157        var defunNode = node as DefunTreeNode;
158        strBuilder.Append(defunNode.FunctionName + " & = ");
159      } else if (node.Symbol is InvokeFunction) {
160        var invokeNode = node as InvokeFunctionTreeNode;
161        strBuilder.Append(invokeNode.Symbol.FunctionName + @" ( ");
162      } else if (node.Symbol is StartSymbol) {
163        strBuilder.Append("Result & = ");
164      } else if (node.Symbol is Argument) {
165        var argSym = node.Symbol as Argument;
166        strBuilder.Append(" ARG+" + argSym.ArgumentIndex + " ");
167      } else if (node.Symbol is Derivative) {
168        strBuilder.Append(@" \cfrac{d ( ");
169      } else if (node.Symbol is TimeLag) {
170        var laggedNode = node as ILaggedTreeNode;
171        currentLag += laggedNode.Lag;
172      } else if (node.Symbol is Power) {
173        strBuilder.Append(@" ( ");
174      } else if (node.Symbol is Root) {
175        strBuilder.Append(@" ( ");
176      } else if (node.Symbol is Integral) {
177        // actually a new variable for t is needed in all subtrees (TODO)
178        var laggedTreeNode = node as ILaggedTreeNode;
179        strBuilder.Append(@"\sum_{t=" + (laggedTreeNode.Lag + currentLag) + @"}^0 ( ");
180      } else if (node.Symbol is VariableCondition) {
181        var conditionTreeNode = node as VariableConditionTreeNode;
182        string p = @"1 /  1 + \exp  - c_{" + constants.Count + "} ";
183        constants.Add(conditionTreeNode.Slope);
184        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"}   ";
185        constants.Add(conditionTreeNode.Threshold);
186        strBuilder.Append(@" ( " + p + @"\cdot ");
187      } else {
188        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
189      }
190    }
191
192    private void FormatSep(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
193      if (node.Symbol is Addition) {
194        strBuilder.Append(" + ");
195      } else if (node.Symbol is Subtraction) {
196        strBuilder.Append(" - ");
197      } else if (node.Symbol is Multiplication) {
198        strBuilder.Append(@" \cdot ");
199      } else if (node.Symbol is Division) {
200        strBuilder.Append(@" }{ \cfrac{ ");
201      } else if (node.Symbol is Average) {
202        strBuilder.Append(@" + ");
203      } else if (node.Symbol is Logarithm) {
204        throw new InvalidOperationException();
205      } else if (node.Symbol is Exponential) {
206        throw new InvalidOperationException();
207      } else if (node.Symbol is Sine) {
208        throw new InvalidOperationException();
209      } else if (node.Symbol is Cosine) {
210        throw new InvalidOperationException();
211      } else if (node.Symbol is Tangent) {
212        throw new InvalidOperationException();
213      } else if (node.Symbol is GreaterThan) {
214        strBuilder.Append(@" > ");
215      } else if (node.Symbol is LessThan) {
216        strBuilder.Append(@" < ");
217      } else if (node.Symbol is And) {
218        strBuilder.Append(@" > 0  ) \land (");
219      } else if (node.Symbol is Or) {
220        strBuilder.Append(@" > 0  ) \lor (");
221      } else if (node.Symbol is Not) {
222        throw new InvalidOperationException();
223      } else if (node.Symbol is IfThenElse) {
224        strBuilder.Append(@" ) , (");
225      } else if (node.Symbol is ProgramRootSymbol) {
226        strBuilder.Append(@"\\" + Environment.NewLine);
227      } else if (node.Symbol is Defun) {
228      } else if (node.Symbol is InvokeFunction) {
229        strBuilder.Append(" , ");
230      } else if (node.Symbol is StartSymbol) {
231        strBuilder.Append(@"\\" + Environment.NewLine + " & ");
232      } else if (node.Symbol is Power) {
233        strBuilder.Append(@") ^ { \operatorname{round} (");
234      } else if (node.Symbol is Root) {
235        strBuilder.Append(@") ^ {  \cfrac{1}{ \operatorname{round} (");
236      } else if (node.Symbol is VariableCondition) {
237        var conditionTreeNode = node as VariableConditionTreeNode;
238        string p = @"1 / ( 1 + \exp ( - c_{" + constants.Count + "} ";
239        constants.Add(conditionTreeNode.Slope);
240        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"} ) ) )   ";
241        constants.Add(conditionTreeNode.Threshold);
242        strBuilder.Append(@" +  ( 1 - " + p + @" ) \cdot ");
243      } else {
244        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
245      }
246    }
247
248    private void FormatEnd(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
249      if (node.Symbol is Addition) {
250        strBuilder.Append(@" ) ");
251      } else if (node.Symbol is Subtraction) {
252        strBuilder.Append(@" ) ");
253      } else if (node.Symbol is Multiplication) {
254      } else if (node.Symbol is Division) {
255        strBuilder.Append("} ");
256        if (node.SubtreeCount > 1)
257          strBuilder.Append("{1} ");
258        for (int i = 1; i < node.SubtreeCount; i++) {
259          strBuilder.Append(" } ");
260        }
261      } else if (node.Symbol is Average) {
262        strBuilder.Append(@" ) ");
263      } else if (node.Symbol is Logarithm) {
264        strBuilder.Append(@" ) ");
265      } else if (node.Symbol is Exponential) {
266        strBuilder.Append(@" ) ");
267      } else if (node.Symbol is Sine) {
268        strBuilder.Append(@" ) ");
269      } else if (node.Symbol is Cosine) {
270        strBuilder.Append(@" ) ");
271      } else if (node.Symbol is Tangent) {
272        strBuilder.Append(@" ) ");
273      } else if (node.Symbol is GreaterThan) {
274        strBuilder.Append(@" ) ");
275      } else if (node.Symbol is LessThan) {
276        strBuilder.Append(@" ) ");
277      } else if (node.Symbol is And) {
278        strBuilder.Append(@" > 0 ) ) ");
279      } else if (node.Symbol is Or) {
280        strBuilder.Append(@" > 0 ) ) ");
281      } else if (node.Symbol is Not) {
282        strBuilder.Append(@" ) ");
283      } else if (node.Symbol is IfThenElse) {
284        strBuilder.Append(@" ) ) ");
285      } else if (node.Symbol is Constant) {
286      } else if (node.Symbol is LaggedVariable) {
287      } else if (node.Symbol is Variable) {
288      } else if (node.Symbol is ProgramRootSymbol) {
289        // output all constant values
290        if (constants.Count > 0) {
291          int i = 0;
292          foreach (var constant in constants) {
293            strBuilder.AppendLine(@"\\");
294            strBuilder.Append("c_{" + i + "} & = " + constant);
295            i++;
296          }
297        }
298      } else if (node.Symbol is Defun) {
299      } else if (node.Symbol is InvokeFunction) {
300        strBuilder.Append(@" ) ");
301      } else if (node.Symbol is StartSymbol) {
302      } else if (node.Symbol is Argument) {
303      } else if (node.Symbol is Derivative) {
304        strBuilder.Append(@" ) }{dt} ");
305      } else if (node.Symbol is TimeLag) {
306        var laggedNode = node as ILaggedTreeNode;
307        currentLag -= laggedNode.Lag;
308      } else if (node.Symbol is Power) {
309        strBuilder.Append(@" ) } ");
310      } else if (node.Symbol is Root) {
311        strBuilder.Append(@" ) } ) } ");
312      } else if (node.Symbol is Integral) {
313        var laggedTreeNode = node as ILaggedTreeNode;
314        strBuilder.Append(@" ) ");
315      } else if (node.Symbol is VariableCondition) {
316        strBuilder.Append(@"\left) ");
317      } else {
318        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
319      }
320    }
321
322    private string LagToString(int lag) {
323      if (lag < 0) {
324        return "(t" + lag + ")";
325      } else if (lag > 0) {
326        return "(t+" + lag + ")";
327      } else return "";
328    }
329
330    private string EscapeLatexString(string s) {
331      return s.Replace(@"_", @"\_");
332    }
333  }
334}
Note: See TracBrowser for help on using the repository browser.