Skip to content

Commit

Permalink
Do not convert gradeList back and forth between list and ndarray (#340)
Browse files Browse the repository at this point in the history
This should be a marginal performance boost, but the main goal is to reduce the amount of similar state in `Layout`.
  • Loading branch information
eric-wieser committed Jun 23, 2020
1 parent e378723 commit 3bcbbdb
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 47 deletions.
4 changes: 2 additions & 2 deletions clifford/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def grade_obj(objin, threshold=0.0000001):
'''
Returns the modal grade of a multivector
'''
return grade_obj_func(objin.value, np.asarray(objin.layout.gradeList), threshold)
return grade_obj_func(objin.value, objin.layout._basis_blade_order.grades, threshold)


def grades_present(objin: 'MultiVector', threshold=0.0000001) -> Set[int]:
Expand Down Expand Up @@ -407,7 +407,7 @@ def randomMV(
grades = [grades]
newValue = np.zeros((layout.gaDims,))
for i in range(layout.gaDims):
if layout.gradeList[i] in grades:
if layout._basis_blade_order.grades[i] in grades:
newValue[i] = uniform(min, max)
mv = mvClass(layout, newValue)

Expand Down
57 changes: 25 additions & 32 deletions clifford/_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,6 @@ class Layout(object):
normalized signature, with all values ``+1`` or ``-1``
bladeTupList :
list of blades
gradeList :
corresponding list of the grades of each blade
gaDims :
2**dims
names :
Expand Down Expand Up @@ -334,24 +332,19 @@ def __init__(self, *args, **kw):
self._basis_vector_ids = ids
self._basis_blade_order = order

self.gradeList = list(self._basis_blade_order.grades)
self.gaDims = len(self.gradeList)
self.gaDims = len(order.grades)

self._metric = None

if names is None or isinstance(names, str):
if isinstance(names, str):
e = str(names)
e = names
else:
e = 'e'
self.names = []

for i in range(self.gaDims):
if self.gradeList[i] >= 1:
self.names.append(e + ''.join(
map(str, self.bladeTupList[i])))
else:
self.names.append('')
self.names = [
e + ''.join(map(str, tup)) if tup else ''
for tup in self.bladeTupList
]

elif len(names) == self.gaDims:
self.names = names
Expand All @@ -372,6 +365,10 @@ def __init__(self, *args, **kw):
self.vee_func
self.inv_func

@property
def gradeList(self):
return list(self._basis_blade_order.grades)

@_cached_property
def gmt(self):
r""" Multiplication table for the geometric product.
Expand Down Expand Up @@ -518,25 +515,25 @@ def parse_multivector(self, mv_string: str) -> MultiVector:

def gmt_func_generator(self, grades_a=None, grades_b=None, filter_mask=None):
return get_mult_function(
self.gmt, self.gradeList,
self.gmt, self._basis_blade_order.grades,
grades_a=grades_a, grades_b=grades_b, filter_mask=filter_mask
)

def imt_func_generator(self, grades_a=None, grades_b=None, filter_mask=None):
return get_mult_function(
self.imt, self.gradeList,
self.imt, self._basis_blade_order.grades,
grades_a=grades_a, grades_b=grades_b, filter_mask=filter_mask
)

def omt_func_generator(self, grades_a=None, grades_b=None, filter_mask=None):
return get_mult_function(
self.omt, self.gradeList,
self.omt, self._basis_blade_order.grades,
grades_a=grades_a, grades_b=grades_b, filter_mask=filter_mask
)

def lcmt_func_generator(self, grades_a=None, grades_b=None, filter_mask=None):
return get_mult_function(
self.lcmt, self.gradeList,
self.lcmt, self._basis_blade_order.grades,
grades_a=grades_a, grades_b=grades_b, filter_mask=filter_mask
)

Expand All @@ -545,7 +542,7 @@ def get_grade_projection_matrix(self, grade):
Returns the matrix M_g that performs grade projection via left multiplication
eg. ``M_g@A.value = A(g).value``
"""
diag_mask = 1.0 * (np.array(self.gradeList) == grade)
diag_mask = 1.0 * (self._basis_blade_order.grades == grade)
return np.diag(diag_mask)

def _gen_complement_func(self, omt):
Expand Down Expand Up @@ -573,19 +570,19 @@ def comp_func(Xval):

@_cached_property
def gmt_func(self):
return get_mult_function(self.gmt, self.gradeList)
return get_mult_function(self.gmt, self._basis_blade_order.grades)

@_cached_property
def imt_func(self):
return get_mult_function(self.imt, self.gradeList)
return get_mult_function(self.imt, self._basis_blade_order.grades)

@_cached_property
def omt_func(self):
return get_mult_function(self.omt, self.gradeList)
return get_mult_function(self.omt, self._basis_blade_order.grades)

@_cached_property
def lcmt_func(self):
return get_mult_function(self.lcmt, self.gradeList)
return get_mult_function(self.lcmt, self._basis_blade_order.grades)

@_cached_property
def left_complement_func(self):
Expand All @@ -600,7 +597,7 @@ def adjoint_func(self):
'''
This function returns a fast jitted adjoint function
'''
grades = np.array(self.gradeList)
grades = self._basis_blade_order.grades
signs = np.power(-1, grades*(grades-1)//2)
@_numba_utils.njit
def adjoint_func(value):
Expand All @@ -621,7 +618,7 @@ def inv_func(self):
n_dims = mult_table.shape[1]

identity = np.zeros((n_dims,))
identity[self.gradeList.index(0)] = 1
identity[self._basis_blade_order.bitmap_to_index[0]] = 1

@_numba_utils.njit
def leftLaInvJIT(value):
Expand Down Expand Up @@ -657,15 +654,11 @@ def load_ga_file(self, filename: str) -> 'cf.MVArray':
return cf.MVArray.from_value_array(self, data_array)

def grade_mask(self, grade: int) -> np.ndarray:
return np.equal(grade, self.gradeList)
return grade == self._basis_blade_order.grades

@property
def rotor_mask(self) -> np.ndarray:
return sum(
self.grade_mask(i)
for i in range(self.dims + 1)
if not i % 2
)
return self._basis_blade_order.grades % 2 == 0

@property
def metric(self) -> np.ndarray:
Expand Down Expand Up @@ -769,7 +762,7 @@ def blades_of_grade(self, grade: int) -> List[MultiVector]:
'''
return [
self._basis_blade(i)
for i, i_grade in enumerate(self.gradeList)
for i, i_grade in enumerate(self._basis_blade_order.grades)
if i_grade == grade
]

Expand Down Expand Up @@ -799,7 +792,7 @@ def bases(self, mvClass=MultiVector, grades: Optional[Container[int]] = None) ->
"""
return {
name: self._basis_blade(i, mvClass)
for i, (name, grade) in enumerate(zip(self.names, self.gradeList))
for i, (name, grade) in enumerate(zip(self.names, self._basis_blade_order.grades))
if grades is None or grade in grades
}

Expand Down
20 changes: 7 additions & 13 deletions clifford/_multivector.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,13 +442,10 @@ def __call__(self, other, *others) -> 'MultiVector':
if len(others) != 0:
return sum([self.__call__(k) for k in (other,)+others])

if grade not in self.layout.gradeList:
raise ValueError("algebra does not have grade %s" % grade)

if not np.issubdtype(type(grade), np.integer):
raise ValueError("grade must be an integer")

mask = np.equal(grade, self.layout.gradeList)
mask = self.layout.grade_mask(grade)

newValue = np.multiply(mask, self.value)

Expand All @@ -462,7 +459,7 @@ def __str__(self) -> str:
s = ''
p = _settings._print_precision

for grade, name, coeff in zip(self.layout.gradeList, self.layout.names, self.value):
for grade, name, coeff in zip(self.layout._basis_blade_order.grades, self.layout.names, self.value):
# if we have nothing yet, don't use + and - as operators but
# use - as an unary prefix if necessary
if s:
Expand Down Expand Up @@ -616,7 +613,7 @@ def isScalar(self) -> bool:
"""

indices = list(range(self.layout.gaDims))
indices.remove(self.layout.gradeList.index(0))
del indices[self.layout._basis_blade_order.bitmap_to_index[0]]

for i in indices:
if abs(self.value[i]) < _settings._eps:
Expand Down Expand Up @@ -669,12 +666,9 @@ def grades(self, eps=None) -> Set[int]:
"""
if eps is None:
eps = _settings._eps
nonzero = abs(self.value) > eps
return {
grade_i
for grade_i, nonzero_i in zip(self.layout.gradeList, nonzero)
if nonzero_i
}
nonzero_grades = self.layout._basis_blade_order.grades[abs(self.value) > eps]

return set(nonzero_grades)

@property
def blades_list(self) -> List['MultiVector']:
Expand Down Expand Up @@ -786,7 +780,7 @@ def gradeInvol(self) -> 'MultiVector':
{(-1)^i \left<M\right>_i}
"""

signs = np.power(-1, self.layout.gradeList)
signs = np.power(-1, self.layout._basis_blade_order.grades)

newValue = signs * self.value

Expand Down
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
Changelog
=========

Changes in 1.4.x
++++++++++++++++

* Projection using :meth:`Multivector.__call__` no longer raises :exc:`ValueError`
for grades not present in the algebra, and instead just returns zero.

Changes in 1.3.x
++++++++++++++++

Expand Down

0 comments on commit 3bcbbdb

Please sign in to comment.