Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.3/Symbolic/SimpleArithmeticExpressionInterpreter.cs @ 3733

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

Added test cases for arithmetic expression interpreter. And fixed minor problems in the interpreter. #791

File size: 6.4 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 HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
24using HeuristicLab.Common;
25using HeuristicLab.Core;
26using System.Collections.Generic;
27using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Symbols;
29using HeuristicLab.Problems.DataAnalysis.Symbolic.Symbols;
30using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Compiler;
31
32namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
33  [StorableClass]
34  [Item("SimpleArithmeticExpressionInterpreter", "Interpreter for arithmetic symbolic expression trees including function calls.")]
35  // not thread safe!
36  public class SimpleArithmeticExpressionInterpreter : NamedItem, ISymbolicExpressionTreeInterpreter {
37    private class OpCodes {
38      public const byte Add = 1;
39      public const byte Sub = 2;
40      public const byte Mul = 3;
41      public const byte Div = 4;
42      public const byte Variable = 5;
43      public const byte Constant = 6;
44      public const byte Call = 100;
45      public const byte Arg = 101;
46    }
47
48    private const int ARGUMENT_STACK_SIZE = 1024;
49
50    private Dataset dataset;
51    private int row;
52    private Instruction[] code;
53    private int pc;
54
55    public override bool CanChangeName {
56      get { return false; }
57    }
58    public override bool CanChangeDescription {
59      get { return false; }
60    }
61
62    public SimpleArithmeticExpressionInterpreter()
63      : base() {
64    }
65
66    public IEnumerable<double> GetSymbolicExpressionTreeValues(SymbolicExpressionTree tree, Dataset dataset, IEnumerable<int> rows) {
67      this.dataset = dataset;
68      var compiler = new SymbolicExpressionTreeCompiler();
69      compiler.AddInstructionPostProcessingHook(PostProcessInstruction);
70      code = compiler.Compile(tree, MapSymbolToOpCode);
71      foreach (var row in rows) {
72        this.row = row;
73        pc = 0;
74        argStackPointer = 0;
75        yield return Evaluate();
76      }
77    }
78
79    private Instruction PostProcessInstruction(Instruction instr) {
80      if (instr.opCode == OpCodes.Variable) {
81        var variableTreeNode = instr.dynamicNode as VariableTreeNode;
82        instr.iArg0 = (ushort)dataset.GetVariableIndex(variableTreeNode.VariableName);
83      }
84      return instr;
85    }
86
87    private byte MapSymbolToOpCode(SymbolicExpressionTreeNode treeNode) {
88      if (treeNode.Symbol is Addition) return OpCodes.Add;
89      if (treeNode.Symbol is Subtraction) return OpCodes.Sub;
90      if (treeNode.Symbol is Multiplication) return OpCodes.Mul;
91      if (treeNode.Symbol is Division) return OpCodes.Div;
92      if (treeNode.Symbol is HeuristicLab.Problems.DataAnalysis.Symbolic.Symbols.Variable) return OpCodes.Variable;
93      if (treeNode.Symbol is Constant) return OpCodes.Constant;
94      if (treeNode.Symbol is InvokeFunction) return OpCodes.Call;
95      if (treeNode.Symbol is Argument) return OpCodes.Arg;
96      throw new NotSupportedException("Symbol: " + treeNode.Symbol);
97    }
98
99    private double[] argumentStack = new double[ARGUMENT_STACK_SIZE];
100    private int argStackPointer;
101
102    public double Evaluate() {
103      var currentInstr = code[pc++];
104      switch (currentInstr.opCode) {
105        case OpCodes.Add: {
106            double s = 0.0;
107            for (int i = 0; i < currentInstr.nArguments; i++) {
108              s += Evaluate();
109            }
110            return s;
111          }
112        case OpCodes.Sub: {
113            double s = Evaluate();
114            for (int i = 1; i < currentInstr.nArguments; i++) {
115              s -= Evaluate();
116            }
117            if (currentInstr.nArguments == 1) s = -s;
118            return s;
119          }
120        case OpCodes.Mul: {
121            double p = Evaluate();
122            for (int i = 1; i < currentInstr.nArguments; i++) {
123              p *= Evaluate();
124            }
125            return p;
126          }
127        case OpCodes.Div: {
128            double p = Evaluate();
129            for (int i = 1; i < currentInstr.nArguments; i++) {
130              p /= Evaluate();
131            }
132            if (currentInstr.nArguments == 1) p = 1.0 / p;
133            return p;
134          }
135        case OpCodes.Call: {
136            // evaluate sub-trees
137            // push on argStack in reverse order
138            for (int i = 0; i < currentInstr.nArguments; i++) {
139              argumentStack[argStackPointer + currentInstr.nArguments - i] = Evaluate();
140              argStackPointer++;
141            }
142
143            // save the pc
144            int nextPc = pc;
145            // set pc to start of function 
146            pc = currentInstr.iArg0;
147            // evaluate the function
148            double v = Evaluate();
149
150            // decrease the argument stack pointer by the number of arguments pushed
151            // to set the argStackPointer back to the original location
152            argStackPointer -= currentInstr.nArguments;
153
154            // restore the pc => evaluation will continue at point after my subtrees 
155            pc = nextPc;
156            return v;
157          }
158        case OpCodes.Arg: {
159            return argumentStack[argStackPointer - currentInstr.iArg0];
160          }
161        case OpCodes.Variable: {
162            var variableTreeNode = currentInstr.dynamicNode as VariableTreeNode;
163            return dataset[row, currentInstr.iArg0] * variableTreeNode.Weight;
164          }
165        case OpCodes.Constant: {
166            var constTreeNode = currentInstr.dynamicNode as ConstantTreeNode;
167            return constTreeNode.Value;
168          }
169        default: throw new NotSupportedException();
170      }
171    }
172  }
173}
Note: See TracBrowser for help on using the repository browser.