Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3040_VectorBasedGP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs @ 18083

Last change on this file since 18083 was 17825, checked in by pfleck, 4 years ago

#3040 Merged trunk into branch.

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