Skip to content

Commit

Permalink
Change isBlade to work on denegerate metrics
Browse files Browse the repository at this point in the history
```python
>>> import clifford
>>> layout, _ = clifford.Cl(3, 0, 1, firstIdx=0)
>>> locals().update(layout.blades)
>>> e0.isBlade()
True
>>> e0.isBlade(invertible=True)
False
```
  • Loading branch information
eric-wieser committed Jul 27, 2020
1 parent a37bb85 commit bd449ba
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 8 deletions.
27 changes: 20 additions & 7 deletions clifford/_multivector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import math
from typing import List, Set, Tuple, Union
import warnings
import operator

import numpy as np

Expand Down Expand Up @@ -645,33 +646,45 @@ def isScalar(self) -> bool:

return True

def isBlade(self) -> bool:
def isBlade(self, *, invertible=False) -> bool:
"""Returns true if multivector is a blade.
"""
if len(self.grades()) != 1:
if len(self.grades()) > 1:
return False

return self.isVersor()
return self.isVersor(invertible=invertible)

def isVersor(self) -> bool:
def isVersor(self, *, invertible=True) -> bool:
"""Returns true if multivector is a versor.
From :cite:`ga4cs` section 21.5, definition from 7.6.4
"""
if invertible:
grade_cmp = operator.eq
else:
grade_cmp = operator.le # subset

Vhat = self.gradeInvol()
Vrev = ~self
Vinv = Vrev/(self*Vrev)[()]
mag = (self*Vrev)[()]
if mag != 0:
# not strictly necessary, just scales to make eps appropriate below
Vinv = Vrev / mag
elif invertible:
return False
else:
Vinv = Vrev

# Test if the versor inverse (~V)/(V * ~V) is truly the inverse of the
# multivector V
if (Vhat*Vinv).grades(eps=0.000001) != {0}:
if not grade_cmp((Vhat*Vinv).grades(eps=0.000001), {0}):
return False
if not np.sum(np.abs((Vhat*Vinv).value - (Vinv*Vhat).value)) < 0.0001:
return False

# applying a versor (and hence an invertible blade) to a vector should
# not change the grade
if not all(
(Vhat*e*Vrev).grades(eps=0.000001) == {1}
grade_cmp((Vhat*e*Vrev).grades(eps=0.000001), {1})
for e in cf.basis_vectors(self.layout).values()
):
return False
Expand Down
21 changes: 20 additions & 1 deletion clifford/test/test_clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,14 +631,33 @@ def test_categorization(self, g3):
assert v.isVersor()

neither = [
layout.scalar*0,
1 + e1,
1 + (e1^e2^e3)
]
for n in neither:
assert not n.isBlade()
assert not n.isVersor()

def test_categorization_null(self, g3c):
layout = g3c
e1 = layout.blades['e1']

blades = [
layout.scalar*0,
layout.einf,
layout.eo,
e1^layout.eo
]
for b in blades:
# none of these are invertible
assert not b.isBlade(invertible=True)
assert not b.isVersor(invertible=True)

# but if we allow them to be non-invertible, they are blades and
# versors
assert b.isBlade(invertible=False)
assert b.isVersor(invertible=False)

def test_blades_of_grade(self, g3):
layout = g3
e1 = layout.blades['e1']
Expand Down
9 changes: 9 additions & 0 deletions clifford/test/test_degenerate.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ def POINT(x, y, z):
L = P1 & P2
assert L == -(4^e01) + (3^e02) - (1^e12) - (4^e13) + (3^e23)

def test_is_blade(self):
blades = self.layout.blades
e0 = blades['e0']
e1 = blades['e1']

for b in [e0, e0^e1]:
assert e0.isBlade()
assert not e0.isBlade(invertible=True)

def test_no_crash(self):
""" TODO: This doesn't actually do any asserts! """
layout = self.layout
Expand Down

0 comments on commit bd449ba

Please sign in to comment.