diff --git a/src/mv.jl b/src/mv.jl index 0d5a52b..0227b11 100644 --- a/src/mv.jl +++ b/src/mv.jl @@ -13,6 +13,7 @@ import Base: abs, inv, adjoint, exp, getindex export Mv export norm, rev, dual, involute, proj, refl, rot, exp_with_hint, scalar, even, odd +export undual, mag2, mag, shirokov_inverse, hitzer_inverse, sp # export \cdot, \wedge, \intprod, \intprodr, \odot, \boxtimes, \circledast, \times export +, -, *, /, ^, |, %, ==, !=, <, >, <<, >>, ~, ⋅, ∧, ⨼, ⨽, ⊙, ⊠, ⊛, × # Operator precedence: they have the same precedence, unlike in math @@ -364,3 +365,60 @@ Odd-grade part. x.__pow__(y) end end + +# ── galgebra 0.6.0 additions ────────────────────────────────────────────────── + +@doc raw""" +Inverse of `dual()`: `undual(dual(A)) == A` and `dual(undual(A)) == A`. + +`undual(A) = A.undual()` ``\equiv I^2 A^{\bot}`` +""" +@define_unary_op(Mv, undual, undual) + +@doc raw""" +Squared magnitude: sum of absolute values of grade-wise norm-squareds. + +`mag2(A) = A.mag2()` + +For Euclidean metrics this equals `norm(A)^2`; for non-Euclidean metrics +it differs from `norm2` since it sums `|⟨A⟩_r * ~⟨A⟩_r|` per grade. +""" +@define_unary_op(Mv, mag2, mag2) + +@doc raw""" +Magnitude: square root of `mag2(A)`. + +`mag(A) = A.mag()` + +Equals `norm(A)` only for Euclidean metrics. +""" +@define_unary_op(Mv, mag, mag) + +@doc raw""" +Multivector inverse via Shirokov's algorithm (Theorem 4, arXiv:2005.04015). + +`shirokov_inverse(A) = A.shirokov_inverse()` + +Works for any Clifford algebra. Raises an error if A has no inverse. +""" +@define_unary_op(Mv, shirokov_inverse, shirokov_inverse) + +@doc raw""" +Multivector inverse via Hitzer–Sangwine algorithm. + +`hitzer_inverse(A) = A.hitzer_inverse()` + +Efficient for ``n < 6`` (number of basis vectors). Raises an error if A has no inverse. +""" +@define_unary_op(Mv, hitzer_inverse, hitzer_inverse) + +@doc raw""" +Scalar product of A and B: ``\langle A B \rangle``. + +`sp(A, B)` ``\equiv (A B)_0`` + +Note: differs from `A ⊛ B` which is ``\langle A \tilde{B} \rangle``. +Pass `switch="rev"` to use ``\langle \tilde{A} B \rangle`` instead. +""" +sp(A::Mv, B::Mv) = A.sp(B) +sp(A::Mv, B::Mv, switch::AbstractString) = A.sp(B, switch) diff --git a/test/runtests.jl b/test/runtests.jl index f2041c3..190bb3f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -104,6 +104,10 @@ end if V ∉ [Spacetime, PGA2D, PGA3D, CGA2D, CGA3D] @test abs(R) == norm(R) == R.norm() end + + # galgebra 0.6.0 additions: mag2/mag (grade-wise sum of |norm2|, differs from norm in non-Euclidean) + @test mag2(v) == v.mag2() + @test mag(v) == v.mag() @test ~A == A[:~] == rev(A) == A.rev() @@ -111,10 +115,16 @@ end @test A' == dual(A) == A.dual() == adjoint(A) == A * I # Ga.dual_mode_value is default to "I+" @test (v)⁻¹ == v[:⁻¹] == v^-1 == inv(v) == v.inv() @test v^-2 == (v^2).inv() + # galgebra 0.6.0: undual is inverse of dual + @test undual(dual(v)) == v + @test dual(undual(v)) == v end @test (A)ˣ == A[:*] == involute(A) == (A)₊ - (A)₋ == A[:+] - A[:-] == A.even() - A.odd() - @test (A)ǂ == A[:ǂ] == conj(A) == involute(A).rev() + @test (A)ǂ == A[:ǂ] == conj(A) == involute(A).rev() + # galgebra 0.6.0: g_invol() and ccon() are new names for involute/conj + @test involute(A) == A.g_invol() + @test conj(A) == A.ccon() if V ∉ [Spacetime, PGA2D, PGA3D, CGA2D, CGA3D] @test R^-2 == (R^2).inv() @@ -126,6 +136,9 @@ end if V ∈ [Cl2, Cl3] @test (v)⁻¹ == (~v) / norm(v)^2 == v / v^2 @test (R)⁻¹ == (~R) / norm(R)^2 == R / R^2 + # galgebra 0.6.0: alternative inverse algorithms + @test shirokov_inverse(v) == v.shirokov_inverse() == inv(v) + @test hitzer_inverse(v) == v.hitzer_inverse() == inv(v) end @test v^0 == 1 @@ -147,6 +160,9 @@ end # @test typeof(scalar(A)) == Sym @test typeof(A[0]) == Mv @test scalar(A) == A.scalar() == A[0].obj + # galgebra 0.6.0: sp is (A*B).scalar(), distinct from A ⊛ B = (A*~B).scalar() + @test sp(A, B) == A.sp(B) + @test sp(A, B) == (A * B).scalar() @test (A)₊ == A[:+] == even(A) == A.even() @test (A)₋ == A[:-] == odd(A) == A.odd()