Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 18242 was 18220, checked in by gkronber, 2 years ago

#3136: reintegrated structure-template GP branch into trunk

File size: 24.4 KB
RevLine 
[4327]1#region License Information
2/* HeuristicLab
[17180]3 * Copyright (C) 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;
[16702]26using HEAL.Attic;
[6975]27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
[4327]30
[5745]31namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
[4969]32  [Item("LaTeX String Formatter", "Formatter for symbolic expression trees for import into LaTeX documents.")]
[16565]33  [StorableType("D7186DFF-1596-4A58-B27D-974DF0D93E4F")]
[5745]34  public sealed class SymbolicDataAnalysisExpressionLatexFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
[18132]35    private readonly List<KeyValuePair<string, double>> parameters;
36    private int paramIdx;
[8798]37    private int targetCount;
[5428]38    private int currentLag;
[14255]39    private string targetVariable;
40    private bool containsTimeSeriesSymbol;
[4969]41
[5429]42    [StorableConstructor]
[16565]43    private SymbolicDataAnalysisExpressionLatexFormatter(StorableConstructorFlag _) : base(_) { }
[5745]44    private SymbolicDataAnalysisExpressionLatexFormatter(SymbolicDataAnalysisExpressionLatexFormatter original, Cloner cloner)
[5429]45      : base(original, cloner) {
[18132]46      parameters = new List<KeyValuePair<string, double>>(original.parameters);
47      paramIdx = original.paramIdx;
[14826]48      currentLag = original.currentLag;
49      targetCount = original.targetCount;
[5429]50    }
[5745]51    public SymbolicDataAnalysisExpressionLatexFormatter()
52      : base() {
53      Name = ItemName;
54      Description = ItemDescription;
[18132]55      parameters = new List<KeyValuePair<string, double>>();
[4969]56    }
[4327]57
[4900]58    public override IDeepCloneable Clone(Cloner cloner) {
[5745]59      return new SymbolicDataAnalysisExpressionLatexFormatter(this, cloner);
[4900]60    }
61
[5745]62    public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
[14255]63      return Format(symbolicExpressionTree, null);
64    }
65    public string Format(ISymbolicExpressionTree symbolicExpressionTree, string targetVariable) {
[4327]66      try {
67        StringBuilder strBuilder = new StringBuilder();
[18132]68        parameters.Clear();
69        paramIdx = 0;
[14255]70        this.targetVariable = targetVariable;
71        containsTimeSeriesSymbol = symbolicExpressionTree.IterateNodesBreadth().Any(n => IsTimeSeriesSymbol(n.Symbol));
[4327]72        strBuilder.AppendLine(FormatRecursively(symbolicExpressionTree.Root));
73        return strBuilder.ToString();
[14367]74      } catch (NotImplementedException ex) {
[4327]75        return ex.Message + Environment.NewLine + ex.StackTrace;
76      }
77    }
[14255]78    static bool IsTimeSeriesSymbol(ISymbol s) {
79      return s is TimeLag || s is Integral || s is Derivative || s is LaggedVariable;
80    }
[4327]81
[5745]82    private string FormatRecursively(ISymbolicExpressionTreeNode node) {
[4327]83      StringBuilder strBuilder = new StringBuilder();
[5428]84      currentLag = 0;
[4327]85      FormatBegin(node, strBuilder);
86
[6803]87      if (node.SubtreeCount > 0) {
[5745]88        strBuilder.Append(FormatRecursively(node.GetSubtree(0)));
[4327]89      }
[7140]90      int i = 1;
[14826]91      foreach (var subTree in node.Subtrees.Skip(1)) {
[7140]92        FormatSep(node, strBuilder, i);
[4327]93        // format the whole subtree
94        strBuilder.Append(FormatRecursively(subTree));
[7140]95        i++;
[4327]96      }
97
98      FormatEnd(node, strBuilder);
99
100      return strBuilder.ToString();
101    }
102
[5745]103    private void FormatBegin(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
[4327]104      if (node.Symbol is Addition) {
[7446]105        strBuilder.Append(@" \left( ");
[4327]106      } else if (node.Symbol is Subtraction) {
[6803]107        if (node.SubtreeCount == 1) {
[7446]108          strBuilder.Append(@"- \left( ");
[5428]109        } else {
[7446]110          strBuilder.Append(@" \left( ");
[5428]111        }
[4327]112      } else if (node.Symbol is Multiplication) {
113      } else if (node.Symbol is Division) {
[6803]114        if (node.SubtreeCount == 1) {
[14367]115          strBuilder.Append(@" \cfrac{1}{");
[5428]116        } else {
117          strBuilder.Append(@" \cfrac{ ");
118        }
[16802]119      } else if (node.Symbol is Absolute) {
120        strBuilder.Append(@"\operatorname{abs} \left( ");
121      } else if (node.Symbol is AnalyticQuotient) {
122        strBuilder.Append(@" \frac { ");
[4969]123      } else if (node.Symbol is Average) {
[5428]124        // skip output of (1/1) if only one subtree
[6803]125        if (node.SubtreeCount > 1) {
126          strBuilder.Append(@" \cfrac{1}{" + node.SubtreeCount + @"}");
[5428]127        }
[7446]128        strBuilder.Append(@" \left( ");
[4969]129      } else if (node.Symbol is Logarithm) {
[7446]130        strBuilder.Append(@"\log \left( ");
[4969]131      } else if (node.Symbol is Exponential) {
[7446]132        strBuilder.Append(@"\exp \left( ");
[7695]133      } else if (node.Symbol is Square) {
134        strBuilder.Append(@"\left(");
135      } else if (node.Symbol is SquareRoot) {
136        strBuilder.Append(@"\sqrt{");
[16802]137      } else if (node.Symbol is Cube) {
138        strBuilder.Append(@"\left(");
139      } else if (node.Symbol is CubeRoot) {
[16905]140        strBuilder.Append(@"\operatorname{cbrt}\left(");
[4969]141      } else if (node.Symbol is Sine) {
[7446]142        strBuilder.Append(@"\sin \left( ");
[4969]143      } else if (node.Symbol is Cosine) {
[7446]144        strBuilder.Append(@"\cos \left( ");
[4969]145      } else if (node.Symbol is Tangent) {
[7446]146        strBuilder.Append(@"\tan \left( ");
[16702]147      } else if (node.Symbol is HyperbolicTangent) {
148        strBuilder.Append(@"\tanh \left( ");
[7696]149      } else if (node.Symbol is AiryA) {
150        strBuilder.Append(@"\operatorname{airy}_a \left( ");
151      } else if (node.Symbol is AiryB) {
152        strBuilder.Append(@"\operatorname{airy}_b \left( ");
153      } else if (node.Symbol is Bessel) {
154        strBuilder.Append(@"\operatorname{bessel}_1 \left( ");
155      } else if (node.Symbol is CosineIntegral) {
156        strBuilder.Append(@"\operatorname{cosInt} \left( ");
157      } else if (node.Symbol is Dawson) {
158        strBuilder.Append(@"\operatorname{dawson} \left( ");
159      } else if (node.Symbol is Erf) {
160        strBuilder.Append(@"\operatorname{erf} \left( ");
161      } else if (node.Symbol is ExponentialIntegralEi) {
162        strBuilder.Append(@"\operatorname{expInt}_i \left( ");
163      } else if (node.Symbol is FresnelCosineIntegral) {
[7708]164        strBuilder.Append(@"\operatorname{fresnel}_\operatorname{cosInt} \left( ");
[7696]165      } else if (node.Symbol is FresnelSineIntegral) {
[7708]166        strBuilder.Append(@"\operatorname{fresnel}_\operatorname{sinInt} \left( ");
[7696]167      } else if (node.Symbol is Gamma) {
168        strBuilder.Append(@"\Gamma \left( ");
169      } else if (node.Symbol is HyperbolicCosineIntegral) {
170        strBuilder.Append(@"\operatorname{hypCosInt} \left( ");
171      } else if (node.Symbol is HyperbolicSineIntegral) {
172        strBuilder.Append(@"\operatorname{hypSinInt} \left( ");
[7697]173      } else if (node.Symbol is Norm) {
174        strBuilder.Append(@"\operatorname{norm} \left( ");
[7696]175      } else if (node.Symbol is Psi) {
176        strBuilder.Append(@"\operatorname{digamma} \left( ");
177      } else if (node.Symbol is SineIntegral) {
178        strBuilder.Append(@"\operatorname{sinInt} \left( ");
[4969]179      } else if (node.Symbol is GreaterThan) {
[7446]180        strBuilder.Append(@"  \left( ");
[4969]181      } else if (node.Symbol is LessThan) {
[7446]182        strBuilder.Append(@"  \left( ");
[4969]183      } else if (node.Symbol is And) {
[7451]184        strBuilder.Append(@"  \left( \left( ");
[4969]185      } else if (node.Symbol is Or) {
[7451]186        strBuilder.Append(@"   \left( \left( ");
[4969]187      } else if (node.Symbol is Not) {
[7446]188        strBuilder.Append(@" \neg \left( ");
[4969]189      } else if (node.Symbol is IfThenElse) {
[7446]190        strBuilder.Append(@" \operatorname{if}  \left( ");
[18132]191      } else if (node.Symbol is INumericSymbol numSy) {
192        var numName = "c_{" + paramIdx + "}";
193        paramIdx++;
194        var numericNode = node as INumericTreeNode;
195        if (numericNode.Value.IsAlmost(1.0)) {
[14367]196          strBuilder.Append("1 ");
197        } else {
[18132]198          strBuilder.Append(numName);
199          parameters.Add(new KeyValuePair<string, double>(numName, numericNode.Value));
[14367]200        }
[14826]201      } else if (node.Symbol is FactorVariable) {
202        var factorNode = node as FactorVariableTreeNode;
[18132]203        var paramName = "c_{" + paramIdx + "}";
204        strBuilder.Append(paramName + " ");
[14826]205        foreach (var e in factorNode.Symbol.GetVariableValues(factorNode.VariableName)
206          .Zip(factorNode.Weights, Tuple.Create)) {
[18132]207          parameters.Add(new KeyValuePair<string, double>("c_{" + paramIdx + ", " + EscapeLatexString(factorNode.VariableName) + "=" + EscapeLatexString(e.Item1) + "}", e.Item2));
[14826]208        }
[18132]209        paramIdx++;
[14826]210      } else if (node.Symbol is BinaryFactorVariable) {
211        var binFactorNode = node as BinaryFactorVariableTreeNode;
212        if (!binFactorNode.Weight.IsAlmost((1.0))) {
[18132]213          var paramName = "c_{" + paramIdx + "}";
214          strBuilder.Append(paramName + "  \\cdot");
215          parameters.Add(new KeyValuePair<string, double>(paramName, binFactorNode.Weight));
216          paramIdx++;
[14826]217        }
218        strBuilder.Append("(" + EscapeLatexString(binFactorNode.VariableName));
219        strBuilder.Append(LagToString(currentLag));
220        strBuilder.Append(" = " + EscapeLatexString(binFactorNode.VariableValue) + " )");
[5428]221      } else if (node.Symbol is LaggedVariable) {
222        var laggedVarNode = node as LaggedVariableTreeNode;
[7038]223        if (!laggedVarNode.Weight.IsAlmost(1.0)) {
[18132]224          var paramName = "c_{" + paramIdx + "}";
225          strBuilder.Append(paramName + "  \\cdot");
226          parameters.Add(new KeyValuePair<string, double>(paramName, laggedVarNode.Weight));
227          paramIdx++;
[7038]228        }
229        strBuilder.Append(EscapeLatexString(laggedVarNode.VariableName));
[5428]230        strBuilder.Append(LagToString(currentLag + laggedVarNode.Lag));
[7038]231
232      } else if (node.Symbol is Variable) {
[4327]233        var varNode = node as VariableTreeNode;
[7038]234        if (!varNode.Weight.IsAlmost((1.0))) {
[18132]235          var paramName = "c_{" + paramIdx + "}";
236          strBuilder.Append(paramName + "  \\cdot");
237          parameters.Add(new KeyValuePair<string, double>(paramName, varNode.Weight));
238          paramIdx++;
[7038]239        }
240        strBuilder.Append(EscapeLatexString(varNode.VariableName));
[5428]241        strBuilder.Append(LagToString(currentLag));
[4327]242      } else if (node.Symbol is ProgramRootSymbol) {
[7446]243        strBuilder
244          .AppendLine("\\begin{align*}")
245          .AppendLine("\\nonumber");
[4327]246      } else if (node.Symbol is Defun) {
247        var defunNode = node as DefunTreeNode;
[4969]248        strBuilder.Append(defunNode.FunctionName + " & = ");
[4327]249      } else if (node.Symbol is InvokeFunction) {
250        var invokeNode = node as InvokeFunctionTreeNode;
[7446]251        strBuilder.Append(invokeNode.Symbol.FunctionName + @" \left( ");
[4327]252      } else if (node.Symbol is StartSymbol) {
[14255]253        FormatStartSymbol(strBuilder);
[4900]254      } else if (node.Symbol is Argument) {
[4327]255        var argSym = node.Symbol as Argument;
[4900]256        strBuilder.Append(" ARG+" + argSym.ArgumentIndex + " ");
[5428]257      } else if (node.Symbol is Derivative) {
[7446]258        strBuilder.Append(@" \cfrac{d \left( ");
[5428]259      } else if (node.Symbol is TimeLag) {
260        var laggedNode = node as ILaggedTreeNode;
261        currentLag += laggedNode.Lag;
262      } else if (node.Symbol is Power) {
[7446]263        strBuilder.Append(@" \left( ");
[5428]264      } else if (node.Symbol is Root) {
[7446]265        strBuilder.Append(@" \left( ");
[5428]266      } else if (node.Symbol is Integral) {
267        // actually a new variable for t is needed in all subtrees (TODO)
268        var laggedTreeNode = node as ILaggedTreeNode;
[7446]269        strBuilder.Append(@"\sum_{t=" + (laggedTreeNode.Lag + currentLag) + @"}^0 \left( ");
[5468]270      } else if (node.Symbol is VariableCondition) {
271        var conditionTreeNode = node as VariableConditionTreeNode;
[18132]272        var paramName = "c_{" + parameters.Count + "}";
273        string p = @"1 /  1 + \exp  - " + paramName + " ";
274        parameters.Add(new KeyValuePair<string, double>(paramName, conditionTreeNode.Slope));
275        paramIdx++;
276        var const2Name = "c_{" + parameters.Count + @"}";
[14826]277        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - " + const2Name + "   ";
[18132]278        parameters.Add(new KeyValuePair<string, double>(const2Name, conditionTreeNode.Threshold));
279        paramIdx++;
[7446]280        strBuilder.Append(@" \left( " + p + @"\cdot ");
[18220]281      } else if (node.Symbol is SubFunctionSymbol) {
282        // to nothing, skip symbol
[4327]283      } else {
284        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
285      }
286    }
287
[7140]288    private void FormatSep(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, int step) {
[4327]289      if (node.Symbol is Addition) {
290        strBuilder.Append(" + ");
291      } else if (node.Symbol is Subtraction) {
292        strBuilder.Append(" - ");
293      } else if (node.Symbol is Multiplication) {
[5428]294        strBuilder.Append(@" \cdot ");
[4327]295      } else if (node.Symbol is Division) {
[7140]296        if (step + 1 == node.SubtreeCount)
297          strBuilder.Append(@"}{");
298        else
299          strBuilder.Append(@" }{ \cfrac{ ");
[16802]300      } else if (node.Symbol is Absolute) {
301        throw new InvalidOperationException();
302      } else if (node.Symbol is AnalyticQuotient) {
303        strBuilder.Append(@"}{\sqrt{1 + \left( ");
[4969]304      } else if (node.Symbol is Average) {
305        strBuilder.Append(@" + ");
306      } else if (node.Symbol is Logarithm) {
307        throw new InvalidOperationException();
308      } else if (node.Symbol is Exponential) {
309        throw new InvalidOperationException();
[7695]310      } else if (node.Symbol is Square) {
311        throw new InvalidOperationException();
312      } else if (node.Symbol is SquareRoot) {
313        throw new InvalidOperationException();
[16802]314      } else if (node.Symbol is Cube) {
315        throw new InvalidOperationException();
316      } else if (node.Symbol is CubeRoot) {
317        throw new InvalidOperationException();
[4969]318      } else if (node.Symbol is Sine) {
319        throw new InvalidOperationException();
320      } else if (node.Symbol is Cosine) {
321        throw new InvalidOperationException();
322      } else if (node.Symbol is Tangent) {
323        throw new InvalidOperationException();
[16702]324      } else if (node.Symbol is HyperbolicTangent) {
325        throw new InvalidOperationException();
[7696]326      } else if (node.Symbol is AiryA) {
327        throw new InvalidOperationException();
328      } else if (node.Symbol is AiryB) {
329        throw new InvalidOperationException();
330      } else if (node.Symbol is Bessel) {
331        throw new InvalidOperationException();
332      } else if (node.Symbol is CosineIntegral) {
333        throw new InvalidOperationException();
334      } else if (node.Symbol is Dawson) {
335        throw new InvalidOperationException();
336      } else if (node.Symbol is Erf) {
337        throw new InvalidOperationException();
338      } else if (node.Symbol is ExponentialIntegralEi) {
339        throw new InvalidOperationException();
340      } else if (node.Symbol is FresnelCosineIntegral) {
341        throw new InvalidOperationException();
342      } else if (node.Symbol is FresnelSineIntegral) {
343        throw new InvalidOperationException();
344      } else if (node.Symbol is Gamma) {
345        throw new InvalidOperationException();
346      } else if (node.Symbol is HyperbolicCosineIntegral) {
347        throw new InvalidOperationException();
348      } else if (node.Symbol is HyperbolicSineIntegral) {
349        throw new InvalidOperationException();
[7697]350      } else if (node.Symbol is Norm) {
351        throw new InvalidOperationException();
[7696]352      } else if (node.Symbol is Psi) {
353        throw new InvalidOperationException();
354      } else if (node.Symbol is SineIntegral) {
355        throw new InvalidOperationException();
[4969]356      } else if (node.Symbol is GreaterThan) {
357        strBuilder.Append(@" > ");
358      } else if (node.Symbol is LessThan) {
359        strBuilder.Append(@" < ");
360      } else if (node.Symbol is And) {
[7446]361        strBuilder.Append(@" > 0  \right) \land \left(");
[4969]362      } else if (node.Symbol is Or) {
[7446]363        strBuilder.Append(@" > 0  \right) \lor \left(");
[4969]364      } else if (node.Symbol is Not) {
365        throw new InvalidOperationException();
366      } else if (node.Symbol is IfThenElse) {
[7446]367        strBuilder.Append(@" , ");
[4327]368      } else if (node.Symbol is ProgramRootSymbol) {
369        strBuilder.Append(@"\\" + Environment.NewLine);
370      } else if (node.Symbol is Defun) {
371      } else if (node.Symbol is InvokeFunction) {
372        strBuilder.Append(" , ");
373      } else if (node.Symbol is StartSymbol) {
[8798]374        strBuilder.Append(@"\\" + Environment.NewLine);
[14255]375        FormatStartSymbol(strBuilder);
[5428]376      } else if (node.Symbol is Power) {
[7446]377        strBuilder.Append(@"\right) ^ { \operatorname{round} \left(");
[5428]378      } else if (node.Symbol is Root) {
[7446]379        strBuilder.Append(@"\right) ^ {  \cfrac{1}{ \operatorname{round} \left(");
[5468]380      } else if (node.Symbol is VariableCondition) {
381        var conditionTreeNode = node as VariableConditionTreeNode;
[18132]382        var const1Name = "c_{" + parameters.Count + "}";
[14826]383        string p = @"1 / \left( 1 + \exp \left( - " + const1Name + " ";
[18132]384        parameters.Add(new KeyValuePair<string, double>(const1Name, conditionTreeNode.Slope));
385        paramIdx++;
386        var const2Name = "c_{" + parameters.Count + "}";
[14826]387        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - " + const2Name + " \right) \right) \right)   ";
[18132]388        parameters.Add(new KeyValuePair<string, double>(const2Name, conditionTreeNode.Threshold));
389        paramIdx++;
[7446]390        strBuilder.Append(@" +  \left( 1 - " + p + @" \right) \cdot ");
[18220]391      } else if (node.Symbol is SubFunctionSymbol) {
392        throw new InvalidOperationException();
[4327]393      } else {
394        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
395      }
396    }
397
[5745]398    private void FormatEnd(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) {
[4327]399      if (node.Symbol is Addition) {
[7446]400        strBuilder.Append(@" \right) ");
[4327]401      } else if (node.Symbol is Subtraction) {
[7446]402        strBuilder.Append(@" \right) ");
[4327]403      } else if (node.Symbol is Multiplication) {
404      } else if (node.Symbol is Division) {
[7140]405        strBuilder.Append(" } ");
406        for (int i = 2; i < node.SubtreeCount; i++)
[5428]407          strBuilder.Append(" } ");
[16802]408      } else if (node.Symbol is Absolute) {
409        strBuilder.Append(@" \right)");
410      } else if (node.Symbol is AnalyticQuotient) {
411        strBuilder.Append(@" \right)^2}}");
[4969]412      } else if (node.Symbol is Average) {
[7446]413        strBuilder.Append(@" \right) ");
[4969]414      } else if (node.Symbol is Logarithm) {
[7446]415        strBuilder.Append(@" \right) ");
[4969]416      } else if (node.Symbol is Exponential) {
[7446]417        strBuilder.Append(@" \right) ");
[7695]418      } else if (node.Symbol is Square) {
419        strBuilder.Append(@"\right)^2");
420      } else if (node.Symbol is SquareRoot) {
421        strBuilder.Append(@"}");
[16802]422      } else if (node.Symbol is Cube) {
423        strBuilder.Append(@"\right)^3");
424      } else if (node.Symbol is CubeRoot) {
[16905]425        strBuilder.Append(@"\right)");
[4969]426      } else if (node.Symbol is Sine) {
[7446]427        strBuilder.Append(@" \right) ");
[4969]428      } else if (node.Symbol is Cosine) {
[7446]429        strBuilder.Append(@" \right) ");
[4969]430      } else if (node.Symbol is Tangent) {
[7446]431        strBuilder.Append(@" \right) ");
[16702]432      } else if (node.Symbol is HyperbolicTangent) {
433        strBuilder.Append(@" \right) ");
[7696]434      } else if (node.Symbol is AiryA) {
435        strBuilder.Append(@" \right) ");
436      } else if (node.Symbol is AiryB) {
437        strBuilder.Append(@" \right) ");
438      } else if (node.Symbol is Bessel) {
439        strBuilder.Append(@" \right) ");
440      } else if (node.Symbol is CosineIntegral) {
441        strBuilder.Append(@" \right) ");
442      } else if (node.Symbol is Dawson) {
443        strBuilder.Append(@" \right) ");
444      } else if (node.Symbol is Erf) {
445        strBuilder.Append(@" \right) ");
446      } else if (node.Symbol is ExponentialIntegralEi) {
447        strBuilder.Append(@" \right) ");
448      } else if (node.Symbol is FresnelCosineIntegral) {
449        strBuilder.Append(@" \right) ");
450      } else if (node.Symbol is FresnelSineIntegral) {
451        strBuilder.Append(@" \right) ");
452      } else if (node.Symbol is Gamma) {
453        strBuilder.Append(@" \right) ");
454      } else if (node.Symbol is HyperbolicCosineIntegral) {
455        strBuilder.Append(@" \right) ");
456      } else if (node.Symbol is HyperbolicSineIntegral) {
457        strBuilder.Append(@" \right) ");
[7697]458      } else if (node.Symbol is Norm) {
459        strBuilder.Append(@" \right) ");
[7696]460      } else if (node.Symbol is Psi) {
461        strBuilder.Append(@" \right) ");
462      } else if (node.Symbol is SineIntegral) {
463        strBuilder.Append(@" \right) ");
[4969]464      } else if (node.Symbol is GreaterThan) {
[7446]465        strBuilder.Append(@" \right) ");
[4969]466      } else if (node.Symbol is LessThan) {
[7446]467        strBuilder.Append(@" \right) ");
[4969]468      } else if (node.Symbol is And) {
[7446]469        strBuilder.Append(@" > 0 \right) \right) ");
[4969]470      } else if (node.Symbol is Or) {
[7446]471        strBuilder.Append(@" > 0 \right) \right) ");
[4969]472      } else if (node.Symbol is Not) {
[7446]473        strBuilder.Append(@" \right) ");
[4969]474      } else if (node.Symbol is IfThenElse) {
[7446]475        strBuilder.Append(@" \right) ");
[18132]476      } else if (node.Symbol is Number) {
[4327]477      } else if (node.Symbol is Constant) {
[5428]478      } else if (node.Symbol is LaggedVariable) {
[7038]479      } else if (node.Symbol is Variable) {
[14826]480      } else if (node.Symbol is FactorVariable) {
481      } else if (node.Symbol is BinaryFactorVariable) {
[4327]482      } else if (node.Symbol is ProgramRootSymbol) {
[7446]483        strBuilder
484          .AppendLine("\\end{align*}")
485          .AppendLine("\\begin{align*}")
486          .AppendLine("\\nonumber");
[18132]487        // output all parameter values
488        if (parameters.Count > 0) {
489          foreach (var param in parameters) {
[7446]490            // replace "." with ".&" to align decimal points
[18132]491            var paramStr = string.Format(System.Globalization.NumberFormatInfo.InvariantInfo, "{0:G5}", param.Value);
492            if (!paramStr.Contains(".")) paramStr = paramStr + ".0";
493            paramStr = paramStr.Replace(".", "&.");  // fix problem in rendering of aligned expressions
494            strBuilder.Append(param.Key + "& = & " + paramStr);
[7446]495            strBuilder.Append(@"\\");
[4969]496          }
497        }
[7446]498        strBuilder.AppendLine("\\end{align*}");
[4327]499      } else if (node.Symbol is Defun) {
500      } else if (node.Symbol is InvokeFunction) {
[7446]501        strBuilder.Append(@" \right) ");
[4327]502      } else if (node.Symbol is StartSymbol) {
503      } else if (node.Symbol is Argument) {
[5428]504      } else if (node.Symbol is Derivative) {
[7446]505        strBuilder.Append(@" \right) }{dt} ");
[5428]506      } else if (node.Symbol is TimeLag) {
507        var laggedNode = node as ILaggedTreeNode;
508        currentLag -= laggedNode.Lag;
509      } else if (node.Symbol is Power) {
[7446]510        strBuilder.Append(@" \right) } ");
[5428]511      } else if (node.Symbol is Root) {
[7446]512        strBuilder.Append(@" \right) } } ");
[5428]513      } else if (node.Symbol is Integral) {
[7446]514        strBuilder.Append(@" \right) ");
[5468]515      } else if (node.Symbol is VariableCondition) {
[7446]516        strBuilder.Append(@"\right) ");
[18220]517      } else if (node.Symbol is SubFunctionSymbol) {
[4327]518      } else {
519        throw new NotImplementedException("Export of " + node.Symbol + " is not implemented.");
520      }
521    }
[6975]522
[14255]523    private void FormatStartSymbol(StringBuilder strBuilder) {
[17826]524      strBuilder.Append(targetVariable != null ? EscapeLatexString(targetVariable) : "\\text{target}_{" + targetCount++ + "}");
[14255]525      if (containsTimeSeriesSymbol)
526        strBuilder.Append("(t)");
527      strBuilder.Append(" & = ");
528    }
529
[5428]530    private string LagToString(int lag) {
531      if (lag < 0) {
532        return "(t" + lag + ")";
533      } else if (lag > 0) {
534        return "(t+" + lag + ")";
[7038]535      } else return "";
[5428]536    }
[6975]537
538    private string EscapeLatexString(string s) {
[7446]539      return "\\text{" +
540        s
541         .Replace("\\", "\\\\")
542         .Replace("{", "\\{")
543         .Replace("}", "\\}")
544        + "}";
[6975]545    }
[4327]546  }
547}
Note: See TracBrowser for help on using the repository browser.