Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 6281 was 5817, checked in by gkronber, 14 years ago

#1418 fixed bug in LaTeX formatter.

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