From 0ed642df5c3f9b5cd082a28cb0238a63d77a6f5c Mon Sep 17 00:00:00 2001 From: Karl Wessel Date: Mon, 10 Jun 2019 11:21:49 +0200 Subject: [PATCH 1/4] added tests for issues and some generic algebra tests --- test/generictests.jl | 183 +++++++++++++++++++++++++++++++++++++++++++ test/issuestests.jl | 83 ++++++++++++++++++++ test/runtests.jl | 3 + 3 files changed, 269 insertions(+) create mode 100644 test/generictests.jl create mode 100644 test/issuestests.jl diff --git a/test/generictests.jl b/test/generictests.jl new file mode 100644 index 0000000..301bfa9 --- /dev/null +++ b/test/generictests.jl @@ -0,0 +1,183 @@ + +""" +Tests based on mathematic properties of a Geometric Algebra that should pass +for any signature or field. +""" +module GenericTests +using Grassmann +using Test +using LinearAlgebra +import Base: isapprox + +# we need an approx method for multivectors for the tests +function isapprox(a::TensorMixed{T1}, b::TensorMixed{T2}) where {T1, T2} + rtol = Base.rtoldefault(T1, T2, 0) + return norm(value(a-b)) <= rtol * max(norm(value(a)), norm(value(b))) +end +isapprox(a::TensorMixed, b::TensorTerm) = isapprox(a, MultiVector(b)) +isapprox(b::TensorTerm, a::TensorMixed) = isapprox(a, MultiVector(b)) +isapprox(a::TensorTerm, b::TensorTerm) = isapprox(MultiVector(a), MultiVector(b)) + +@testset "Test isapprox" begin + basis"2" + + # basis + @test v ≈ v + @test v1 ≈ v1 + @test v2 ≈ v2 + @test v12 ≈ v12 + # S/MValue + @test 2v ≈ 2v + @test 2v1 ≈ 2v1 + # blade + @test v1 + v2 ≈ v1 + v2 + # multivector + @test v + v2 ≈ v + v2 + @test v + v12 ≈ v + v12 + @test v + v2 + v12 ≈ v + v2 + v12 + + # basis and others + @test !(v ≈ v1) + @test !(v ≈ v12) + @test !(v ≈ v1+v2) + @test !(v ≈ v1+v) + @test !(v ≈ v1+v12) + @test !(v ≈ v+v1+v12) + + # S/MValue and others + @test !(2v ≈ v1) + @test !(2v ≈ v12) + @test !(2v ≈ v1+v2) + @test !(2v ≈ v1+v) + @test !(2v ≈ v1+v12) + @test !(2v ≈ v+v1+v12) + + # Blade and others + @test !(v1 + v2 ≈ v1) + @test !(v1 + v2 ≈ v12) + @test !(v1 + v2 ≈ v1+v) + @test !(v1 + v2 ≈ v1+v12) + @test !(v1 + v2 ≈ v+v1+v12) + + # multivector and others + @test !(v+v1+v12 ≈ v1) + @test !(v+v1+v12 ≈ v12) + @test !(v+v1+v12 ≈ v1+v) + @test !(v+v1+v12 ≈ v1+v12) +end + +""" +Return the scalar (grade 0) part of any multivector. +""" +scalar(a::TensorMixed) = grade(a) == 0 ? a[1] : 0 +scalar(a::MultiVector) = a[0][1] +scalar(a::TensorAlgebra) = scalar(MultiVector(a)) +@testset "method: scalar" begin + basis"2" + @test scalar(v) == 1v + @test scalar(2v) == 2v + @test scalar(v1) == 0v + @test scalar(-v+v1) == -1v + @test scalar(v-v) == 0v + @test scalar(v1+v2) == 0v +end + +for 𝔽 in [Float64] + e = one(𝔽) + α = rand(𝔽) + @test α*e == α + @testset "Field: $(𝔽)" begin + for G in [V"+++", S"∞+", S"∅+", V"-+++", + S"∞∅+" # is currently broken for associativity + ] + @testset "Algebra: $(G)" begin + dims = length(G) + + # all basis elements of the whole algebra + basis = collect(G)[1:end] + + # all basis vectors of the generating vectorspace + basisvecs = basis[2:dims+1] + + # test set of vectors + b = collect(1:dims) + B = sum(b.*basisvecs) + vectors = Any[basisvecs...] + push!(vectors, B) + + # test set of multivectors + a = rand(𝔽, 2^dims) + A = sum(a.*basis) + multivectors = Any[basis...] + push!(multivectors, A) + push!(multivectors, B) + + @testset "Existence of unity" begin + for A in multivectors + @test e*A == A == A*e + end + end + + @testset "a² ∈ 𝔽" begin + for a in vectors + @test a^2 ≈ scalar(a^2)*basis[1] + end + end + + # currently fails for S"∞∅+" + if G != S"∞∅+" + @testset "Associativity" begin + for A in multivectors, + B in multivectors, + C in multivectors + + @test (A*(B*C)) ≈ ((A*B)*C) + end + end + + # currently fails for S"∞∅+" + @testset "Distributivity" begin + for A in multivectors, + B in multivectors, + C in multivectors + + @test A*(B + C) ≈ (A*B) + (A*C) + end + end + end + + @testset "a⋅b = 0.5(ab + ba)" begin + for a in vectors, b in vectors + @test a⋅b == 0.5*(a*b + b*a) + end + end + + @testset "a∧b = 0.5(ab - ba)" begin + for a in vectors, b in vectors + @test a∧b == 0.5*(a*b - b*a) + end + end + + @testset "ab = a⋅b + a∧b" begin + for a in vectors, b in vectors + @test a*b == a⋅b + a∧b + end + end + + @testset "ab = 2a⋅b - ba" begin + for a in vectors, b in vectors + @test a*b == 2*a⋅b - b*a + end + end + + @testset "aa = a⋅a" begin + for a in vectors + @test a*a == a⋅a + end + end + end + end + end +end + +end diff --git a/test/issuestests.jl b/test/issuestests.jl new file mode 100644 index 0000000..c570016 --- /dev/null +++ b/test/issuestests.jl @@ -0,0 +1,83 @@ +""" +Unittests for issues from Github +""" +module IssuesTests + +using Grassmann +using Test + +@testset "Issue #19: Conformal split example" begin + @basis S"∞∅++" + @test (v∞^2, v∅^2, v1^2, v2^2) == (0v, 0v, v, v) + @test v∞ ⋅ v∅ == -1v + @test v∞∅^2 == v + @test (v∞∅ * v∞, v∞∅ * v∅) == (-1v∞, v∅) + @test (v∞ * v∅, v∅ * v∞) == (-1 + 1v∞∅, -1 - 1v∞∅) +end + +@testset "Issue #17: Equality between Number and MultiVector" begin + basis"2" + + a = v + v1 - v1 + @test a == v + @test typeof(a) <: MultiVector + @test a == 1 + + b = a - 1 + @test b == 0 + @test b == 0 + + @test a - 1 == 0 +end + +@testset "Issue #16: Adding basis vector to multivector" begin + basis"2" + + A = 2v1 + v2 + B = v1 + v2 + + @test A + B == 3v1 + 2v2 + @test A == 2v1 + v2 + @test B == v1 + v2 + + @test v1 + A == 3v1 + 1v2 + @test A == 2v1 + v2 +end + +@testset "Issue #14: Error when adding MultiVectors" begin + basis"+++" + @test (v1+v2) + (v1+v2)*(v1+v2) == 2 + 1v1 + 1v2 +end + +@testset "Issue #15: generate rotation quaternions using exp" begin + basis"3" + i, j, k = hyperplanes(ℝ^3) + alpha = 0.5π + + @test exp(alpha/2*(i)) == 0.7071067811865476 - 0.7071067811865475v23 + + a, b, c = 1/sqrt(2) * [1, 1, 0] + @test_broken exp(alpha/2*(a*i + b*j + c*k)) +end + +@testset "Issue #20: geometric product of null basis and negative origin" begin + @basis S"∞∅+" + + @test v∅*v∞ == -1 - v∞∅ + @test_broken v∅*(-v∞) == 1 + v∞∅ + + a = v∅*basis(-v∞) + @test a == -1 - v∞∅ + @test_broken SValue{V}(-1, a) +end + +@testset "Issue 22: Error in MultiVector constructor for Blades" begin + basis"++" + + a = v1 + v2 + @test typeof(a) <: MBlade + + @test_broken MultiVector(a) + @test_broken SBlade(v) +end +end diff --git a/test/runtests.jl b/test/runtests.jl index 2ed4ab4..a9da6a6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,3 +12,6 @@ using Test @test Λ.V3 == Λ.C3' @test Λ(14) + Λ(14)' == Λ(vectorspace(14)+vectorspace(14)') @test ((a,b) = ((:a*Λ(2).v1 + :b*Λ(2).v2),(:c*Λ(2).v1 + :d*Λ(2).v2)); Algebra.:+(a∧b,a⋅b)==a*b) + +include("issuestests.jl") +include("generictests.jl") From 8b61caa11bc2eb1965443600fc6fa2573d9dfdc5 Mon Sep 17 00:00:00 2001 From: Karl Wessel Date: Mon, 10 Jun 2019 20:35:31 +0200 Subject: [PATCH 2/4] moved isapprox and scalar to multivectors.jl --- src/multivectors.jl | 21 +++++++++++++++++++-- test/generictests.jl | 18 ++---------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/multivectors.jl b/src/multivectors.jl index 423c637..2567e20 100644 --- a/src/multivectors.jl +++ b/src/multivectors.jl @@ -311,8 +311,8 @@ end ## Generic -import Base: isinf -export basis, grade, hasinf, hasorigin, isorigin +import Base: isinf, isapprox +export basis, grade, hasinf, hasorigin, isorigin, scalar const VBV = Union{MValue,SValue,MBlade,SBlade,MultiVector} @@ -336,6 +336,23 @@ const VBV = Union{MValue,SValue,MBlade,SBlade,MultiVector} @pure hasorigin(t::Union{MValue,SValue}) = hasorigin(basis(t)) @pure hasorigin(m::TensorAlgebra) = hasorigin(vectorspace(m)) +function isapprox(a::TensorMixed{T1}, b::TensorMixed{T2}) where {T1, T2} + rtol = Base.rtoldefault(T1, T2, 0) + return norm(value(a-b)) <= rtol * max(norm(value(a)), norm(value(b))) +end +isapprox(a::TensorMixed, b::TensorTerm) = isapprox(a, MultiVector(b)) +isapprox(b::TensorTerm, a::TensorMixed) = isapprox(a, MultiVector(b)) +isapprox(a::TensorTerm, b::TensorTerm) = isapprox(MultiVector(a), MultiVector(b)) + +""" + scalar(multivector) + +Return the scalar (grade 0) part of any multivector. +""" +scalar(a::TensorMixed) = grade(a) == 0 ? a[1] : 0 +scalar(a::MultiVector) = a[0][1] +scalar(a::TensorAlgebra) = scalar(MultiVector(a)) + ## MultiGrade{N} struct MultiGrade{V} <: TensorAlgebra{V} diff --git a/test/generictests.jl b/test/generictests.jl index 301bfa9..228bd63 100644 --- a/test/generictests.jl +++ b/test/generictests.jl @@ -7,17 +7,9 @@ module GenericTests using Grassmann using Test using LinearAlgebra -import Base: isapprox - -# we need an approx method for multivectors for the tests -function isapprox(a::TensorMixed{T1}, b::TensorMixed{T2}) where {T1, T2} - rtol = Base.rtoldefault(T1, T2, 0) - return norm(value(a-b)) <= rtol * max(norm(value(a)), norm(value(b))) -end -isapprox(a::TensorMixed, b::TensorTerm) = isapprox(a, MultiVector(b)) -isapprox(b::TensorTerm, a::TensorMixed) = isapprox(a, MultiVector(b)) -isapprox(a::TensorTerm, b::TensorTerm) = isapprox(MultiVector(a), MultiVector(b)) +# TODO: move tests for isapprox and scalar to a more appropriate file +# since they are no general mathematical properties of an GA @testset "Test isapprox" begin basis"2" @@ -66,12 +58,6 @@ isapprox(a::TensorTerm, b::TensorTerm) = isapprox(MultiVector(a), MultiVector(b) @test !(v+v1+v12 ≈ v1+v12) end -""" -Return the scalar (grade 0) part of any multivector. -""" -scalar(a::TensorMixed) = grade(a) == 0 ? a[1] : 0 -scalar(a::MultiVector) = a[0][1] -scalar(a::TensorAlgebra) = scalar(MultiVector(a)) @testset "method: scalar" begin basis"2" @test scalar(v) == 1v From 7aef92302100b0891d3670b03ded5051843973ce Mon Sep 17 00:00:00 2001 From: Karl Wessel Date: Mon, 10 Jun 2019 20:55:24 +0200 Subject: [PATCH 3/4] use ndims instead of length to get the number of dimensions of an algebra --- test/generictests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/generictests.jl b/test/generictests.jl index 228bd63..b41043d 100644 --- a/test/generictests.jl +++ b/test/generictests.jl @@ -77,7 +77,7 @@ for 𝔽 in [Float64] S"∞∅+" # is currently broken for associativity ] @testset "Algebra: $(G)" begin - dims = length(G) + dims = ndims(G) # all basis elements of the whole algebra basis = collect(G)[1:end] From 35f2c6c5cbf6f8124baf0112cc26dbf703da1a88 Mon Sep 17 00:00:00 2001 From: Karl Wessel Date: Mon, 10 Jun 2019 20:56:00 +0200 Subject: [PATCH 4/4] issue #22 is partially fixed now --- test/issuestests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/issuestests.jl b/test/issuestests.jl index c570016..6c06efb 100644 --- a/test/issuestests.jl +++ b/test/issuestests.jl @@ -71,13 +71,13 @@ end @test_broken SValue{V}(-1, a) end -@testset "Issue 22: Error in MultiVector constructor for Blades" begin +@testset "Issue #22: Error in MultiVector constructor for Blades" begin basis"++" a = v1 + v2 @test typeof(a) <: MBlade - @test_broken MultiVector(a) - @test_broken SBlade(v) + @test MultiVector(a) == v1 + v2 + @test_broken SBlade(v) == v end end