Towards Execution

Evaluation

In [18]:
from pymbolic import parse, var
from pymbolic.mapper import Mapper

expr = parse("(x**2 + y**2)**0.5")
expr
Out[18]:
Power(Sum((Power(Variable('x'), 2), Power(Variable('y'), 2))), 0.5)

Write an evaluator:

In [8]:
class Evaluator(Mapper):
    def __init__(self, context):
        self.context = context

    def map_variable(self, expr):
        return self.context[expr.name]

    def map_constant(self, expr):
        return expr

    def map_sum(self, expr):
        return sum(self.rec(ch) for ch in expr.children)

    def map_power(self, expr):
        return self.rec(expr.base) ** self.rec(expr.exponent)
In [9]:
Evaluator({"x": 5, "y": 7})(expr)
Out[9]:
8.602325267042627

Towards Executable Code

In [25]:
class CodeWriter(Mapper):
    def __init__(self):
        self.lines = []
        self.name_nr = 0
        
    def make_name(self):
        self.name_nr += 1
        return var(f"tmp{self.name_nr}")

    def map_variable(self, expr):
        return expr

    def map_constant(self, expr):
        return expr

    def map_sum(self, expr):
        a, b = expr.children
        tmp = self.make_name()
                   
        self.lines.append("%s <- %s + %s" % (tmp, self.rec(a), self.rec(b)))
        return tmp

    def map_power(self, expr):
        tmp = self.make_name()
                   
        self.lines.append("%s <- %s ** %s" % (tmp, self.rec(expr.base), self.rec(expr.exponent)))
        return tmp
In [27]:
cw = CodeWriter()
result = cw(expr)
for l in cw.lines:
    print(l)
print()
print(result)
tmp3 <- x ** 2
tmp4 <- y ** 2
tmp2 <- tmp3 + tmp4
tmp1 <- tmp2 ** 0.5

tmp1

Common Subexpressions

In [12]:
from pymbolic.mapper.c_code import CCodeMapper

ccm = CCodeMapper()
x = parse("x")
ccm((x+4)**17)
Out[12]:
'pow(x + 4, 17)'

Often, some parts of an expression occur multiple times in a bigger expression.

In [14]:
u = (x+4)**3

h = parse("h")

expr = u + 2*u*h + 4*u*h**2
ccm(expr)
Out[14]:
'pow(x + 4, 3) + 4 * pow(x + 4, 3) * h * h + 2 * pow(x + 4, 3) * h'
  • Obviously, that doesn't lead to great code. In particular, the redundancy is carried through to the code side.
  • Impulse: define variables.
  • Resist for a moment: Use expression as idenntifier. (Valid?)
In [15]:
from pymbolic.primitives import CommonSubexpression as CSE

u = CSE((x+4)**3)

h = parse("h")

expr = u + 2*u*h + 4*u*h**2

result = ccm(expr)

for name, value in ccm.cse_name_list:
    print(name, "=", value)
    
print(result)
_cse0 = pow(x + 4, 3)
_cse0 + 4 * _cse0 * h * h + 2 * _cse0 * h
In [ ]: