From 3d8276922000aee6964d1bfb44e948e2ee50b25a Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sat, 11 May 2019 02:19:18 -0500 Subject: [PATCH 1/9] WIP: Implement a function registry with support for external calls --- grudge/execution.py | 52 ++----- grudge/function_registry.py | 198 +++++++++++++++++++++++++++ grudge/symbolic/compiler.py | 60 +++++--- grudge/symbolic/dofdesc_inference.py | 9 +- grudge/symbolic/mappers/__init__.py | 10 +- grudge/symbolic/primitives.py | 58 +++----- test/test_grudge.py | 35 +++++ 7 files changed, 319 insertions(+), 103 deletions(-) create mode 100644 grudge/function_registry.py diff --git a/grudge/execution.py b/grudge/execution.py index c20aa4bc..9e48aba3 100644 --- a/grudge/execution.py +++ b/grudge/execution.py @@ -31,6 +31,7 @@ from pytools import memoize_in import grudge.symbolic.mappers as mappers from grudge import sym +from grudge.function_registry import base_function_registry import logging logger = logging.getLogger(__name__) @@ -50,6 +51,7 @@ class ExecutionMapper(mappers.Evaluator, super(ExecutionMapper, self).__init__(context) self.discrwb = bound_op.discrwb self.bound_op = bound_op + self.function_registry = bound_op.function_registry self.queue = queue # {{{ expression mappings ------------------------------------------------- @@ -95,45 +97,8 @@ class ExecutionMapper(mappers.Evaluator, return value def map_call(self, expr): - from pymbolic.primitives import Variable - assert isinstance(expr.function, Variable) - - # FIXME: Make a way to register functions - args = [self.rec(p) for p in expr.parameters] - from numbers import Number - representative_arg = args[0] - if ( - isinstance(representative_arg, Number) - or (isinstance(representative_arg, np.ndarray) - and representative_arg.shape == ())): - func = getattr(np, expr.function.name) - return func(*args) - - cached_name = "map_call_knl_" - - i = Variable("i") - func = Variable(expr.function.name) - if expr.function.name == "fabs": # FIXME - func = Variable("abs") - cached_name += "abs" - else: - cached_name += expr.function.name - - @memoize_in(self.bound_op, cached_name) - def knl(): - knl = lp.make_kernel( - "{[i]: 0<=i Date: Sat, 11 May 2019 08:08:29 -0500 Subject: [PATCH 2/9] Remove dead code --- grudge/symbolic/dofdesc_inference.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/grudge/symbolic/dofdesc_inference.py b/grudge/symbolic/dofdesc_inference.py index 26e5e19a..c44a7940 100644 --- a/grudge/symbolic/dofdesc_inference.py +++ b/grudge/symbolic/dofdesc_inference.py @@ -193,9 +193,6 @@ class DOFDescInferenceMapper(RecursiveMapper, CSECachingMapperMixin): self.function_registry[expr.function.name] .get_result_dofdesc(arg_dds)) - # FIXME - return arg_dds[0] - # }}} # {{{ instruction mappings -- GitLab From 9055bda3472356fbe83f9266be10a02098e1d5bb Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 May 2019 20:00:37 -0500 Subject: [PATCH 3/9] Remove some unrelated changes --- grudge/symbolic/compiler.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/grudge/symbolic/compiler.py b/grudge/symbolic/compiler.py index c2750e33..8c1c5925 100644 --- a/grudge/symbolic/compiler.py +++ b/grudge/symbolic/compiler.py @@ -447,8 +447,7 @@ class Code(object): available_insns = [ (insn, insn.priority) for insn in self.instructions if insn not in done_insns - and all((dep.aggregate.name if isinstance(dep, Subscript) - else dep.name) in available_names + and all(dep.name in available_names for dep in insn.get_dependencies())] if not available_insns: @@ -456,8 +455,7 @@ class Code(object): from pytools import flatten discardable_vars = set(available_names) - set(flatten( - [dep.aggregate.name if isinstance(dep, Subscript) else dep.name - for dep in insn.get_dependencies()] + [dep.name for dep in insn.get_dependencies()] for insn in self.instructions if insn not in done_insns)) @@ -1299,14 +1297,15 @@ class OperatorCompiler(mappers.IdentityMapper): else: # If it's not a C-level function, it shouldn't get muddled up into # a vector math expression. + return self.assign_to_new_var( - codegen_state, - type(expr)( - expr.function, - [self.assign_to_new_var( - codegen_state, - self.rec(par, codegen_state)) - for par in expr.parameters])) + codegen_state, + type(expr)( + expr.function, + [self.assign_to_new_var( + codegen_state, + self.rec(par, codegen_state)) + for par in expr.parameters])) def map_ref_diff_op_binding(self, expr, codegen_state): try: -- GitLab From 0e42886c32db681dddc1787f669db3e5bb27baa5 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 May 2019 20:01:05 -0500 Subject: [PATCH 4/9] Introduce a FunctionSymbol class --- examples/gas_dynamics/lbm-simple.py | 6 +++--- examples/wave/wiggly.py | 4 ++-- grudge/models/em.py | 2 +- grudge/models/gas_dynamics/__init__.py | 8 ++++---- grudge/models/wave.py | 2 +- grudge/symbolic/mappers/__init__.py | 9 ++++++--- grudge/symbolic/operators.py | 6 +++--- grudge/symbolic/primitives.py | 23 +++++++++++++++-------- test/test_grudge.py | 3 +-- 9 files changed, 36 insertions(+), 27 deletions(-) diff --git a/examples/gas_dynamics/lbm-simple.py b/examples/gas_dynamics/lbm-simple.py index f99d5b76..2d3496da 100644 --- a/examples/gas_dynamics/lbm-simple.py +++ b/examples/gas_dynamics/lbm-simple.py @@ -63,12 +63,12 @@ def main(write_output=True, dtype=np.float32): from grudge.data import CompiledExpressionData def ic_expr(t, x, fields): - from grudge.symbolic import CFunction + from grudge.symbolic import FunctionSymbol from pymbolic.primitives import IfPositive from pytools.obj_array import make_obj_array - tanh = CFunction("tanh") - sin = CFunction("sin") + tanh = FunctionSymbol("tanh") + sin = FunctionSymbol("sin") rho = 1 u0 = 0.05 diff --git a/examples/wave/wiggly.py b/examples/wave/wiggly.py index f7860744..11c01212 100644 --- a/examples/wave/wiggly.py +++ b/examples/wave/wiggly.py @@ -70,8 +70,8 @@ def main(write_output=True, from grudge.models.wave import StrongWaveOperator op = StrongWaveOperator(-1, discr.dimensions, source_f= - sym.CFunction("sin")(source_omega*sym.ScalarParameter("t")) - * sym.CFunction("exp")( + sym.FunctionSymbol("sin")(source_omega*sym.ScalarParameter("t")) + * sym.FunctionSymbol("exp")( -np.dot(sym_source_center_dist, sym_source_center_dist) / source_width**2), dirichlet_tag="boundary", diff --git a/grudge/models/em.py b/grudge/models/em.py index f0e44f90..bf7495e2 100644 --- a/grudge/models/em.py +++ b/grudge/models/em.py @@ -355,7 +355,7 @@ class MaxwellOperator(HyperbolicOperator): return 1/sqrt(self.epsilon*self.mu) # a number else: import grudge.symbolic as sym - return sym.NodalMax()(1/sym.CFunction("sqrt")(self.epsilon*self.mu)) + return sym.NodalMax()(1/sym.FunctionSymbol("sqrt")(self.epsilon*self.mu)) def max_eigenvalue(self, t, fields=None, discr=None, context={}): if self.fixed_material: diff --git a/grudge/models/gas_dynamics/__init__.py b/grudge/models/gas_dynamics/__init__.py index e5a8ddc9..4df408d8 100644 --- a/grudge/models/gas_dynamics/__init__.py +++ b/grudge/models/gas_dynamics/__init__.py @@ -326,8 +326,8 @@ class GasDynamicsOperator(TimeDependentOperator): def characteristic_velocity_optemplate(self, state): from grudge.symbolic.operators import ElementwiseMaxOperator - from grudge.symbolic.primitives import CFunction - sqrt = CFunction("sqrt") + from grudge.symbolic.primitives import FunctionSymbol + sqrt = FunctionSymbol("sqrt") sound_speed = cse(sqrt( self.equation_of_state.gamma*self.cse_p(state)/self.cse_rho(state)), @@ -743,8 +743,8 @@ class GasDynamicsOperator(TimeDependentOperator): volq_flux = self.flux(self.volq_state()) faceq_flux = self.flux(self.faceq_state()) - from grudge.symbolic.primitives import CFunction - sqrt = CFunction("sqrt") + from grudge.symbolic.primitives import FunctionSymbol + sqrt = FunctionSymbol("sqrt") speed = self.characteristic_velocity_optemplate(self.state()) diff --git a/grudge/models/wave.py b/grudge/models/wave.py index 40710eb2..0ac60a33 100644 --- a/grudge/models/wave.py +++ b/grudge/models/wave.py @@ -459,7 +459,7 @@ class VariableCoefficientWeakWaveOperator(HyperbolicOperator): self.radiation_tag]) def max_eigenvalue(self, t, fields=None, discr=None): - return sym.NodalMax()(sym.CFunction("fabs")(self.c)) + return sym.NodalMax()(sym.FunctionSymbol("fabs")(self.c)) # }}} diff --git a/grudge/symbolic/mappers/__init__.py b/grudge/symbolic/mappers/__init__.py index bfb60879..304bfb4c 100644 --- a/grudge/symbolic/mappers/__init__.py +++ b/grudge/symbolic/mappers/__init__.py @@ -211,6 +211,7 @@ class IdentityMapperMixin(LocalOpReducerMixin, FluxOpReducerMixin): # it's a leaf--no changing children return expr + map_function_symbol = map_grudge_variable map_ones = map_grudge_variable map_node_coordinate_component = map_grudge_variable @@ -277,7 +278,7 @@ class FlopCounter( def map_grudge_variable(self, expr): return 0 - def map_call(self, expr): + def map_function_symbol(self, expr): return 1 def map_ones(self, expr): @@ -842,6 +843,9 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): def map_grudge_variable(self, expr, enclosing_prec): return "%s:%s" % (expr.name, self._format_dd(expr.dd)) + def map_function_symbol(self, expr, enclosing_prec): + return expr + def map_interpolation(self, expr, enclosing_prec): return "Interp" + self._format_op_dd(expr) @@ -1227,8 +1231,7 @@ class CollectorMixin(OperatorReducerMixin, LocalOpReducerMixin, FluxOpReducerMix return OrderedSet() map_grudge_variable = map_constant - # Found in function call nodes - map_variable = map_grudge_variable + map_function_symbol = map_constant map_ones = map_grudge_variable map_node_coordinate_component = map_grudge_variable diff --git a/grudge/symbolic/operators.py b/grudge/symbolic/operators.py index 53fb1422..a1899ed2 100644 --- a/grudge/symbolic/operators.py +++ b/grudge/symbolic/operators.py @@ -602,16 +602,16 @@ def norm(p, arg, dd=None): if p == 2: norm_squared = sym.NodalSum(dd_in=dd)( - sym.CFunction("fabs")( + sym.FunctionSymbol("fabs")( arg * sym.MassOperator()(arg))) if isinstance(norm_squared, np.ndarray): norm_squared = norm_squared.sum() - return sym.CFunction("sqrt")(norm_squared) + return sym.FunctionSymbol("sqrt")(norm_squared) elif p == np.Inf: - result = sym.NodalMax(dd_in=dd)(sym.CFunction("fabs")(arg)) + result = sym.NodalMax(dd_in=dd)(sym.FunctionSymbol("fabs")(arg)) from pymbolic.primitives import Max if isinstance(result, np.ndarray): diff --git a/grudge/symbolic/primitives.py b/grudge/symbolic/primitives.py index 3c2dc96e..c59f1518 100644 --- a/grudge/symbolic/primitives.py +++ b/grudge/symbolic/primitives.py @@ -354,14 +354,21 @@ def make_sym_mv(name, dim, var_factory=None): make_sym_array(name, dim, var_factory)) -# function symbols -CFunction = Variable -sqrt = Variable("sqrt") -exp = Variable("exp") -sin = Variable("sin") -cos = Variable("cos") -bessel_j = Variable("bessel_j") -bessel_y = Variable("bessel_y") +class FunctionSymbol(ExpressionBase, pymbolic.primitives.Variable): + """A symbol to be used as the function argument of + :class:`pymbolic.primitives.Call`. + + """ + + mapper_method = "map_function_symbol" + + +sqrt = FunctionSymbol("sqrt") +exp = FunctionSymbol("exp") +sin = FunctionSymbol("sin") +cos = FunctionSymbol("cos") +bessel_j = FunctionSymbol("bessel_j") +bessel_y = FunctionSymbol("bessel_y") # }}} diff --git a/test/test_grudge.py b/test/test_grudge.py index f646dff4..288922e0 100644 --- a/test/test_grudge.py +++ b/test/test_grudge.py @@ -554,10 +554,9 @@ def test_external_call(ctx_factory): discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=1) ones = sym.Ones(sym.DD_VOLUME) - from pymbolic.primitives import Variable op = ( ones * 3 - + Variable("double")(ones)) + + sym.FunctionSymbol("double")(ones)) from grudge.function_registry import ( base_function_registry, register_external_function) -- GitLab From 1873e91fb86506468dd8b0e461017eadf5294b18 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 May 2019 20:04:46 -0500 Subject: [PATCH 5/9] Make assertions more strict --- grudge/symbolic/compiler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/grudge/symbolic/compiler.py b/grudge/symbolic/compiler.py index 8c1c5925..864bbed5 100644 --- a/grudge/symbolic/compiler.py +++ b/grudge/symbolic/compiler.py @@ -31,7 +31,7 @@ from six.moves import zip, reduce from pytools import Record, memoize_method, memoize from grudge import sym import grudge.symbolic.mappers as mappers -from pymbolic.primitives import Variable, Subscript, Call +from pymbolic.primitives import Variable, Subscript from six.moves import intern from loopy.version import LOOPY_USE_LANGUAGE_VERSION_2018_1 # noqa: F401 @@ -828,13 +828,15 @@ def aggregate_assignments(inf_mapper, instructions, result, # {{{ to-loopy mapper def is_external_call(expr, function_registry): + from pymbolic.primitives import Call if not isinstance(expr, Call): return False return not is_function_loopyable(expr.function, function_registry) def is_function_loopyable(function, function_registry): - assert isinstance(function, Variable) + from grudge.primitives import FunctionSymbol + assert isinstance(function, FunctionSymbol) return function_registry[function.name].supports_codegen -- GitLab From c40c36212d126da8d2627096b5a12bbbe39d9420 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 May 2019 20:53:18 -0500 Subject: [PATCH 6/9] Fix function printing --- grudge/symbolic/mappers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grudge/symbolic/mappers/__init__.py b/grudge/symbolic/mappers/__init__.py index 304bfb4c..df53cadf 100644 --- a/grudge/symbolic/mappers/__init__.py +++ b/grudge/symbolic/mappers/__init__.py @@ -844,7 +844,7 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): return "%s:%s" % (expr.name, self._format_dd(expr.dd)) def map_function_symbol(self, expr, enclosing_prec): - return expr + return expr.name def map_interpolation(self, expr, enclosing_prec): return "Interp" + self._format_op_dd(expr) -- GitLab From bea73dfb0ca165de932c1385e3359871f2322a72 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 May 2019 20:58:00 -0500 Subject: [PATCH 7/9] Import fix --- grudge/symbolic/compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grudge/symbolic/compiler.py b/grudge/symbolic/compiler.py index 864bbed5..498e5172 100644 --- a/grudge/symbolic/compiler.py +++ b/grudge/symbolic/compiler.py @@ -835,7 +835,7 @@ def is_external_call(expr, function_registry): def is_function_loopyable(function, function_registry): - from grudge.primitives import FunctionSymbol + from grudge.symbolic.primitives import FunctionSymbol assert isinstance(function, FunctionSymbol) return function_registry[function.name].supports_codegen -- GitLab From 12401570bf4a3d391507d652acc6c851aec5faf3 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 May 2019 21:14:18 -0500 Subject: [PATCH 8/9] Change prototype to accept only a single arg --- grudge/function_registry.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/grudge/function_registry.py b/grudge/function_registry.py index 6efa39da..df709f93 100644 --- a/grudge/function_registry.py +++ b/grudge/function_registry.py @@ -71,12 +71,9 @@ class CElementwiseUnaryFunction(Function): assert len(arg_dds) == 1 return arg_dds[0] - def __call__(self, queue, *args): - assert len(args) == 1 - + def __call__(self, queue, arg): func_name = self.identifier - arg, = args from numbers import Number if ( isinstance(arg, Number) -- GitLab From 10d1391f0a85705e68c80f101a0b37d82e9b2e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Sat, 1 Jun 2019 22:12:49 +0200 Subject: [PATCH 9/9] Apply suggestion to grudge/function_registry.py --- grudge/function_registry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/grudge/function_registry.py b/grudge/function_registry.py index df709f93..31961b6f 100644 --- a/grudge/function_registry.py +++ b/grudge/function_registry.py @@ -88,6 +88,7 @@ class CElementwiseUnaryFunction(Function): i = Variable("i") if self.identifier == "fabs": # FIXME + # Loopy has a type-adaptive "abs", but no "fabs". func_name = "abs" cached_name += func_name -- GitLab