Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.3/Symbolic/SymbolicSimplifier.cs @ 5429

Last change on this file since 5429 was 5377, checked in by mkommend, 14 years ago

ticket #1256 - Changed SymbolicSimplifier to handle lagged variables correctly.

File size: 22.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Diagnostics;
25using System.Linq;
26using HeuristicLab.Common;
27using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Symbols;
29using HeuristicLab.Problems.DataAnalysis.Symbolic.Symbols;
30
31namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
32  /// <summary>
33  /// Simplistic simplifier for arithmetic expressions
34  /// </summary>
35  public class SymbolicSimplifier {
36    private Addition addSymbol = new Addition();
37    private Multiplication mulSymbol = new Multiplication();
38    private Division divSymbol = new Division();
39    private Constant constSymbol = new Constant();
40    private Variable varSymbol = new Variable();
41
42    public SymbolicExpressionTree Simplify(SymbolicExpressionTree originalTree) {
43      var clone = (SymbolicExpressionTreeNode)originalTree.Root.Clone();
44      // macro expand (initially no argument trees)
45      var macroExpandedTree = MacroExpand(clone, clone.SubTrees[0], new List<SymbolicExpressionTreeNode>());
46      SymbolicExpressionTreeNode rootNode = (new ProgramRootSymbol()).CreateTreeNode();
47      rootNode.AddSubTree(GetSimplifiedTree(macroExpandedTree));
48      return new SymbolicExpressionTree(rootNode);
49    }
50
51    // the argumentTrees list contains already expanded trees used as arguments for invocations
52    private SymbolicExpressionTreeNode MacroExpand(SymbolicExpressionTreeNode root, SymbolicExpressionTreeNode node, IList<SymbolicExpressionTreeNode> argumentTrees) {
53      List<SymbolicExpressionTreeNode> subtrees = new List<SymbolicExpressionTreeNode>(node.SubTrees);
54      while (node.SubTrees.Count > 0) node.RemoveSubTree(0);
55      if (node.Symbol is InvokeFunction) {
56        var invokeSym = node.Symbol as InvokeFunction;
57        var defunNode = FindFunctionDefinition(root, invokeSym.FunctionName);
58        var macroExpandedArguments = new List<SymbolicExpressionTreeNode>();
59        foreach (var subtree in subtrees) {
60          macroExpandedArguments.Add(MacroExpand(root, subtree, argumentTrees));
61        }
62        return MacroExpand(root, defunNode, macroExpandedArguments);
63      } else if (node.Symbol is Argument) {
64        var argSym = node.Symbol as Argument;
65        // return the correct argument sub-tree (already macro-expanded)
66        return (SymbolicExpressionTreeNode)argumentTrees[argSym.ArgumentIndex].Clone();
67      } else {
68        // recursive application
69        foreach (var subtree in subtrees) {
70          node.AddSubTree(MacroExpand(root, subtree, argumentTrees));
71        }
72        return node;
73      }
74    }
75
76    private SymbolicExpressionTreeNode FindFunctionDefinition(SymbolicExpressionTreeNode root, string functionName) {
77      foreach (var subtree in root.SubTrees.OfType<DefunTreeNode>()) {
78        if (subtree.FunctionName == functionName) return subtree.SubTrees[0];
79      }
80
81      throw new ArgumentException("Definition of function " + functionName + " not found.");
82    }
83
84
85    #region symbol predicates
86    private bool IsDivision(SymbolicExpressionTreeNode original) {
87      return original.Symbol is Division;
88    }
89
90    private bool IsMultiplication(SymbolicExpressionTreeNode original) {
91      return original.Symbol is Multiplication;
92    }
93
94    private bool IsSubtraction(SymbolicExpressionTreeNode original) {
95      return original.Symbol is Subtraction;
96    }
97
98    private bool IsAddition(SymbolicExpressionTreeNode original) {
99      return original.Symbol is Addition;
100    }
101
102    private bool IsVariable(SymbolicExpressionTreeNode original) {
103      return original.Symbol is Variable;
104    }
105
106    private bool IsConstant(SymbolicExpressionTreeNode original) {
107      return original.Symbol is Constant;
108    }
109
110    private bool IsAverage(SymbolicExpressionTreeNode original) {
111      return original.Symbol is Average;
112    }
113    private bool IsLog(SymbolicExpressionTreeNode original) {
114      return original.Symbol is Logarithm;
115    }
116    private bool IsIfThenElse(SymbolicExpressionTreeNode original) {
117      return original.Symbol is IfThenElse;
118    }
119    #endregion
120
121    /// <summary>
122    /// Creates a new simplified tree
123    /// </summary>
124    /// <param name="original"></param>
125    /// <returns></returns>
126    public SymbolicExpressionTreeNode GetSimplifiedTree(SymbolicExpressionTreeNode original) {
127      if (IsConstant(original) || IsVariable(original)) {
128        return (SymbolicExpressionTreeNode)original.Clone();
129      } else if (IsAddition(original)) {
130        return SimplifyAddition(original);
131      } else if (IsSubtraction(original)) {
132        return SimplifySubtraction(original);
133      } else if (IsMultiplication(original)) {
134        return SimplifyMultiplication(original);
135      } else if (IsDivision(original)) {
136        return SimplifyDivision(original);
137      } else if (IsAverage(original)) {
138        return SimplifyAverage(original);
139      } else if (IsLog(original)) {
140        // TODO simplify logarithm
141        return SimplifyAny(original);
142      } else if (IsIfThenElse(original)) {
143        // TODO simplify conditionals
144        return SimplifyAny(original);
145      } else if (IsAverage(original)) {
146        return SimplifyAverage(original);
147      } else {
148        return SimplifyAny(original);
149      }
150    }
151
152    #region specific simplification routines
153    private SymbolicExpressionTreeNode SimplifyAny(SymbolicExpressionTreeNode original) {
154      // can't simplify this function but simplify all subtrees
155      List<SymbolicExpressionTreeNode> subTrees = new List<SymbolicExpressionTreeNode>(original.SubTrees);
156      while (original.SubTrees.Count > 0) original.RemoveSubTree(0);
157      var clone = (SymbolicExpressionTreeNode)original.Clone();
158      List<SymbolicExpressionTreeNode> simplifiedSubTrees = new List<SymbolicExpressionTreeNode>();
159      foreach (var subTree in subTrees) {
160        simplifiedSubTrees.Add(GetSimplifiedTree(subTree));
161        original.AddSubTree(subTree);
162      }
163      foreach (var simplifiedSubtree in simplifiedSubTrees) {
164        clone.AddSubTree(simplifiedSubtree);
165      }
166      if (simplifiedSubTrees.TrueForAll(t => IsConstant(t))) {
167        SimplifyConstantExpression(clone);
168      }
169      return clone;
170    }
171
172    private SymbolicExpressionTreeNode SimplifyConstantExpression(SymbolicExpressionTreeNode original) {
173      // not yet implemented
174      return original;
175    }
176
177    private SymbolicExpressionTreeNode SimplifyAverage(SymbolicExpressionTreeNode original) {
178      if (original.SubTrees.Count == 1) {
179        return GetSimplifiedTree(original.SubTrees[0]);
180      } else {
181        // simplify expressions x0..xn
182        // make sum(x0..xn) / n
183        Trace.Assert(original.SubTrees.Count > 1);
184        var sum = original.SubTrees
185          .Select(x => GetSimplifiedTree(x))
186          .Aggregate((a, b) => MakeSum(a, b));
187        return MakeFraction(sum, MakeConstant(original.SubTrees.Count));
188      }
189    }
190
191    private SymbolicExpressionTreeNode SimplifyDivision(SymbolicExpressionTreeNode original) {
192      if (original.SubTrees.Count == 1) {
193        return Invert(GetSimplifiedTree(original.SubTrees[0]));
194      } else {
195        // simplify expressions x0..xn
196        // make multiplication (x0 * 1/(x1 * x1 * .. * xn))
197        Trace.Assert(original.SubTrees.Count > 1);
198        var simplifiedTrees = original.SubTrees.Select(x => GetSimplifiedTree(x));
199        return
200          MakeProduct(simplifiedTrees.First(), Invert(simplifiedTrees.Skip(1).Aggregate((a, b) => MakeProduct(a, b))));
201      }
202    }
203
204    private SymbolicExpressionTreeNode SimplifyMultiplication(SymbolicExpressionTreeNode original) {
205      if (original.SubTrees.Count == 1) {
206        return GetSimplifiedTree(original.SubTrees[0]);
207      } else {
208        Trace.Assert(original.SubTrees.Count > 1);
209        return original.SubTrees
210          .Select(x => GetSimplifiedTree(x))
211          .Aggregate((a, b) => MakeProduct(a, b));
212      }
213    }
214
215    private SymbolicExpressionTreeNode SimplifySubtraction(SymbolicExpressionTreeNode original) {
216      if (original.SubTrees.Count == 1) {
217        return Negate(GetSimplifiedTree(original.SubTrees[0]));
218      } else {
219        // simplify expressions x0..xn
220        // make addition (x0,-x1..-xn)
221        Trace.Assert(original.SubTrees.Count > 1);
222        var simplifiedTrees = original.SubTrees.Select(x => GetSimplifiedTree(x));
223        return simplifiedTrees.Take(1)
224          .Concat(simplifiedTrees.Skip(1).Select(x => Negate(x)))
225          .Aggregate((a, b) => MakeSum(a, b));
226      }
227    }
228
229    private SymbolicExpressionTreeNode SimplifyAddition(SymbolicExpressionTreeNode original) {
230      if (original.SubTrees.Count == 1) {
231        return GetSimplifiedTree(original.SubTrees[0]);
232      } else {
233        // simplify expression x0..xn
234        // make addition (x0..xn)
235        Trace.Assert(original.SubTrees.Count > 1);
236        return original.SubTrees
237          .Select(x => GetSimplifiedTree(x))
238          .Aggregate((a, b) => MakeSum(a, b));
239      }
240    }
241    #endregion
242
243
244
245    #region low level tree restructuring
246    // MakeFraction, MakeProduct and MakeSum take two already simplified trees and create a new simplified tree
247
248    private SymbolicExpressionTreeNode MakeFraction(SymbolicExpressionTreeNode a, SymbolicExpressionTreeNode b) {
249      if (IsConstant(a) && IsConstant(b)) {
250        // fold constants
251        return MakeConstant(((ConstantTreeNode)a).Value / ((ConstantTreeNode)b).Value);
252      } if (IsConstant(a) && !((ConstantTreeNode)a).Value.IsAlmost(1.0)) {
253        return MakeFraction(MakeConstant(1.0), MakeProduct(b, Invert(a)));
254      } else if (IsVariable(a) && IsConstant(b)) {
255        // merge constant values into variable weights
256        var constB = ((ConstantTreeNode)b).Value;
257        ((VariableTreeNode)a).Weight /= constB;
258        return a;
259      } else if (IsAddition(a) && IsConstant(b)) {
260        return a.SubTrees
261          .Select(x => GetSimplifiedTree(x))
262         .Select(x => MakeFraction(x, b))
263         .Aggregate((c, d) => MakeSum(c, d));
264      } else if (IsMultiplication(a) && IsConstant(b)) {
265        return MakeProduct(a, Invert(b));
266      } else if (IsDivision(a) && IsConstant(b)) {
267        // (a1 / a2) / c => (a1 / (a2 * c))
268        Trace.Assert(a.SubTrees.Count == 2);
269        return MakeFraction(a.SubTrees[0], MakeProduct(a.SubTrees[1], b));
270      } else if (IsDivision(a) && IsDivision(b)) {
271        // (a1 / a2) / (b1 / b2) =>
272        Trace.Assert(a.SubTrees.Count == 2);
273        Trace.Assert(b.SubTrees.Count == 2);
274        return MakeFraction(MakeProduct(a.SubTrees[0], b.SubTrees[1]), MakeProduct(a.SubTrees[1], b.SubTrees[0]));
275      } else if (IsDivision(a)) {
276        // (a1 / a2) / b => (a1 / (a2 * b))
277        Trace.Assert(a.SubTrees.Count == 2);
278        return MakeFraction(a.SubTrees[0], MakeProduct(a.SubTrees[1], b));
279      } else if (IsDivision(b)) {
280        // a / (b1 / b2) => (a * b2) / b1
281        Trace.Assert(b.SubTrees.Count == 2);
282        return MakeFraction(MakeProduct(a, b.SubTrees[1]), b.SubTrees[0]);
283      } else {
284        var div = divSymbol.CreateTreeNode();
285        div.AddSubTree(a);
286        div.AddSubTree(b);
287        return div;
288      }
289    }
290
291    private SymbolicExpressionTreeNode MakeSum(SymbolicExpressionTreeNode a, SymbolicExpressionTreeNode b) {
292      if (IsConstant(a) && IsConstant(b)) {
293        // fold constants
294        ((ConstantTreeNode)a).Value += ((ConstantTreeNode)b).Value;
295        return a;
296      } else if (IsConstant(a)) {
297        // c + x => x + c
298        // b is not constant => make sure constant is on the right
299        return MakeSum(b, a);
300      } else if (IsConstant(b) && ((ConstantTreeNode)b).Value.IsAlmost(0.0)) {
301        // x + 0 => x
302        return a;
303      } else if (IsAddition(a) && IsAddition(b)) {
304        // merge additions
305        var add = addSymbol.CreateTreeNode();
306        for (int i = 0; i < a.SubTrees.Count - 1; i++) add.AddSubTree(a.SubTrees[i]);
307        for (int i = 0; i < b.SubTrees.Count - 1; i++) add.AddSubTree(b.SubTrees[i]);
308        if (IsConstant(a.SubTrees.Last()) && IsConstant(b.SubTrees.Last())) {
309          add.AddSubTree(MakeSum(a.SubTrees.Last(), b.SubTrees.Last()));
310        } else if (IsConstant(a.SubTrees.Last())) {
311          add.AddSubTree(b.SubTrees.Last());
312          add.AddSubTree(a.SubTrees.Last());
313        } else {
314          add.AddSubTree(a.SubTrees.Last());
315          add.AddSubTree(b.SubTrees.Last());
316        }
317        MergeVariablesInSum(add);
318        return add;
319      } else if (IsAddition(b)) {
320        return MakeSum(b, a);
321      } else if (IsAddition(a) && IsConstant(b)) {
322        // a is an addition and b is a constant => append b to a and make sure the constants are merged
323        var add = addSymbol.CreateTreeNode();
324        for (int i = 0; i < a.SubTrees.Count - 1; i++) add.AddSubTree(a.SubTrees[i]);
325        if (IsConstant(a.SubTrees.Last()))
326          add.AddSubTree(MakeSum(a.SubTrees.Last(), b));
327        else {
328          add.AddSubTree(a.SubTrees.Last());
329          add.AddSubTree(b);
330        }
331        return add;
332      } else if (IsAddition(a)) {
333        // a is already an addition => append b
334        var add = addSymbol.CreateTreeNode();
335        add.AddSubTree(b);
336        foreach (var subTree in a.SubTrees) {
337          add.AddSubTree(subTree);
338        }
339        MergeVariablesInSum(add);
340        return add;
341      } else {
342        var add = addSymbol.CreateTreeNode();
343        add.AddSubTree(a);
344        add.AddSubTree(b);
345        MergeVariablesInSum(add);
346        return add;
347      }
348    }
349
350    // makes sure variable symbols in sums are combined
351    // possible improvment: combine sums of products where the products only reference the same variable
352    private void MergeVariablesInSum(SymbolicExpressionTreeNode sum) {
353      var subtrees = new List<SymbolicExpressionTreeNode>(sum.SubTrees);
354      while (sum.SubTrees.Count > 0) sum.RemoveSubTree(0);
355      var groupedVarNodes = from node in subtrees.OfType<VariableTreeNode>()
356                            let lag = (node is LaggedVariableTreeNode) ? ((LaggedVariableTreeNode)node).Lag : 0
357                            group node by node.VariableName + lag into g
358                            select g;
359
360      var unchangedSubTrees = subtrees.Where(t => !(t is VariableTreeNode));
361
362      foreach (var variableNodeGroup in groupedVarNodes) {
363        var weightSum = variableNodeGroup.Select(t => t.Weight).Sum();
364        var representative = variableNodeGroup.First();
365        representative.Weight = weightSum;
366        sum.AddSubTree(representative);
367      }
368      foreach (var unchangedSubtree in unchangedSubTrees)
369        sum.AddSubTree(unchangedSubtree);
370    }
371
372
373    private SymbolicExpressionTreeNode MakeProduct(SymbolicExpressionTreeNode a, SymbolicExpressionTreeNode b) {
374      if (IsConstant(a) && IsConstant(b)) {
375        // fold constants
376        ((ConstantTreeNode)a).Value *= ((ConstantTreeNode)b).Value;
377        return a;
378      } else if (IsConstant(a)) {
379        // a * $ => $ * a
380        return MakeProduct(b, a);
381      } else if (IsConstant(b) && ((ConstantTreeNode)b).Value.IsAlmost(1.0)) {
382        // $ * 1.0 => $
383        return a;
384      } else if (IsConstant(b) && IsVariable(a)) {
385        // multiply constants into variables weights
386        ((VariableTreeNode)a).Weight *= ((ConstantTreeNode)b).Value;
387        return a;
388      } else if (IsConstant(b) && IsAddition(a)) {
389        // multiply constants into additions
390        return a.SubTrees.Select(x => MakeProduct(x, b)).Aggregate((c, d) => MakeSum(c, d));
391      } else if (IsDivision(a) && IsDivision(b)) {
392        // (a1 / a2) * (b1 / b2) => (a1 * b1) / (a2 * b2)
393        Trace.Assert(a.SubTrees.Count == 2);
394        Trace.Assert(b.SubTrees.Count == 2);
395        return MakeFraction(MakeProduct(a.SubTrees[0], b.SubTrees[0]), MakeProduct(a.SubTrees[1], b.SubTrees[1]));
396      } else if (IsDivision(a)) {
397        // (a1 / a2) * b => (a1 * b) / a2
398        Trace.Assert(a.SubTrees.Count == 2);
399        return MakeFraction(MakeProduct(a.SubTrees[0], b), a.SubTrees[1]);
400      } else if (IsDivision(b)) {
401        // a * (b1 / b2) => (b1 * a) / b2
402        Trace.Assert(b.SubTrees.Count == 2);
403        return MakeFraction(MakeProduct(b.SubTrees[0], a), b.SubTrees[1]);
404      } else if (IsMultiplication(a) && IsMultiplication(b)) {
405        // merge multiplications (make sure constants are merged)
406        var mul = mulSymbol.CreateTreeNode();
407        for (int i = 0; i < a.SubTrees.Count; i++) mul.AddSubTree(a.SubTrees[i]);
408        for (int i = 0; i < b.SubTrees.Count; i++) mul.AddSubTree(b.SubTrees[i]);
409        MergeVariablesAndConstantsInProduct(mul);
410        return mul;
411      } else if (IsMultiplication(b)) {
412        return MakeProduct(b, a);
413      } else if (IsMultiplication(a)) {
414        // a is already an multiplication => append b
415        a.AddSubTree(b);
416        MergeVariablesAndConstantsInProduct(a);
417        return a;
418      } else {
419        var mul = mulSymbol.CreateTreeNode();
420        mul.SubTrees.Add(a);
421        mul.SubTrees.Add(b);
422        MergeVariablesAndConstantsInProduct(mul);
423        return mul;
424      }
425    }
426    #endregion
427
428    // helper to combine the constant factors in products and to combine variables (powers of 2, 3...)
429    private void MergeVariablesAndConstantsInProduct(SymbolicExpressionTreeNode prod) {
430      var subtrees = new List<SymbolicExpressionTreeNode>(prod.SubTrees);
431      while (prod.SubTrees.Count > 0) prod.RemoveSubTree(0);
432      var groupedVarNodes = from node in subtrees.OfType<VariableTreeNode>()
433                            let lag = (node is LaggedVariableTreeNode) ? ((LaggedVariableTreeNode)node).Lag : 0
434                            group node by node.VariableName + lag into g
435                            orderby g.Count()
436                            select g;
437      var constantProduct = (from node in subtrees.OfType<VariableTreeNode>()
438                             select node.Weight)
439                            .Concat(from node in subtrees.OfType<ConstantTreeNode>()
440                                    select node.Value)
441                            .DefaultIfEmpty(1.0)
442                            .Aggregate((c1, c2) => c1 * c2);
443
444      var unchangedSubTrees = from tree in subtrees
445                              where !(tree is VariableTreeNode)
446                              where !(tree is ConstantTreeNode)
447                              select tree;
448
449      foreach (var variableNodeGroup in groupedVarNodes) {
450        var representative = variableNodeGroup.First();
451        representative.Weight = 1.0;
452        if (variableNodeGroup.Count() > 1) {
453          var poly = mulSymbol.CreateTreeNode();
454          for (int p = 0; p < variableNodeGroup.Count(); p++) {
455            poly.AddSubTree((SymbolicExpressionTreeNode)representative.Clone());
456          }
457          prod.AddSubTree(poly);
458        } else {
459          prod.AddSubTree(representative);
460        }
461      }
462
463      foreach (var unchangedSubtree in unchangedSubTrees)
464        prod.AddSubTree(unchangedSubtree);
465
466      if (!constantProduct.IsAlmost(1.0)) {
467        prod.AddSubTree(MakeConstant(constantProduct));
468      }
469    }
470
471
472    #region helper functions
473    /// <summary>
474    /// x => x * -1
475    /// Doesn't create new trees and manipulates x
476    /// </summary>
477    /// <param name="x"></param>
478    /// <returns>-x</returns>
479    private SymbolicExpressionTreeNode Negate(SymbolicExpressionTreeNode x) {
480      if (IsConstant(x)) {
481        ((ConstantTreeNode)x).Value *= -1;
482      } else if (IsVariable(x)) {
483        var variableTree = (VariableTreeNode)x;
484        variableTree.Weight *= -1.0;
485      } else if (IsAddition(x)) {
486        // (x0 + x1 + .. + xn) * -1 => (-x0 + -x1 + .. + -xn)       
487        for (int i = 0; i < x.SubTrees.Count; i++)
488          x.SubTrees[i] = Negate(x.SubTrees[i]);
489      } else if (IsMultiplication(x) || IsDivision(x)) {
490        // x0 * x1 * .. * xn * -1 => x0 * x1 * .. * -xn
491        x.SubTrees[x.SubTrees.Count - 1] = Negate(x.SubTrees.Last()); // last is maybe a constant, prefer to negate the constant
492      } else {
493        // any other function
494        return MakeProduct(x, MakeConstant(-1));
495      }
496      return x;
497    }
498
499    /// <summary>
500    /// x => 1/x
501    /// Doesn't create new trees and manipulates x
502    /// </summary>
503    /// <param name="x"></param>
504    /// <returns></returns>
505    private SymbolicExpressionTreeNode Invert(SymbolicExpressionTreeNode x) {
506      if (IsConstant(x)) {
507        return MakeConstant(1.0 / ((ConstantTreeNode)x).Value);
508      } else if (IsDivision(x)) {
509        Trace.Assert(x.SubTrees.Count == 2);
510        return MakeFraction(x.SubTrees[1], x.SubTrees[0]);
511      } else {
512        // any other function
513        return MakeFraction(MakeConstant(1), x);
514      }
515    }
516
517    private SymbolicExpressionTreeNode MakeConstant(double value) {
518      ConstantTreeNode constantTreeNode = (ConstantTreeNode)(constSymbol.CreateTreeNode());
519      constantTreeNode.Value = value;
520      return (SymbolicExpressionTreeNode)constantTreeNode;
521    }
522
523    private SymbolicExpressionTreeNode MakeVariable(double weight, string name) {
524      var tree = (VariableTreeNode)varSymbol.CreateTreeNode();
525      tree.Weight = weight;
526      tree.VariableName = name;
527      return tree;
528    }
529    #endregion
530  }
531}
Note: See TracBrowser for help on using the repository browser.