using System; using HeuristicLab.Common; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { public abstract class Interpreter where T : IAlgebraicType { public struct Instruction { public byte opcode; public ushort narg; public int childIndex; public double dblVal; public object data; // any kind of data you want to store in instructions public T value; } public T Evaluate(Instruction[] code) { for (int i = code.Length - 1; i >= 0; --i) { var instr = code[i]; var c = instr.childIndex; var n = instr.narg; switch (instr.opcode) { case OpCodes.Variable: { LoadVariable(instr); break; } case OpCodes.Constant: { break; } // we initialize constants in Compile. The value never changes afterwards case OpCodes.Add: { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Add(code[c + j].value); } break; } case OpCodes.Sub: { if (n == 1) { instr.value.AssignNeg(code[c].value); } else { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Sub(code[c + j].value); } } break; } case OpCodes.Mul: { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Mul(code[c + j].value); } break; } case OpCodes.Div: { if (n == 1) { instr.value.AssignInv(code[c].value); } else { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Div(code[c + j].value); } } break; } case OpCodes.Square: { instr.value.AssignIntPower(code[c].value, 2); break; } case OpCodes.SquareRoot: { instr.value.AssignIntRoot(code[c].value, 2); break; } case OpCodes.Cube: { instr.value.AssignIntPower(code[c].value, 3); break; } case OpCodes.CubeRoot: { instr.value.AssignIntRoot(code[c].value, 3); break; } case OpCodes.Exp: { instr.value.AssignExp(code[c].value); break; } case OpCodes.Log: { instr.value.AssignLog(code[c].value); break; } case OpCodes.Sin: { instr.value.AssignSin(code[c].value); break; } case OpCodes.Cos: { instr.value.AssignCos(code[c].value); break; } case OpCodes.Absolute: { instr.value.AssignAbs(code[c].value); break; } case OpCodes.AnalyticQuotient: { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { var t = instr.value.One; t.Add(code[c + j].value.Clone().IntPower(2)); instr.value.Div(t.IntRoot(2)); } break; } case OpCodes.Tanh: { instr.value.AssignTanh(code[c].value); break; } default: throw new ArgumentException($"Unknown opcode {instr.opcode}"); } } return code[0].value; } protected Instruction[] Compile(ISymbolicExpressionTree tree) { var root = tree.Root.GetSubtree(0).GetSubtree(0); var code = new Instruction[root.GetLength()]; if (root.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)"); int c = 1, i = 0; foreach (var node in root.IterateNodesBreadth()) { if (node.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)"); code[i] = new Instruction { opcode = OpCodes.MapSymbolToOpCode(node), narg = (ushort)node.SubtreeCount, childIndex = c }; if (node is VariableTreeNode variable) { InitializeTerminalInstruction(ref code[i], variable); } else if (node is ConstantTreeNode constant) { InitializeTerminalInstruction(ref code[i], constant); } else { InitializeInternalInstruction(ref code[i], node); } c += node.SubtreeCount; ++i; } return code; } protected abstract void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant); protected abstract void InitializeTerminalInstruction(ref Instruction instruction, VariableTreeNode variable); protected abstract void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node); protected abstract void LoadVariable(Instruction a); } }