From 67e4b5b839b8ec4b1e505ca37c4109ce6122a3b0 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Sat, 1 Jun 2013 15:05:41 -0400
Subject: [PATCH] Make many more mappers multivector-aware. Make MVs hashable.

---
 doc/geometric-algebra.rst     |  1 +
 pymbolic/geometric_algebra.py | 33 +++++++++++++++++++++++++++++----
 pymbolic/mapper/__init__.py   |  6 ++++++
 pymbolic/mapper/evaluator.py  |  3 +++
 4 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/doc/geometric-algebra.rst b/doc/geometric-algebra.rst
index 53e3d5f..e9c5458 100644
--- a/doc/geometric-algebra.rst
+++ b/doc/geometric-algebra.rst
@@ -35,6 +35,7 @@ Multivectors
     .. automethod:: rev
     .. automethod:: invol
     .. automethod:: dual
+    .. automethod:: __inv__
     .. automethod:: norm_squared
     .. automethod:: __abs__
     .. autoattribute:: I
diff --git a/pymbolic/geometric_algebra.py b/pymbolic/geometric_algebra.py
index d7957f2..967deef 100644
--- a/pymbolic/geometric_algebra.py
+++ b/pymbolic/geometric_algebra.py
@@ -421,7 +421,7 @@ class MultiVector(object):
     def __getinitargs__(self):
         return (self.data, self.space)
 
-    mapper_method = "map_multi_vector"
+    mapper_method = "map_multivector"
 
     # {{{ stringification
 
@@ -438,8 +438,8 @@ class MultiVector(object):
             except AttributeError:
                 coeff_str = str(coeff)
             else:
-                from pymbolic.mapper.stringifier import PREC_TIMES
-                coeff_str = strifier(coeff, PREC_TIMES)
+                from pymbolic.mapper.stringifier import PREC_PRODUCT
+                coeff_str = strifier()(coeff, PREC_PRODUCT)
 
             blade_str = self.space.blade_bits_to_str(bits)
             if not blade_str:
@@ -688,11 +688,15 @@ class MultiVector(object):
     def dual(self):
         r"""Return the dual of *self*, see (1.2.26) in [HS].
 
-        Often written :math:`\widetilde A`.
+        Written :math:`\widetilde A` by [HS] and :math:`A^\ast` by [DFW].
         """
 
         return self | self.I.rev()
 
+    def __inv__(self):
+        """Return the dual of *self*, see :meth:`dual`."""
+        return self.dual()
+
     def norm_squared(self):
         return self.rev().scalar_product(self)
 
@@ -709,6 +713,14 @@ class MultiVector(object):
 
     # {{{ comparisons
 
+    @memoize_method
+    def __hash__(self):
+        result = hash(self.space)
+        for bits, coeff in self.data.iteritems():
+            result ^= hash(bits) ^ hash(coeff)
+
+        return result
+
     def __nonzero__(self):
         return bool(self.data)
 
@@ -863,4 +875,17 @@ class MultiVector(object):
 
 # }}}
 
+
+def componentwise(f, expr):
+    """Apply function *f* componentwise to object arrays and
+    :class:`MultiVector` instances. *expr* is also allowed to
+    be a scalar.
+    """
+
+    if isinstance(expr, MultiVector):
+        return expr.map(f)
+
+    from pytools.obj_array import with_object_array_or_scalar
+    return with_object_array_or_scalar(f, expr)
+
 # vim: foldmethod=marker
diff --git a/pymbolic/mapper/__init__.py b/pymbolic/mapper/__init__.py
index cd56ad8..bf3e628 100644
--- a/pymbolic/mapper/__init__.py
+++ b/pymbolic/mapper/__init__.py
@@ -216,6 +216,9 @@ class CombineMapper(RecursiveMapper):
     def map_numpy_array(self, expr, *args):
         return self.combine(self.rec(el) for el in expr.flat)
 
+    def map_multivector(self, expr, *args):
+        return self.combine(self.rec(coeff) for bits, coeff in expr.data.iteritems())
+
     def map_common_subexpression(self, expr, *args):
         return self.rec(expr.child, *args)
 
@@ -338,6 +341,9 @@ class IdentityMapper(Mapper):
             result[i] = self.rec(expr[i])
         return result
 
+    def map_multivector(self, expr, *args):
+        return expr.map(lambda ch: self.rec(ch, *args))
+
     def map_common_subexpression(self, expr, *args, **kwargs):
         from pymbolic.primitives import is_zero
         result = self.rec(expr.child, *args, **kwargs)
diff --git a/pymbolic/mapper/evaluator.py b/pymbolic/mapper/evaluator.py
index c4ef3f6..ec6f93e 100644
--- a/pymbolic/mapper/evaluator.py
+++ b/pymbolic/mapper/evaluator.py
@@ -147,6 +147,9 @@ class EvaluationMapper(RecursiveMapper):
             result[i] = self.rec(expr[i])
         return result
 
+    def map_multivector(self, expr, *args):
+        return expr.map(lambda ch: self.rec(ch, *args))
+
     def map_common_subexpression(self, expr):
         try:
             return self.common_subexp_cache[expr.child]
-- 
GitLab