Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.3/Symbolic/Formatters/SymbolicExpressionTreeLatexFormatter.cs @ 5428

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

#1152 Merged LaTeX formatter from trunk and implemented formatting rules for new symbols (related to dynamic models).

File size: 12.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.Text;
23using System.Linq;
24using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
25using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
26using System.Collections.Generic;
27using HeuristicLab.Problems.DataAnalysis.Symbolic.Symbols;
28using System;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Symbols;
30using HeuristicLab.Core;
31using HeuristicLab.Common;
32
33namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Formatters {
34  [Item("LaTeX String Formatter", "Formatter for symbolic expression trees for import into LaTeX documents.")]
35  public sealed class SymbolicExpressionTreeLatexFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
36    private List<double> constants;
37    private int currentLag;
38
39    public SymbolicExpressionTreeLatexFormatter()
40      : base("LaTeX String Formatter", "Formatter for symbolic expression trees for import into LaTeX documents.") {
41      constants = new List<double>();
42    }
43    private SymbolicExpressionTreeLatexFormatter(SymbolicExpressionTreeLatexFormatter original, Cloner cloner)
44      : base(original, cloner) {
45      constants = new List<double>(original.constants);
46    }
47
48    public override IDeepCloneable Clone(Cloner cloner) {
49      return new SymbolicExpressionTreeLatexFormatter(this, cloner);
50    }
51
52    public string Format(SymbolicExpressionTree symbolicExpressionTree) {
53      try {
54        StringBuilder strBuilder = new StringBuilder();
55        constants.Clear();
56        strBuilder.AppendLine("% needs \\usepackage{amsmath}");
57        strBuilder.AppendLine("\\begin{align}");
58        strBuilder.AppendLine(FormatRecursively(symbolicExpressionTree.Root));
59        strBuilder.AppendLine("\\end{align}");
60        return strBuilder.ToString();
61      }
62      catch (NotImplementedException ex) {
63        return ex.Message + Environment.NewLine + ex.StackTrace;
64      }
65    }
66
67    private string FormatRecursively(SymbolicExpressionTreeNode node) {
68      StringBuilder strBuilder = new StringBuilder();
69      currentLag = 0;
70      FormatBegin(node, strBuilder);
71
72      if (node.SubTrees.Count > 0) {
73        strBuilder.Append(FormatRecursively(node.SubTrees[0]));
74      }
75      foreach (SymbolicExpressionTreeNode subTree in node.SubTrees.Skip(1)) {
76        FormatSep(node, strBuilder);
77        // format the whole subtree
78        strBuilder.Append(FormatRecursively(subTree));
79      }
80
81      FormatEnd(node, strBuilder);
82
83      return strBuilder.ToString();
84    }
85
86    private void FormatBegin(SymbolicExpressionTreeNode node, StringBuilder strBuilder) {
87      if (node.Symbol is Addition) {
88        strBuilder.Append(@" \left( ");
89      } else if (node.Symbol is Subtraction) {
90        if (node.SubTrees.Count == 1) {
91          strBuilder.Append(@"- \left(");
92        } else {
93          strBuilder.Append(@" \left( ");
94        }
95      } else if (node.Symbol is Multiplication) {
96      } else if (node.Symbol is Division) {
97        if (node.SubTrees.Count == 1) {
98          strBuilder.Append(@" \cfrac{1}{");
99        } else {
100          strBuilder.Append(@" \cfrac{ ");
101        }
102      } else if (node.Symbol is Average) {
103        // skip output of (1/1) if only one subtree
104        if (node.SubTrees.Count > 1) {
105          strBuilder.Append(@" \cfrac{1}{" + node.SubTrees.Count + @"}");
106        }
107        strBuilder.Append(@" \left(");
108      } else if (node.Symbol is Logarithm) {
109        strBuilder.Append(@"\log \left(");
110      } else if (node.Symbol is Exponential) {
111        strBuilder.Append(@"\exp \left(");
112      } else if (node.Symbol is Sine) {
113        strBuilder.Append(@"\sin \left(");
114      } else if (node.Symbol is Cosine) {
115        strBuilder.Append(@"\cos \left(");
116      } else if (node.Symbol is Tangent) {
117        strBuilder.Append(@"\tan \left(");
118      } else if (node.Symbol is GreaterThan) {
119        strBuilder.Append(@" \left( ");
120      } else if (node.Symbol is LessThan) {
121        strBuilder.Append(@" \left( ");
122      } else if (node.Symbol is And) {
123        strBuilder.Append(@" \left( \left( ");
124      } else if (node.Symbol is Or) {
125        strBuilder.Append(@" \left( \left( ");
126      } else if (node.Symbol is Not) {
127        strBuilder.Append(@" -1.0 \cdot \left( ");
128      } else if (node.Symbol is IfThenElse) {
129        strBuilder.Append(@"\left( \operatorname{if} \left( 0 < ");
130      } else if (node.Symbol is Constant) {
131        strBuilder.Append("c_{" + constants.Count + "} ");
132        var constNode = node as ConstantTreeNode;
133        constants.Add(constNode.Value);
134      } else if (node.Symbol is LaggedVariable) {
135        var laggedVarNode = node as LaggedVariableTreeNode;
136        strBuilder.Append("c_{" + constants.Count + "} " + laggedVarNode.VariableName);
137        strBuilder.Append(LagToString(currentLag + laggedVarNode.Lag));
138        constants.Add(laggedVarNode.Weight);
139      } else if (node.Symbol is HeuristicLab.Problems.DataAnalysis.Symbolic.Symbols.Variable) {
140        var varNode = node as VariableTreeNode;
141        strBuilder.Append("c_{" + constants.Count + "} " + varNode.VariableName);
142        strBuilder.Append(LagToString(currentLag));
143        constants.Add(varNode.Weight);
144      } else if (node.Symbol is ProgramRootSymbol) {
145      } else if (node.Symbol is Defun) {
146        var defunNode = node as DefunTreeNode;
147        strBuilder.Append(defunNode.FunctionName + " & = ");
148      } else if (node.Symbol is InvokeFunction) {
149        var invokeNode = node as InvokeFunctionTreeNode;
150        strBuilder.Append(invokeNode.Symbol.FunctionName + @" \left( ");
151      } else if (node.Symbol is StartSymbol) {
152        strBuilder.Append("Result & = ");
153      } else if (node.Symbol is Argument) {
154        var argSym = node.Symbol as Argument;
155        strBuilder.Append(" ARG+" + argSym.ArgumentIndex + " ");
156      } else if (node.Symbol is Derivative) {
157        strBuilder.Append(@" \cfrac{d \left(");
158      } else if (node.Symbol is TimeLag) {
159        var laggedNode = node as ILaggedTreeNode;
160        currentLag += laggedNode.Lag;
161      } else if (node.Symbol is Power) {
162        strBuilder.Append(@"\left(");
163      } else if (node.Symbol is Root) {
164        strBuilder.Append(@"\left(");
165      } else if (node.Symbol is Integral) {
166        // actually a new variable for t is needed in all subtrees (TODO)
167        var laggedTreeNode = node as ILaggedTreeNode;
168        strBuilder.Append(@"\sum_{t=" + (laggedTreeNode.Lag + currentLag) + @"}^0 \left(");
169      } else {
170        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
171      }
172    }
173
174    private void FormatSep(SymbolicExpressionTreeNode node, StringBuilder strBuilder) {
175      if (node.Symbol is Addition) {
176        strBuilder.Append(" + ");
177      } else if (node.Symbol is Subtraction) {
178        strBuilder.Append(" - ");
179      } else if (node.Symbol is Multiplication) {
180        strBuilder.Append(@" \cdot ");
181      } else if (node.Symbol is Division) {
182        strBuilder.Append(@" }{ \cfrac{ ");
183      } else if (node.Symbol is Average) {
184        strBuilder.Append(@" + ");
185      } else if (node.Symbol is Logarithm) {
186        throw new InvalidOperationException();
187      } else if (node.Symbol is Exponential) {
188        throw new InvalidOperationException();
189      } else if (node.Symbol is Sine) {
190        throw new InvalidOperationException();
191      } else if (node.Symbol is Cosine) {
192        throw new InvalidOperationException();
193      } else if (node.Symbol is Tangent) {
194        throw new InvalidOperationException();
195      } else if (node.Symbol is GreaterThan) {
196        strBuilder.Append(@" > ");
197      } else if (node.Symbol is LessThan) {
198        strBuilder.Append(@" < ");
199      } else if (node.Symbol is And) {
200        strBuilder.Append(@" > 0 \right) \land \left(");
201      } else if (node.Symbol is Or) {
202        strBuilder.Append(@" > 0 \right) \lor \left(");
203      } else if (node.Symbol is Not) {
204        throw new InvalidOperationException();
205      } else if (node.Symbol is IfThenElse) {
206        strBuilder.Append(@" \right) , \left(");
207      } else if (node.Symbol is ProgramRootSymbol) {
208        strBuilder.Append(@"\\" + Environment.NewLine);
209      } else if (node.Symbol is Defun) {
210      } else if (node.Symbol is InvokeFunction) {
211        strBuilder.Append(" , ");
212      } else if (node.Symbol is StartSymbol) {
213        strBuilder.Append(@"\\" + Environment.NewLine + " & ");
214      } else if (node.Symbol is Power) {
215        strBuilder.Append(@"\right) ^ { \operatorname{round} \left(");
216      } else if (node.Symbol is Root) {
217        strBuilder.Append(@"\right) ^ { \left( \cfrac{1}{ \operatorname{round} \left(");
218      } else {
219        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
220      }
221    }
222
223    private void FormatEnd(SymbolicExpressionTreeNode node, StringBuilder strBuilder) {
224      if (node.Symbol is Addition) {
225        strBuilder.Append(@" \right) ");
226      } else if (node.Symbol is Subtraction) {
227        strBuilder.Append(@" \right) ");
228      } else if (node.Symbol is Multiplication) {
229      } else if (node.Symbol is Division) {
230        strBuilder.Append("} ");
231        if (node.SubTrees.Count > 1)
232          strBuilder.Append("{1} ");
233        for (int i = 1; i < node.SubTrees.Count; i++) {
234          strBuilder.Append(" } ");
235        }
236      } else if (node.Symbol is Average) {
237        strBuilder.Append(@" \right)");
238      } else if (node.Symbol is Logarithm) {
239        strBuilder.Append(@" \right) ");
240      } else if (node.Symbol is Exponential) {
241        strBuilder.Append(@" \right) ");
242      } else if (node.Symbol is Sine) {
243        strBuilder.Append(@" \right) ");
244      } else if (node.Symbol is Cosine) {
245        strBuilder.Append(@" \right) ");
246      } else if (node.Symbol is Tangent) {
247        strBuilder.Append(@" \right) ");
248      } else if (node.Symbol is GreaterThan) {
249        strBuilder.Append(@" \right) ");
250      } else if (node.Symbol is LessThan) {
251        strBuilder.Append(@" \right) ");
252      } else if (node.Symbol is And) {
253        strBuilder.Append(@" > 0 \right) \right) ");
254      } else if (node.Symbol is Or) {
255        strBuilder.Append(@" > 0 \right) \right) ");
256      } else if (node.Symbol is Not) {
257        strBuilder.Append(@" \right) ");
258      } else if (node.Symbol is IfThenElse) {
259        strBuilder.Append(@" \right) \right) ");
260      } else if (node.Symbol is Constant) {
261      } else if (node.Symbol is LaggedVariable) {
262      } else if (node.Symbol is HeuristicLab.Problems.DataAnalysis.Symbolic.Symbols.Variable) {
263      } else if (node.Symbol is ProgramRootSymbol) {
264        // output all constant values
265        if (constants.Count > 0) {
266          int i = 0;
267          foreach (var constant in constants) {
268            strBuilder.AppendLine(@"\\");
269            strBuilder.Append("c_{" + i + "} & = " + constant);
270            i++;
271          }
272        }
273      } else if (node.Symbol is Defun) {
274      } else if (node.Symbol is InvokeFunction) {
275        strBuilder.Append(@" \right) ");
276      } else if (node.Symbol is StartSymbol) {
277      } else if (node.Symbol is Argument) {
278      } else if (node.Symbol is Derivative) {
279        strBuilder.Append(@" \right) }{dt} ");
280      } else if (node.Symbol is TimeLag) {
281        var laggedNode = node as ILaggedTreeNode;
282        currentLag -= laggedNode.Lag;
283      } else if (node.Symbol is Power) {
284        strBuilder.Append(@"\right) } ");
285      } else if (node.Symbol is Root) {
286        strBuilder.Append(@"\right) } \right) } ");
287      } else if (node.Symbol is Integral) {
288        var laggedTreeNode = node as ILaggedTreeNode;
289        strBuilder.Append(@"\right) ");
290      } else {
291        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
292      }
293    }
294    private string LagToString(int lag) {
295      if (lag < 0) {
296        return "(t" + lag + ")";
297      } else if (lag > 0) {
298        return "(t+" + lag + ")";
299      } else return "(t)";
300    }
301  }
302}
Note: See TracBrowser for help on using the repository browser.