/
interpreter.py
124 lines (93 loc) · 4.23 KB
/
interpreter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import os
from m2cgen import ast
from m2cgen.interpreters import utils, mixins
from m2cgen.interpreters.c.code_generator import CCodeGenerator
from m2cgen.interpreters.interpreter import ImperativeToCodeInterpreter
class CInterpreter(ImperativeToCodeInterpreter,
mixins.LinearAlgebraMixin):
supported_bin_vector_ops = {
ast.BinNumOpType.ADD: "add_vectors",
}
supported_bin_vector_num_ops = {
ast.BinNumOpType.MUL: "mul_vector_number",
}
abs_function_name = "fabs"
atan_function_name = "atan"
exponent_function_name = "exp"
logarithm_function_name = "log"
log1p_function_name = "log1p"
power_function_name = "pow"
softmax_function_name = "softmax"
sqrt_function_name = "sqrt"
tanh_function_name = "tanh"
with_softmax_expr = False
def __init__(self, indent=4, function_name="score", *args, **kwargs):
self.function_name = function_name
cg = CCodeGenerator(indent=indent)
super().__init__(cg, *args, **kwargs)
def interpret(self, expr):
self._cg.reset_state()
self._reset_reused_expr_cache()
args = [(True, self._feature_array_name)]
# C doesn't allow returning vectors, so if model returns vector we will
# have additional vector argument which we will populate at the end.
if expr.output_size > 1:
args += [(True, "output")]
with self._cg.function_definition(
name=self.function_name,
args=args,
is_scalar_output=expr.output_size == 1):
last_result = self._do_interpret(expr)
if expr.output_size > 1:
self._cg.add_assign_array_statement(
last_result, "output", expr.output_size)
else:
self._cg.add_return_statement(last_result)
if self.with_linear_algebra:
filename = os.path.join(
os.path.dirname(__file__), "linear_algebra.c")
self._cg.prepend_code_lines(utils.get_file_content(filename))
if self.with_softmax_expr:
filename = os.path.join(
os.path.dirname(__file__), "softmax.c")
self._cg.prepend_code_lines(utils.get_file_content(filename))
if self.with_vectors:
self._cg.add_dependency("<string.h>")
if self.with_math_module:
self._cg.add_dependency("<math.h>")
return self._cg.finalize_and_get_generated_code()
# Both methods supporting linear algebra do several things:
#
# 1. Call super method with extra parameters. Super method will return a
# string with a call to the respective linear algebra function;
# 2. Add variable declaration where the result of the operation will be
# stored;
# 3. Add code returned from super method to the result code;
# 4. Return name of the variable with current result.
def interpret_bin_vector_expr(self, expr, **kwargs):
var_name = self._cg.add_var_declaration(expr.output_size)
# Result: string like "addVectors(v1, v2, <size>, <var_name>)"
func_inv = super().interpret_bin_vector_expr(
expr, extra_func_args=[expr.output_size, var_name], **kwargs)
self._cg.add_code_line(f"{func_inv};")
return var_name
def interpret_bin_vector_num_expr(self, expr, **kwargs):
var_name = self._cg.add_var_declaration(expr.output_size)
# Result: string like "mulVectorNumber(v1, num, <size>, <var_name>)"
func_inv = super().interpret_bin_vector_num_expr(
expr, extra_func_args=[expr.output_size, var_name], **kwargs)
self._cg.add_code_line(f"{func_inv};")
return var_name
# Do the same things for softmax as for linear algebra.
def interpret_softmax_expr(self, expr, **kwargs):
self.with_vectors = True
self.with_softmax_expr = True
var_name = self._cg.add_var_declaration(expr.output_size)
nested = [self._do_interpret(expr, **kwargs) for expr in expr.exprs]
func_inv = self._cg.function_invocation(
self.softmax_function_name,
self._cg.vector_init(nested),
expr.output_size,
var_name)
self._cg.add_code_line(f"{func_inv};")
return var_name