Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1810 fixed bug in tree interpreter for psi function, extended IL emitting interpreter to handle the new special functions.

File size: 19.6 KB
RevLine 
[4327]1#region License Information
2/* HeuristicLab
[7259]3 * Copyright (C) 2002-2012 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
[6975]22using System;
23using System.Collections.Generic;
24using System.Linq;
[4327]25using System.Text;
[6975]26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
[4327]29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
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();
[4327]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
[5745]67    private string FormatRecursively(ISymbolicExpressionTreeNode node) {
[4327]68      StringBuilder strBuilder = new StringBuilder();
[5428]69      currentLag = 0;
[4327]70      FormatBegin(node, strBuilder);
71
[6803]72      if (node.SubtreeCount > 0) {
[5745]73        strBuilder.Append(FormatRecursively(node.GetSubtree(0)));
[4327]74      }
[7140]75      int i = 1;
[5745]76      foreach (SymbolicExpressionTreeNode subTree in node.Subtrees.Skip(1)) {
[7140]77        FormatSep(node, strBuilder, i);
[4327]78        // format the whole subtree
79        strBuilder.Append(FormatRecursively(subTree));
[7140]80        i++;
[4327]81      }
82
83      FormatEnd(node, strBuilder);
84
85      return strBuilder.ToString();
86    }
87
[5745]88    private void FormatBegin(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
[4327]89      if (node.Symbol is Addition) {
[7446]90        strBuilder.Append(@" \left( ");
[4327]91      } else if (node.Symbol is Subtraction) {
[6803]92        if (node.SubtreeCount == 1) {
[7446]93          strBuilder.Append(@"- \left( ");
[5428]94        } else {
[7446]95          strBuilder.Append(@" \left( ");
[5428]96        }
[4327]97      } else if (node.Symbol is Multiplication) {
98      } else if (node.Symbol is Division) {
[6803]99        if (node.SubtreeCount == 1) {
[5428]100          strBuilder.Append(@" \cfrac{1}{");
101        } else {
102          strBuilder.Append(@" \cfrac{ ");
103        }
[4969]104      } else if (node.Symbol is Average) {
[5428]105        // skip output of (1/1) if only one subtree
[6803]106        if (node.SubtreeCount > 1) {
107          strBuilder.Append(@" \cfrac{1}{" + node.SubtreeCount + @"}");
[5428]108        }
[7446]109        strBuilder.Append(@" \left( ");
[4969]110      } else if (node.Symbol is Logarithm) {
[7446]111        strBuilder.Append(@"\log \left( ");
[4969]112      } else if (node.Symbol is Exponential) {
[7446]113        strBuilder.Append(@"\exp \left( ");
[7695]114      } else if (node.Symbol is Square) {
115        strBuilder.Append(@"\left(");
116      } else if (node.Symbol is SquareRoot) {
117        strBuilder.Append(@"\sqrt{");
[4969]118      } else if (node.Symbol is Sine) {
[7446]119        strBuilder.Append(@"\sin \left( ");
[4969]120      } else if (node.Symbol is Cosine) {
[7446]121        strBuilder.Append(@"\cos \left( ");
[4969]122      } else if (node.Symbol is Tangent) {
[7446]123        strBuilder.Append(@"\tan \left( ");
[7696]124      } else if (node.Symbol is AiryA) {
125        strBuilder.Append(@"\operatorname{airy}_a \left( ");
126      } else if (node.Symbol is AiryB) {
127        strBuilder.Append(@"\operatorname{airy}_b \left( ");
128      } else if (node.Symbol is Bessel) {
129        strBuilder.Append(@"\operatorname{bessel}_1 \left( ");
130      } else if (node.Symbol is CosineIntegral) {
131        strBuilder.Append(@"\operatorname{cosInt} \left( ");
132      } else if (node.Symbol is Dawson) {
133        strBuilder.Append(@"\operatorname{dawson} \left( ");
134      } else if (node.Symbol is Erf) {
135        strBuilder.Append(@"\operatorname{erf} \left( ");
136      } else if (node.Symbol is ExponentialIntegralEi) {
137        strBuilder.Append(@"\operatorname{expInt}_i \left( ");
138      } else if (node.Symbol is FresnelCosineIntegral) {
[7708]139        strBuilder.Append(@"\operatorname{fresnel}_\operatorname{cosInt} \left( ");
[7696]140      } else if (node.Symbol is FresnelSineIntegral) {
[7708]141        strBuilder.Append(@"\operatorname{fresnel}_\operatorname{sinInt} \left( ");
[7696]142      } else if (node.Symbol is Gamma) {
143        strBuilder.Append(@"\Gamma \left( ");
144      } else if (node.Symbol is HyperbolicCosineIntegral) {
145        strBuilder.Append(@"\operatorname{hypCosInt} \left( ");
146      } else if (node.Symbol is HyperbolicSineIntegral) {
147        strBuilder.Append(@"\operatorname{hypSinInt} \left( ");
[7697]148      } else if (node.Symbol is Norm) {
149        strBuilder.Append(@"\operatorname{norm} \left( ");
[7696]150      } else if (node.Symbol is Psi) {
151        strBuilder.Append(@"\operatorname{digamma} \left( ");
152      } else if (node.Symbol is SineIntegral) {
153        strBuilder.Append(@"\operatorname{sinInt} \left( ");
[4969]154      } else if (node.Symbol is GreaterThan) {
[7446]155        strBuilder.Append(@"  \left( ");
[4969]156      } else if (node.Symbol is LessThan) {
[7446]157        strBuilder.Append(@"  \left( ");
[4969]158      } else if (node.Symbol is And) {
[7451]159        strBuilder.Append(@"  \left( \left( ");
[4969]160      } else if (node.Symbol is Or) {
[7451]161        strBuilder.Append(@"   \left( \left( ");
[4969]162      } else if (node.Symbol is Not) {
[7446]163        strBuilder.Append(@" \neg \left( ");
[4969]164      } else if (node.Symbol is IfThenElse) {
[7446]165        strBuilder.Append(@" \operatorname{if}  \left( ");
[4327]166      } else if (node.Symbol is Constant) {
[5428]167        strBuilder.Append("c_{" + constants.Count + "} ");
[4969]168        var constNode = node as ConstantTreeNode;
169        constants.Add(constNode.Value);
[5428]170      } else if (node.Symbol is LaggedVariable) {
171        var laggedVarNode = node as LaggedVariableTreeNode;
[7038]172        if (!laggedVarNode.Weight.IsAlmost(1.0)) {
173          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
174          constants.Add(laggedVarNode.Weight);
175        }
176        strBuilder.Append(EscapeLatexString(laggedVarNode.VariableName));
[5428]177        strBuilder.Append(LagToString(currentLag + laggedVarNode.Lag));
[7038]178
179      } else if (node.Symbol is Variable) {
[4327]180        var varNode = node as VariableTreeNode;
[7038]181        if (!varNode.Weight.IsAlmost((1.0))) {
182          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
183          constants.Add(varNode.Weight);
184        }
185        strBuilder.Append(EscapeLatexString(varNode.VariableName));
[5428]186        strBuilder.Append(LagToString(currentLag));
[4327]187      } else if (node.Symbol is ProgramRootSymbol) {
[7446]188        strBuilder
189          .AppendLine("\\begin{align*}")
190          .AppendLine("\\nonumber");
[4327]191      } else if (node.Symbol is Defun) {
192        var defunNode = node as DefunTreeNode;
[4969]193        strBuilder.Append(defunNode.FunctionName + " & = ");
[4327]194      } else if (node.Symbol is InvokeFunction) {
195        var invokeNode = node as InvokeFunctionTreeNode;
[7446]196        strBuilder.Append(invokeNode.Symbol.FunctionName + @" \left( ");
[4327]197      } else if (node.Symbol is StartSymbol) {
[4969]198        strBuilder.Append("Result & = ");
[4900]199      } else if (node.Symbol is Argument) {
[4327]200        var argSym = node.Symbol as Argument;
[4900]201        strBuilder.Append(" ARG+" + argSym.ArgumentIndex + " ");
[5428]202      } else if (node.Symbol is Derivative) {
[7446]203        strBuilder.Append(@" \cfrac{d \left( ");
[5428]204      } else if (node.Symbol is TimeLag) {
205        var laggedNode = node as ILaggedTreeNode;
206        currentLag += laggedNode.Lag;
207      } else if (node.Symbol is Power) {
[7446]208        strBuilder.Append(@" \left( ");
[5428]209      } else if (node.Symbol is Root) {
[7446]210        strBuilder.Append(@" \left( ");
[5428]211      } else if (node.Symbol is Integral) {
212        // actually a new variable for t is needed in all subtrees (TODO)
213        var laggedTreeNode = node as ILaggedTreeNode;
[7446]214        strBuilder.Append(@"\sum_{t=" + (laggedTreeNode.Lag + currentLag) + @"}^0 \left( ");
[5468]215      } else if (node.Symbol is VariableCondition) {
216        var conditionTreeNode = node as VariableConditionTreeNode;
[7038]217        string p = @"1 /  1 + \exp  - c_{" + constants.Count + "} ";
[5468]218        constants.Add(conditionTreeNode.Slope);
[7038]219        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"}   ";
[5468]220        constants.Add(conditionTreeNode.Threshold);
[7446]221        strBuilder.Append(@" \left( " + p + @"\cdot ");
[4327]222      } else {
223        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
224      }
225    }
226
[7140]227    private void FormatSep(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, int step) {
[4327]228      if (node.Symbol is Addition) {
229        strBuilder.Append(" + ");
230      } else if (node.Symbol is Subtraction) {
231        strBuilder.Append(" - ");
232      } else if (node.Symbol is Multiplication) {
[5428]233        strBuilder.Append(@" \cdot ");
[4327]234      } else if (node.Symbol is Division) {
[7140]235        if (step + 1 == node.SubtreeCount)
236          strBuilder.Append(@"}{");
237        else
238          strBuilder.Append(@" }{ \cfrac{ ");
[4969]239      } else if (node.Symbol is Average) {
240        strBuilder.Append(@" + ");
241      } else if (node.Symbol is Logarithm) {
242        throw new InvalidOperationException();
243      } else if (node.Symbol is Exponential) {
244        throw new InvalidOperationException();
[7695]245      } else if (node.Symbol is Square) {
246        throw new InvalidOperationException();
247      } else if (node.Symbol is SquareRoot) {
248        throw new InvalidOperationException();
[4969]249      } else if (node.Symbol is Sine) {
250        throw new InvalidOperationException();
251      } else if (node.Symbol is Cosine) {
252        throw new InvalidOperationException();
253      } else if (node.Symbol is Tangent) {
254        throw new InvalidOperationException();
[7696]255      } else if (node.Symbol is AiryA) {
256        throw new InvalidOperationException();
257      } else if (node.Symbol is AiryB) {
258        throw new InvalidOperationException();
259      } else if (node.Symbol is Bessel) {
260        throw new InvalidOperationException();
261      } else if (node.Symbol is CosineIntegral) {
262        throw new InvalidOperationException();
263      } else if (node.Symbol is Dawson) {
264        throw new InvalidOperationException();
265      } else if (node.Symbol is Erf) {
266        throw new InvalidOperationException();
267      } else if (node.Symbol is ExponentialIntegralEi) {
268        throw new InvalidOperationException();
269      } else if (node.Symbol is FresnelCosineIntegral) {
270        throw new InvalidOperationException();
271      } else if (node.Symbol is FresnelSineIntegral) {
272        throw new InvalidOperationException();
273      } else if (node.Symbol is Gamma) {
274        throw new InvalidOperationException();
275      } else if (node.Symbol is HyperbolicCosineIntegral) {
276        throw new InvalidOperationException();
277      } else if (node.Symbol is HyperbolicSineIntegral) {
278        throw new InvalidOperationException();
[7697]279      } else if (node.Symbol is Norm) {
280        throw new InvalidOperationException();
[7696]281      } else if (node.Symbol is Psi) {
282        throw new InvalidOperationException();
283      } else if (node.Symbol is SineIntegral) {
284        throw new InvalidOperationException();
[4969]285      } else if (node.Symbol is GreaterThan) {
286        strBuilder.Append(@" > ");
287      } else if (node.Symbol is LessThan) {
288        strBuilder.Append(@" < ");
289      } else if (node.Symbol is And) {
[7446]290        strBuilder.Append(@" > 0  \right) \land \left(");
[4969]291      } else if (node.Symbol is Or) {
[7446]292        strBuilder.Append(@" > 0  \right) \lor \left(");
[4969]293      } else if (node.Symbol is Not) {
294        throw new InvalidOperationException();
295      } else if (node.Symbol is IfThenElse) {
[7446]296        strBuilder.Append(@" , ");
[4327]297      } else if (node.Symbol is ProgramRootSymbol) {
298        strBuilder.Append(@"\\" + Environment.NewLine);
299      } else if (node.Symbol is Defun) {
300      } else if (node.Symbol is InvokeFunction) {
301        strBuilder.Append(" , ");
302      } else if (node.Symbol is StartSymbol) {
[4969]303        strBuilder.Append(@"\\" + Environment.NewLine + " & ");
[5428]304      } else if (node.Symbol is Power) {
[7446]305        strBuilder.Append(@"\right) ^ { \operatorname{round} \left(");
[5428]306      } else if (node.Symbol is Root) {
[7446]307        strBuilder.Append(@"\right) ^ {  \cfrac{1}{ \operatorname{round} \left(");
[5468]308      } else if (node.Symbol is VariableCondition) {
309        var conditionTreeNode = node as VariableConditionTreeNode;
[7446]310        string p = @"1 / \left( 1 + \exp \left( - c_{" + constants.Count + "} ";
[5468]311        constants.Add(conditionTreeNode.Slope);
[7446]312        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"} \right) \right) \right)   ";
[5468]313        constants.Add(conditionTreeNode.Threshold);
[7446]314        strBuilder.Append(@" +  \left( 1 - " + p + @" \right) \cdot ");
[4327]315      } else {
316        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
317      }
318    }
319
[5745]320    private void FormatEnd(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
[4327]321      if (node.Symbol is Addition) {
[7446]322        strBuilder.Append(@" \right) ");
[4327]323      } else if (node.Symbol is Subtraction) {
[7446]324        strBuilder.Append(@" \right) ");
[4327]325      } else if (node.Symbol is Multiplication) {
326      } else if (node.Symbol is Division) {
[7140]327        strBuilder.Append(" } ");
328        for (int i = 2; i < node.SubtreeCount; i++)
[5428]329          strBuilder.Append(" } ");
[4969]330      } else if (node.Symbol is Average) {
[7446]331        strBuilder.Append(@" \right) ");
[4969]332      } else if (node.Symbol is Logarithm) {
[7446]333        strBuilder.Append(@" \right) ");
[4969]334      } else if (node.Symbol is Exponential) {
[7446]335        strBuilder.Append(@" \right) ");
[7695]336      } else if (node.Symbol is Square) {
337        strBuilder.Append(@"\right)^2");
338      } else if (node.Symbol is SquareRoot) {
339        strBuilder.Append(@"}");
[4969]340      } else if (node.Symbol is Sine) {
[7446]341        strBuilder.Append(@" \right) ");
[4969]342      } else if (node.Symbol is Cosine) {
[7446]343        strBuilder.Append(@" \right) ");
[4969]344      } else if (node.Symbol is Tangent) {
[7446]345        strBuilder.Append(@" \right) ");
[7696]346      } else if (node.Symbol is AiryA) {
347        strBuilder.Append(@" \right) ");
348      } else if (node.Symbol is AiryB) {
349        strBuilder.Append(@" \right) ");
350      } else if (node.Symbol is Bessel) {
351        strBuilder.Append(@" \right) ");
352      } else if (node.Symbol is CosineIntegral) {
353        strBuilder.Append(@" \right) ");
354      } else if (node.Symbol is Dawson) {
355        strBuilder.Append(@" \right) ");
356      } else if (node.Symbol is Erf) {
357        strBuilder.Append(@" \right) ");
358      } else if (node.Symbol is ExponentialIntegralEi) {
359        strBuilder.Append(@" \right) ");
360      } else if (node.Symbol is FresnelCosineIntegral) {
361        strBuilder.Append(@" \right) ");
362      } else if (node.Symbol is FresnelSineIntegral) {
363        strBuilder.Append(@" \right) ");
364      } else if (node.Symbol is Gamma) {
365        strBuilder.Append(@" \right) ");
366      } else if (node.Symbol is HyperbolicCosineIntegral) {
367        strBuilder.Append(@" \right) ");
368      } else if (node.Symbol is HyperbolicSineIntegral) {
369        strBuilder.Append(@" \right) ");
[7697]370      } else if (node.Symbol is Norm) {
371        strBuilder.Append(@" \right) ");
[7696]372      } else if (node.Symbol is Psi) {
373        strBuilder.Append(@" \right) ");
374      } else if (node.Symbol is SineIntegral) {
375        strBuilder.Append(@" \right) ");
[4969]376      } else if (node.Symbol is GreaterThan) {
[7446]377        strBuilder.Append(@" \right) ");
[4969]378      } else if (node.Symbol is LessThan) {
[7446]379        strBuilder.Append(@" \right) ");
[4969]380      } else if (node.Symbol is And) {
[7446]381        strBuilder.Append(@" > 0 \right) \right) ");
[4969]382      } else if (node.Symbol is Or) {
[7446]383        strBuilder.Append(@" > 0 \right) \right) ");
[4969]384      } else if (node.Symbol is Not) {
[7446]385        strBuilder.Append(@" \right) ");
[4969]386      } else if (node.Symbol is IfThenElse) {
[7446]387        strBuilder.Append(@" \right) ");
[4327]388      } else if (node.Symbol is Constant) {
[5428]389      } else if (node.Symbol is LaggedVariable) {
[7038]390      } else if (node.Symbol is Variable) {
[4327]391      } else if (node.Symbol is ProgramRootSymbol) {
[7446]392        strBuilder
393          .AppendLine("\\end{align*}")
394          .AppendLine("\\begin{align*}")
395          .AppendLine("\\nonumber");
[4969]396        // output all constant values
397        if (constants.Count > 0) {
398          int i = 0;
399          foreach (var constant in constants) {
[7446]400            // replace "." with ".&" to align decimal points
401            var constStr = string.Format(System.Globalization.NumberFormatInfo.InvariantInfo, "{0:G5}", constant);
[7451]402            if (!constStr.Contains(".")) constStr = constStr + ".0";
[7446]403            constStr = constStr.Replace(".", "\\negthickspace&.");  // fix problem in rendering of aligned expressions
404            strBuilder.Append("c_{" + i + "}& = & " + constStr);
405            strBuilder.Append(@"\\");
[4969]406            i++;
407          }
408        }
[7446]409        strBuilder.AppendLine("\\end{align*}");
[4327]410      } else if (node.Symbol is Defun) {
411      } else if (node.Symbol is InvokeFunction) {
[7446]412        strBuilder.Append(@" \right) ");
[4327]413      } else if (node.Symbol is StartSymbol) {
414      } else if (node.Symbol is Argument) {
[5428]415      } else if (node.Symbol is Derivative) {
[7446]416        strBuilder.Append(@" \right) }{dt} ");
[5428]417      } else if (node.Symbol is TimeLag) {
418        var laggedNode = node as ILaggedTreeNode;
419        currentLag -= laggedNode.Lag;
420      } else if (node.Symbol is Power) {
[7446]421        strBuilder.Append(@" \right) } ");
[5428]422      } else if (node.Symbol is Root) {
[7446]423        strBuilder.Append(@" \right) } } ");
[5428]424      } else if (node.Symbol is Integral) {
[7446]425        strBuilder.Append(@" \right) ");
[5468]426      } else if (node.Symbol is VariableCondition) {
[7446]427        strBuilder.Append(@"\right) ");
[4327]428      } else {
429        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
430      }
431    }
[6975]432
[5428]433    private string LagToString(int lag) {
434      if (lag < 0) {
435        return "(t" + lag + ")";
436      } else if (lag > 0) {
437        return "(t+" + lag + ")";
[7038]438      } else return "";
[5428]439    }
[6975]440
441    private string EscapeLatexString(string s) {
[7446]442      return "\\text{" +
443        s
444         .Replace("\\", "\\\\")
445         .Replace("{", "\\{")
446         .Replace("}", "\\}")
447        + "}";
[6975]448    }
[4327]449  }
450}
Note: See TracBrowser for help on using the repository browser.