Skip to content

Commit

Permalink
operator checks
Browse files Browse the repository at this point in the history
  • Loading branch information
Eelco Hoogendoorn committed Nov 4, 2023
1 parent f7b71b4 commit 1bbdb59
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 36 deletions.
28 changes: 14 additions & 14 deletions numga/algebra/test/test_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_complex():

full = complex.subspace.full()
even = complex.subspace.even_grade()
assert even.is_n_simple(1)
assert even.simplicity == 1
c, s = complex.product(even.blades, even.blades)
print(c)
print(s)
Expand Down Expand Up @@ -63,17 +63,17 @@ def test_2d_pga():
rotation_vector = bivector.nondegenerate()
translation_vector = bivector.degenerate()

assert scalar.is_n_simple(0)
assert vector.is_n_simple(1)
assert bivector.is_n_simple(1)
assert rotation_vector.is_n_simple(1)
assert scalar.simplicity == 0
assert vector.simplicity == 1
assert bivector.simplicity == 1
assert rotation_vector.simplicity == 1
assert not rotation_vector.is_degenerate
assert len(rotation_vector) == 1
assert translation_vector.is_n_simple(1)
assert translation_vector.simplicity == 1
assert translation_vector.is_degenerate
assert len(translation_vector) == 2

assert even_grade.is_reverse_n_simple(1)
assert even_grade.simplicity == 1
assert even_grade.is_subalgebra

print(full.bit_blades())
Expand Down Expand Up @@ -104,17 +104,17 @@ def test_3d_pga():
# FIXME: translation motor would be translation_vector + scalar
# should we call it rotor/translator? and is there a natural way to construct it?

assert vector.is_n_simple(1)
assert bivector.is_n_simple(2)
assert rotation_vector.is_n_simple(1)
assert vector.simplicity == 1
assert bivector.simplicity == 2
assert rotation_vector.simplicity == 1
assert not rotation_vector.is_degenerate
assert len(rotation_vector) == 3
assert translation_vector.is_n_simple(1)
assert translation_vector.simplicity == 1
assert translation_vector.is_degenerate
assert len(translation_vector) == 3
assert trivector.is_n_simple(1)
assert even_grade.is_n_simple(2)
assert quaternion.is_n_simple(1)
assert trivector.simplicity == 1
assert even_grade.simplicity == 2
assert quaternion.simplicity == 1

print(full.bit_blades())

Expand Down
32 changes: 23 additions & 9 deletions numga/backend/test/test_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,14 @@ def test_inverse_exhaustive(descr):
for grades in itertools.combinations(all_grades, r+1):
try:
V = ga.subspace.from_grades(list(grades))
print()
print(V.simplicity, list(grades), end='')
# print()
x = random_subspace(ga, V, (N,))
check_inverse(x, x.inverse(), atol=1e-10)
i = x.inverse()
check_inverse(x, i, atol=1e-10)

print(V.simplicity, list(grades), list(np.unique(i.subspace.grades())), end='')
print()
print(i)
except:
pass

Expand Down Expand Up @@ -234,18 +238,28 @@ def test_inverse_degenerate():

def test_inverse_simplicifation_failure():
"""succssive involute products sometimes fail to simplify fully.
this results in extra recursion and poorer high dim genealization
this results in extra recursion and poorer high dim generalization,
and also sometimes dummy zero output grades
"""
ga = NumpyContext(Algebra.from_pqr(5,0,0))
V = ga.subspace.from_grades([1,2,5])
assert V.simplicity == 3 # in reality its two but we lack the symbolic logic to see it
x = random_subspace(ga, V, (1,))
x.inverse()
print()
# can still invert correctly in 3 steps tho
check_inverse(x, x.inverse())

y = x.symmetric_reverse_product()
z = y.symmetric_pseudoscalar_negation_product()
print(x)
print(y)
print(z)
assert z.subspace == ga.subspace.from_grades([0, 5])
assert np.allclose(z.select[5].values, 0)

V = ga.subspace.from_grades([2])
assert V.simplicity == 2 # need two steps; but can do without the extra zeros
x = random_subspace(ga, V, (1,))
i = x.inverse()
assert i.subspace == ga.subspace.from_grades([2, 4])
check_inverse(x, i)
assert np.allclose(i.select[4].values, 0)


def test_inverse_6d():
Expand Down
4 changes: 0 additions & 4 deletions numga/multivector/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@

# NOTE: these methods have a study_ prefix;
# since otherwise theyd shadow equally named functionality with subtly different outcomes
# mv.study_conjugate = SubspaceDispatch("""Study conjugation; tack a minus sign on grade 4 components""")
# @mv.study_conjugate.register(lambda s: s.inside.study())
# def study_conjugate(s: Study) -> Study:
# return s.operator.study_conjugate(s.subspace)(s)
mv.study_norm_squared = SubspaceDispatch("""Study norm squared""")
@mv.study_norm_squared.register(lambda s: s.inside.study())
def study_norm_squared(s: Study) -> Scalar:
Expand Down
14 changes: 5 additions & 9 deletions numga/operator/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,11 @@ def symmetry(self, axes: Tuple[int, int], sign: int) -> "Operator":
# FIXME: is it appropriate to view everything as real numbers at this point?
# i think so; but i keep confusing myself
assert self.axes[axes[0]] == self.axes[axes[1]]
return Operator(
# FIXME: integer division not always appropriate.
# if not, user responsible for slapping on a multiplication factor?
# or do runtime check for inappropriate divisions / remainders?

# FIXME: yeah such a check would be good... already burned myself here with ineria map!
(self.kernel + sign * np.swapaxes(self.kernel, *axes)) // 2,
self.axes
).squeeze()
kernel2 = self.kernel + sign * np.swapaxes(self.kernel, *axes)
kernel = kernel2 // 2
if not np.all(kernel * 2 == kernel2):
raise Exception('inappropriate integer division')
return Operator(kernel, self.axes).squeeze()

def fuse(self, other: "Operator", axis: int) -> "Operator":
"""Fuse to operators; feed output of `self` into nth input argument of `other`"""
Expand Down

0 comments on commit 1bbdb59

Please sign in to comment.