Source code for clifford._mvarray

import functools
import operator
from typing import Tuple

import numpy as np

from clifford.io import write_ga_file, read_ga_file  # noqa: F401
from ._layout import Layout
from ._multivector import MultiVector

dual_array = np.vectorize(MultiVector.dual)
normal_array = np.vectorize(MultiVector.normal)
call_array = np.vectorize(MultiVector.__call__)


def _interrogate_nested_mvs(input_array) -> Tuple[Tuple[int, ...], Layout, np.dtype]:
    """
    Calculates the shape of the nested input_array, and gets the associated layout.
    Stops descending when it encounters a MultiVector.
    """
    if not isinstance(input_array, MultiVector):
        nested_shape, layout, dtype = _interrogate_nested_mvs(input_array[0])
        return (len(input_array), *nested_shape), layout, dtype
    else:
        return (), input_array.layout, input_array.value.dtype


def _index_nested_iterable(input_iterable, index):
    """
    Given a nested iterable, input_iterable, return the element given by the
    1d index iterable
    """
    return functools.reduce(operator.getitem, index, input_iterable)


@functools.lru_cache(None)
def _get_vectorized_value_func(dtype):
    return np.vectorize(lambda x: x.value, otypes=[dtype], signature='()->(n)')


[docs]class MVArray(np.ndarray): """ MultiVector Array """ def __new__(cls, input_array): input_shape, layout, dtype = _interrogate_nested_mvs(input_array) # copying this across elementwise is necessary to prevent numpy recursing into the multivector coefficients obj = np.empty(input_shape, dtype=object) for index in np.ndindex(input_shape): obj[index] = _index_nested_iterable(input_array, index) self = obj.view(cls) return self def __array_finalize__(self, obj): if obj is None: return def _get_first_element(self): return self[(0,) * self.ndim] @property def value(self): """ Return an np array of the values of multivectors """ value_dtype = self._get_first_element().value.dtype return _get_vectorized_value_func(value_dtype)(self)
[docs] @staticmethod def from_value_array(layout, value_array): """ Constructs an array of mvs from a value array """ v_new_mv = np.vectorize(lambda v: layout.MultiVector(v), otypes=[object], signature='(n)->()') return MVArray(v_new_mv(value_array))
[docs] def save(self, filename, compression=True, transpose=False, sparse=False, support=False, compression_opts=1): """ Saves the array to a ga file """ first_element = self._get_first_element() write_ga_file(filename, self.value, first_element.layout.metric, first_element.layout.basis_names, compression=compression, transpose=transpose, sparse=sparse, support=support, compression_opts=compression_opts)
[docs] def sum(self): """ sum elements of this MVArray """ out = self[0] for k in self[1:]: out += k return out
[docs] def gp(self): ''' geometric product of all elements of this MVArray (like reduce) like ``self[0]*self[1]*....self[n]`` ''' out = self[0] for k in self[1:]: out *= k return out
[docs] def op(self): ''' outer product of all elements of this MVArray (like reduce) like ``self[0]^self[1]^....self[n]`` ''' out = self[0] for k in self[1:]: out = out^k return out
[docs] def normal(self): """ Normalises all elements """ return normal_array(self)
[docs] def dual(self): """ Takes the dual of all elements """ return dual_array(self)
[docs] def __call__(self, A): """ Performs grade projection on all elements """ return call_array(self, A)
def array(obj): ''' an array method like :func:`numpy.array`, but returns a :class:`.MVArray`. .. versionchanged:: 1.4 Now produces 0d arrays when ``obj`` is a single :class:`MultiVector` Parameters ------------- obj : MultiVector, list a MV or a list of MV's Examples ---------- >>> import clifford as cf >>> from clifford import g3 >>> import numpy as np >>> np.array([1, 2, 3])*cf.array(g3.e12) MVArray([(1^e12), (2^e12), (3^e12)], dtype=object) ''' return MVArray(obj)