Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions galgebra/mv.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,25 +935,37 @@ def is_base(self) -> bool:

def is_versor(self) -> bool:
"""
Test for versor (geometric product of vectors)
Test if `self` is a versor.

This follows Leo Dorst's test for a versor.
Leo Dorst, 'Geometric Algebra for Computer Science,' p.533
Sets self.versor_flg and returns value
"""
A versor is defined as a geometric product of finitely-many nonnull vectors.

According to Greg Grunberg's analysis, the following are necessary and sufficient
conditions for a multivector V to be a versor:

1. V*V.rev() must be a nonzero scalar
2. V.g_invol()*x*V.rev() must be a vector whenever x is a vector

Sets self.versor_flg and returns value.

See :issue:`533` for more discussions.
"""
if self.versor_flg is not None:
return self.versor_flg

self.characterise_Mv()
self.versor_flg = False
self_rev = self.rev()
# see if self*self.rev() is a scalar
test = self*self_rev
if not test.is_scalar():

# Test condition 1: V*V.rev() must be a nonzero scalar
VVrev = self * self_rev
if not (VVrev.is_scalar() and not VVrev.is_zero()):
self.versor_flg = False
return self.versor_flg
# see if self*x*self.rev() returns a vector for x an arbitrary vector
test = self * self.Ga._XOX * self.rev()
self.versor_flg = test.is_vector()

# Test condition 2: V.g_invol()*x*V.rev() must be a vector
# where x is a generic vector
# and self.Ga._XOX is a generic vector in self's geometric algebra
VinvolXVrev = self.g_invol() * self.Ga._XOX * self_rev
self.versor_flg = VinvolXVrev.is_vector()
return self.versor_flg

r'''
Expand Down
77 changes: 77 additions & 0 deletions test/test_versors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import unittest
from galgebra.ga import Ga
from sympy import S


class TestVersors(unittest.TestCase):
def setUp(self):
# Set up different geometric algebras for testing

# G(3,0) - Euclidean 3-space
self.g3d = Ga("e1 e2 e3", g=[1, 1, 1])

# G(1,3) - Spacetime algebra
self.sta = Ga("e0 e1 e2 e3", g=[1, -1, -1, -1])


def test_basis_vectors_are_versors(self):
"""Individual basis vectors should be versors"""
e1, e2, e3 = self.g3d.mv()
self.assertTrue(e1.is_versor())
self.assertTrue(e2.is_versor())
self.assertTrue(e3.is_versor())

def test_mixed_grades_not_versor(self):
"""A sum of different grades cannot be a versor"""
e1, e2, e3 = self.g3d.mv()
mixed = e1 + e2 * e3 # vector + bivector
self.assertFalse(mixed.is_versor())

def test_dorst_counterexample(self):
"""Test Greg1950's counterexample from the spacetime algebra"""
e0, e1, e2, e3 = self.sta.mv()
V = S.One + self.sta.I() # 1 + I
self.assertFalse(V.is_versor())

def test_rotors_are_versors(self):
"""Test that rotors (even-grade versors) are properly detected"""
e1, e2, e3 = self.g3d.mv()
rotor = S.One + e1 * e2 # 1 + e1e2
self.assertTrue(rotor.is_versor())

def test_null_is_not_versor(self):
"""Test that null multivectors are not versors"""
e1, e2, e3 = self.g3d.mv()
null_mv = e1 + e1 * self.g3d.I() # v + vI (squares to 0)
self.assertFalse(null_mv.is_versor())

def test_scalar_versor(self):
"""Test that nonzero scalars are versors"""
self.assertTrue(self.g3d.mv(2).is_versor())
self.assertFalse(self.g3d.mv(0).is_versor())

def test_bivector_exponential(self):
"""Test that exponentials of bivectors are versors"""
e1, e2, e3 = self.g3d.mv()
B = e1 * e2 # bivector
rotor = (B/2).exp() # exp(B/2) is a rotor
self.assertTrue(rotor.is_versor())

def test_product_of_vectors(self):
"""Test that products of vectors are versors"""
e1, e2, e3 = self.g3d.mv()
v1 = e1 + 2*e2 # vector
v2 = 3*e1 - e3 # vector
self.assertTrue((v1 * v2).is_versor())

def test_degenerate_metric(self):
"""Test versor detection in a degenerate metric G(2,0,1) (PGA)"""
pga = Ga("e0 e1 e2", g=[0, 1, 1]) # e0^2=0, e1^2=1, e2^2=1
e0, e1, e2 = pga.mv()
# Non-null basis vectors are versors
self.assertTrue(e1.is_versor())
self.assertTrue(e2.is_versor())
# Null vector (e0^2 = 0) is not a versor
self.assertFalse(e0.is_versor())
# Product of non-null vectors is a versor
self.assertTrue((e1 * e2).is_versor())
Loading