From 59afe3327a84c7daee5601418ce2071c3d03591b Mon Sep 17 00:00:00 2001 From: Dominic Kempf <dominic.kempf@iwr.uni-heidelberg.de> Date: Thu, 6 Sep 2018 16:09:39 +0200 Subject: [PATCH] Implement code generation for matrix inversion in C++ Matrix inversion at code generation time does only work to a very limited extent (up to n=4). We can instead assemble the tensor in C++ and invert it there (e.g. using Dune::FieldMatrix) --- python/dune/perftool/pdelab/__init__.py | 8 ++++- python/dune/perftool/pdelab/tensors.py | 40 +++++++++++++++++++++++++ python/dune/perftool/ufl/preprocess.py | 10 ++++++- python/dune/perftool/ufl/visitor.py | 3 ++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/python/dune/perftool/pdelab/__init__.py b/python/dune/perftool/pdelab/__init__.py index f53b6ff6..7f1ab8d1 100644 --- a/python/dune/perftool/pdelab/__init__.py +++ b/python/dune/perftool/pdelab/__init__.py @@ -32,7 +32,10 @@ from dune.perftool.pdelab.quadrature import (pymbolic_quadrature_weight, ) from dune.perftool.pdelab.spaces import (lfs_inames, ) -from dune.perftool.pdelab.tensors import pymbolic_list_tensor, pymbolic_identity +from dune.perftool.pdelab.tensors import (pymbolic_list_tensor, + pymbolic_identity, + pymbolic_matrix_inverse, + ) class PDELabInterface(object): @@ -112,6 +115,9 @@ class PDELabInterface(object): def pymbolic_identity(self, o): return pymbolic_identity(o) + def pymbolic_matrix_inverse(self, o): + return pymbolic_matrix_inverse(o, self.visitor) + # # Geometry related generator functions # diff --git a/python/dune/perftool/pdelab/tensors.py b/python/dune/perftool/pdelab/tensors.py index 5e962b99..3d89dd70 100644 --- a/python/dune/perftool/pdelab/tensors.py +++ b/python/dune/perftool/pdelab/tensors.py @@ -11,6 +11,7 @@ from dune.perftool.generation import (get_counted_variable, import pymbolic.primitives as prim import numpy as np import loopy as lp +import itertools as it def define_list_tensor(name, expr, visitor, stack=()): @@ -66,3 +67,42 @@ def pymbolic_identity(expr): ) define_identity(name, expr) return prim.Variable(name) + + +def define_assembled_tensor(name, expr, visitor): + temporary_variable(name, + shape=expr.ufl_shape, + shape_impl=('fm',)) + for indices in it.product(*tuple(range(i) for i in expr.ufl_shape)): + visitor.indices = indices + instruction(assignee=prim.Subscript(prim.Variable(name), indices), + expression=visitor.call(expr), + forced_iname_deps=frozenset(visitor.interface.quadrature_inames()), + depends_on=frozenset({lp.match.Tagged("sumfact_stage1")}), + tags=frozenset({"quad"}), + ) + + +@kernel_cached +def name_assembled_tensor(o, visitor): + name = get_counted_variable("assembled_tensor") + define_assembled_tensor(name, o, visitor) + return name + + +@kernel_cached +def pymbolic_matrix_inverse(o, visitor): + indices = visitor.indices + visitor.indices = None + name = name_assembled_tensor(o.ufl_operands[0], visitor) + + instruction(code="{}.invert();".format(name), + within_inames=frozenset(visitor.interface.quadrature_inames()), + depends_on=frozenset({lp.match.Writes(name), + lp.match.Tagged("sumfact_stage1"), + }), + tags=frozenset({"quad"}), + ) + + visitor.indices = indices + return prim.Variable(name) diff --git a/python/dune/perftool/ufl/preprocess.py b/python/dune/perftool/ufl/preprocess.py index 19ca1035..5d7c93a3 100644 --- a/python/dune/perftool/ufl/preprocess.py +++ b/python/dune/perftool/ufl/preprocess.py @@ -2,6 +2,7 @@ import ufl.classes as uc import ufl.algorithms.apply_function_pullbacks as afp +import ufl.algorithms.apply_algebra_lowering as aal from pytools import memoize @@ -34,7 +35,8 @@ def preprocess_form(form): uc.FacetNormal, uc.JacobianDeterminant, uc.JacobianInverse, - ) + ), + do_estimate_degrees=not get_form_option("quadrature_order"), ) formdata.preprocessed_form = apply_default_transformations(formdata.preprocessed_form) @@ -53,3 +55,9 @@ def apply_default_transformations(form): form = transform_form(form, pushdown_indexed) return form + + +# Monkey patch UFL, such that we invert matrices in C++ instead of Python. +# The latter only works for very small matrices. If this causes a problem at +# some point, we should guard this monkey patch with an option. +aal.LowerCompoundAlgebra.inverse = lambda s, o, A: s.reuse_if_untouched(o, A) diff --git a/python/dune/perftool/ufl/visitor.py b/python/dune/perftool/ufl/visitor.py index 9e14391c..860d5db2 100644 --- a/python/dune/perftool/ufl/visitor.py +++ b/python/dune/perftool/ufl/visitor.py @@ -258,6 +258,9 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker): def identity(self, o): return self.interface.pymbolic_identity(o) + def inverse(self, o): + return self.interface.pymbolic_matrix_inverse(o) + # # Handlers for arithmetic operators and functions # Those handlers would be valid in any code going from UFL to pymbolic -- GitLab