1 | using System;
|
---|
2 | using HeuristicLab.Common;
|
---|
3 | using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
|
---|
4 |
|
---|
5 | namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
|
---|
6 | public abstract class InterpreterBase<T> where T : IAlgebraicType<T> {
|
---|
7 | public struct Instruction {
|
---|
8 | public byte opcode;
|
---|
9 | public ushort narg;
|
---|
10 | public int childIndex;
|
---|
11 | public double dblVal;
|
---|
12 | public object data; // any kind of data you want to store in instructions
|
---|
13 | public T value;
|
---|
14 | }
|
---|
15 |
|
---|
16 | public T Evaluate(Instruction[] code) {
|
---|
17 | for (int i = code.Length - 1; i >= 0; --i) {
|
---|
18 | var instr = code[i];
|
---|
19 | var c = instr.childIndex;
|
---|
20 | var n = instr.narg;
|
---|
21 |
|
---|
22 | switch (instr.opcode) {
|
---|
23 | case OpCodes.Variable: {
|
---|
24 | LoadVariable(instr);
|
---|
25 | break;
|
---|
26 | }
|
---|
27 | case OpCodes.Constant: { break; } // we initialize constants in Compile. The value never changes afterwards
|
---|
28 | case OpCodes.Add: {
|
---|
29 | instr.value.Assign(code[c].value);
|
---|
30 | for (int j = 1; j < n; ++j) {
|
---|
31 | instr.value.Add(code[c + j].value);
|
---|
32 | }
|
---|
33 | break;
|
---|
34 | }
|
---|
35 |
|
---|
36 | case OpCodes.Sub: {
|
---|
37 | if (n == 1) {
|
---|
38 | instr.value.AssignNeg(code[c].value);
|
---|
39 | } else {
|
---|
40 | instr.value.Assign(code[c].value);
|
---|
41 | for (int j = 1; j < n; ++j) {
|
---|
42 | instr.value.Sub(code[c + j].value);
|
---|
43 | }
|
---|
44 | }
|
---|
45 | break;
|
---|
46 | }
|
---|
47 |
|
---|
48 | case OpCodes.Mul: {
|
---|
49 | instr.value.Assign(code[c].value);
|
---|
50 | for (int j = 1; j < n; ++j) {
|
---|
51 | instr.value.Mul(code[c + j].value);
|
---|
52 | }
|
---|
53 | break;
|
---|
54 | }
|
---|
55 |
|
---|
56 | case OpCodes.Div: {
|
---|
57 | if (n == 1) {
|
---|
58 | instr.value.AssignInv(code[c].value);
|
---|
59 | } else {
|
---|
60 | instr.value.Assign(code[c].value);
|
---|
61 | for (int j = 1; j < n; ++j) {
|
---|
62 | instr.value.Div(code[c + j].value);
|
---|
63 | }
|
---|
64 | }
|
---|
65 | break;
|
---|
66 | }
|
---|
67 | case OpCodes.Square: {
|
---|
68 | instr.value.AssignIntPower(code[c].value, 2);
|
---|
69 | break;
|
---|
70 | }
|
---|
71 | case OpCodes.SquareRoot: {
|
---|
72 | instr.value.AssignIntRoot(code[c].value, 2);
|
---|
73 | break;
|
---|
74 | }
|
---|
75 | case OpCodes.Cube: {
|
---|
76 | instr.value.AssignIntPower(code[c].value, 3);
|
---|
77 | break;
|
---|
78 | }
|
---|
79 | case OpCodes.CubeRoot: {
|
---|
80 | instr.value.AssignIntRoot(code[c].value, 3);
|
---|
81 | break;
|
---|
82 | }
|
---|
83 | case OpCodes.Exp: {
|
---|
84 | instr.value.AssignExp(code[c].value);
|
---|
85 | break;
|
---|
86 | }
|
---|
87 | case OpCodes.Log: {
|
---|
88 | instr.value.AssignLog(code[c].value);
|
---|
89 | break;
|
---|
90 | }
|
---|
91 | case OpCodes.Sin: {
|
---|
92 | instr.value.AssignSin(code[c].value);
|
---|
93 | break;
|
---|
94 | }
|
---|
95 | case OpCodes.Cos: {
|
---|
96 | instr.value.AssignCos(code[c].value);
|
---|
97 | break;
|
---|
98 | }
|
---|
99 | case OpCodes.Absolute: {
|
---|
100 | instr.value.AssignAbs(code[c].value);
|
---|
101 | break;
|
---|
102 | }
|
---|
103 | case OpCodes.AnalyticQuotient: {
|
---|
104 | instr.value.Assign(code[c].value);
|
---|
105 | for (int j = 1; j < n; ++j) {
|
---|
106 | var t = instr.value.One;
|
---|
107 | t.Add(code[c + j].value.Clone().IntPower(2));
|
---|
108 | instr.value.Div(t.IntRoot(2));
|
---|
109 | }
|
---|
110 | break;
|
---|
111 | }
|
---|
112 | case OpCodes.Tanh: {
|
---|
113 | instr.value.AssignTanh(code[c].value);
|
---|
114 | break;
|
---|
115 | }
|
---|
116 |
|
---|
117 | default: throw new ArgumentException($"Unknown opcode {instr.opcode}");
|
---|
118 | }
|
---|
119 | }
|
---|
120 | return code[0].value;
|
---|
121 | }
|
---|
122 |
|
---|
123 | protected Instruction[] Compile(ISymbolicExpressionTree tree) {
|
---|
124 | var root = tree.Root.GetSubtree(0).GetSubtree(0);
|
---|
125 | var code = new Instruction[root.GetLength()];
|
---|
126 | if (root.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)");
|
---|
127 | int c = 1, i = 0;
|
---|
128 | foreach (var node in root.IterateNodesBreadth()) {
|
---|
129 | if (node.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)");
|
---|
130 | code[i] = new Instruction {
|
---|
131 | opcode = OpCodes.MapSymbolToOpCode(node),
|
---|
132 | narg = (ushort)node.SubtreeCount,
|
---|
133 | childIndex = c
|
---|
134 | };
|
---|
135 | if (node is VariableTreeNode variable) {
|
---|
136 | InitializeTerminalInstruction(ref code[i], variable);
|
---|
137 | } else if (node is ConstantTreeNode constant) {
|
---|
138 | InitializeTerminalInstruction(ref code[i], constant);
|
---|
139 | } else {
|
---|
140 | InitializeInternalInstruction(ref code[i], node);
|
---|
141 | }
|
---|
142 | c += node.SubtreeCount;
|
---|
143 | ++i;
|
---|
144 | }
|
---|
145 | return code;
|
---|
146 | }
|
---|
147 |
|
---|
148 | protected abstract void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant);
|
---|
149 | protected abstract void InitializeTerminalInstruction(ref Instruction instruction, VariableTreeNode variable);
|
---|
150 | protected abstract void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node);
|
---|
151 |
|
---|
152 | protected abstract void LoadVariable(Instruction a);
|
---|
153 |
|
---|
154 | }
|
---|
155 | } |
---|