Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing tests #74

Merged
merged 64 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
e303f90
Add missing tests from #65
sethaxen Feb 25, 2022
e68d91d
Add basic tests
sethaxen Feb 25, 2022
8fa3c42
Add constructor tests
sethaxen Feb 25, 2022
47d2583
Test type-inferribility
sethaxen Feb 25, 2022
91347bc
Add equality checks
sethaxen Feb 25, 2022
2c40abc
Rename parameters
sethaxen Feb 25, 2022
cd03889
Add more tests for conversions and promotions
sethaxen Feb 25, 2022
1dd3f80
Test isreal and isone
sethaxen Feb 25, 2022
07e1ad3
Remove unnecessary methods
sethaxen Feb 25, 2022
613567e
Swap order of arguments
sethaxen Feb 25, 2022
90e4912
Add missing shorthand tests
sethaxen Feb 25, 2022
916d862
Test normalization functions
sethaxen Feb 25, 2022
e46fd87
Test slerp and linpol for overlapping points
sethaxen Feb 25, 2022
af90ff8
Test exceptions raised for incorrect axis length
sethaxen Feb 25, 2022
d50c7b7
Remove unreachable branch
sethaxen Feb 25, 2022
12f51f0
Test slerp approximately linear interpolation
sethaxen Feb 25, 2022
9dbe09b
Reformat type constructor
sethaxen Feb 25, 2022
00dbd1f
Remove unnecessary constructor
sethaxen Feb 25, 2022
aaebd57
Add missing constructor
sethaxen Feb 25, 2022
ac9c3e9
Add constructor tests
sethaxen Feb 25, 2022
4335870
Add conversion and promotion tests
sethaxen Feb 25, 2022
5fec7a4
Add shorthand tests
sethaxen Feb 25, 2022
57a3ed2
Remove unnecessary promotions
sethaxen Feb 25, 2022
a785b72
Define type constructors instead of conversions
sethaxen Feb 25, 2022
7cb8d88
Add basic tests
sethaxen Feb 25, 2022
a023e6c
Add abs_imag for Octonion
sethaxen Feb 25, 2022
e7737d9
Use abs_imag
sethaxen Feb 25, 2022
3e41fcf
Add and test isXXX functions
sethaxen Feb 25, 2022
9c24c3d
Test pow functions
sethaxen Feb 25, 2022
7d2646f
Test analytic and nob-analytic functions
sethaxen Feb 25, 2022
b742c0d
Ensure log(0) is -Inf
sethaxen Feb 25, 2022
101118b
Add more checks for inferribility
sethaxen Feb 25, 2022
0283e9e
Quaternion to Octonion
sethaxen Feb 25, 2022
36f3a05
Fix test for 1.0
sethaxen Feb 25, 2022
951cc15
Test octorand
sethaxen Feb 25, 2022
8f8dcc8
Test division
sethaxen Feb 25, 2022
b258e1c
Fix test bug
sethaxen Feb 25, 2022
978653d
Add and expand constructors
sethaxen Feb 27, 2022
b3b3d2f
Test new constructors
sethaxen Feb 27, 2022
7ac0dd7
Remove whitespace
sethaxen Feb 27, 2022
d6d46de
Remove unneeded convert methods
sethaxen Feb 27, 2022
9c85baa
Allow passing single quaternion
sethaxen Feb 27, 2022
1211265
Repair constructors and add tests
sethaxen Feb 27, 2022
5a4e8b3
Correctly determine if is norm
sethaxen Feb 27, 2022
9936098
Simplify and test promote rules
sethaxen Feb 27, 2022
7418d87
Test remaining rand functions
sethaxen Feb 27, 2022
7492f87
Test basic functionality
sethaxen Feb 27, 2022
fbe6ce3
Remove duplicate constructor
sethaxen Feb 27, 2022
b4f156f
Add missing type parameter
sethaxen Feb 27, 2022
5e1072d
Test algebraic properties
sethaxen Mar 4, 2022
285b8eb
Add missing imports
sethaxen Mar 5, 2022
dae78d9
Repair normalizea
sethaxen Mar 5, 2022
157f885
Add more tests
sethaxen Mar 5, 2022
6a85c5a
Fix tests
sethaxen Mar 5, 2022
e671bb0
Merge remote-tracking branch 'upstream/master' into moretests
sethaxen Mar 5, 2022
1bed25d
Add ForwardDiff as test dependency
sethaxen Mar 5, 2022
bcedc51
Add missing tests
sethaxen Mar 5, 2022
5fc3d49
Test missing branches
sethaxen Mar 5, 2022
73b3259
Run formatter
sethaxen Mar 5, 2022
5c6f3b9
Increment version number
sethaxen Mar 5, 2022
260d96a
Apply suggestions from code review
sethaxen Mar 6, 2022
1b8abe0
Add tests for consistency
sethaxen Mar 6, 2022
5a419dd
Update test/DualQuaternion.jl
sethaxen Mar 7, 2022
a779f44
Test 100 instead of 10
sethaxen Mar 7, 2022
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
5 changes: 3 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Quaternions"
uuid = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
version = "0.5.1"
version = "0.5.2"

[deps]
DualNumbers = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
Expand All @@ -12,7 +12,8 @@ DualNumbers = "0.5, 0.6"
julia = "1"

[extras]
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
test = ["ForwardDiff", "Test"]
56 changes: 29 additions & 27 deletions src/DualQuaternion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,43 @@ struct DualQuaternion{T<:Real} <: Number
norm::Bool
end

DualQuaternion(q0::Quaternion, qe::Quaternion, n::Bool = false) =
DualQuaternion(promote(q0, qe)..., n)
DualQuaternion{T}(dq::DualQuaternion) where {T<:Real} = DualQuaternion{T}(dq.q0, dq.qe, dq.norm)
function DualQuaternion{T}(d1::Dual, d2::Dual, d3::Dual, d4::Dual, n::Bool=false) where {T<:Real}
return DualQuaternion{T}(
Quaternion(DualNumbers.value(d1), DualNumbers.value(d2), DualNumbers.value(d3), DualNumbers.value(d4), n),
Quaternion(DualNumbers.epsilon(d1), DualNumbers.epsilon(d2), DualNumbers.epsilon(d3), DualNumbers.epsilon(d4)),
n,
)
end
function DualQuaternion{T}(q0::Quaternion) where {T<:Real}
return DualQuaternion{T}(convert(Quaternion{T}, q0), zero(Quaternion{T}), q0.norm)
end
function DualQuaternion{T}(d::Dual) where {T<:Real}
return DualQuaternion(
Quaternion{T}(DualNumbers.value(d)),
Quaternion{T}(DualNumbers.epsilon(d)),
(DualNumbers.value(d)==one(DualNumbers.value(d))) & iszero(DualNumbers.epsilon(d)))
end
function DualQuaternion{T}(x::Real) where {T<:Real}
return DualQuaternion(convert(Quaternion{T}, x), zero(Quaternion{T}), abs(x) == one(x))
end

DualQuaternion(d1::Dual, d2::Dual, d3::Dual, d4::Dual) =
DualQuaternion(Quaternion(DualNumbers.value(d1), DualNumbers.value(d2), DualNumbers.value(d3), DualNumbers.value(d4)),
Quaternion(DualNumbers.epsilon(d1), DualNumbers.epsilon(d2), DualNumbers.epsilon(d3), DualNumbers.epsilon(d4)))
DualQuaternion(d1::Dual, d2::Dual, d3::Dual, d4::Dual, n::Bool) =
DualQuaternion(q0::Quaternion, qe::Quaternion) = DualQuaternion(promote(q0, qe)..., false)
DualQuaternion(d1::Dual, d2::Dual, d3::Dual, d4::Dual, n::Bool=false) =
DualQuaternion(Quaternion(DualNumbers.value(d1), DualNumbers.value(d2), DualNumbers.value(d3), DualNumbers.value(d4), n),
Quaternion(DualNumbers.epsilon(d1), DualNumbers.epsilon(d2), DualNumbers.epsilon(d3), DualNumbers.epsilon(d4)), n)

DualQuaternion(x::Real) = DualQuaternion(Quaternion(x), Quaternion(zero(x)), abs(x) == one(x))

DualQuaternion(d::Dual) = DualQuaternion(Quaternion(DualNumbers.value(d)), Quaternion(DualNumbers.epsilon(d)), (DualNumbers.value(d)==one(DualNumbers.value(d))) & iszero(DualNumbers.epsilon(d)))

DualQuaternion(q::Quaternion) = DualQuaternion(q, zero(q), q.norm)

DualQuaternion(a::Vector) = DualQuaternion(zero(Quaternion{typeof(a[1])}), Quaternion(a))

const DualQuaternionF16 = DualQuaternion{Float16}
const DualQuaternionF32 = DualQuaternion{Float32}
const DualQuaternionF64 = DualQuaternion{Float64}

convert(::Type{DualQuaternion{T}}, x::Real) where {T} = DualQuaternion(convert(T, x))

convert(::Type{DualQuaternion{T}}, d::Dual) where {T} = DualQuaternion(convert(Dual{T}, d))

convert(::Type{DualQuaternion{T}}, q::Quaternion) where {T} = DualQuaternion(convert(Quaternion{T}, q))

convert(::Type{DualQuaternion{T}}, q::DualQuaternion{T}) where {T <: Real} = q

convert(::Type{DualQuaternion{T}}, dq::DualQuaternion) where {T} =
DualQuaternion(convert(Quaternion{T}, dq.q0), convert(Quaternion{T}, dq.qe), dq.norm)

promote_rule(::Type{DualQuaternion{T}}, ::Type{T}) where {T <: Real} = DualQuaternion{T}
promote_rule(::Type{DualQuaternion}, ::Type{T}) where {T <: Real} = DualQuaternion
promote_rule(::Type{DualQuaternion{T}}, ::Type{S}) where {T <: Real, S <: Real} = DualQuaternion{promote_type(T, S)}
promote_rule(::Type{Quaternion{T}}, ::Type{DualQuaternion{S}}) where {T <: Real, S <: Real} = DualQuaternion{promote_type(T, S)}
promote_rule(::Type{DualQuaternion{T}}, ::Type{Dual{S}}) where {T <: Real, S <: Real} = DualQuaternion{promote_type(T, S)}
promote_rule(::Type{DualQuaternion{T}}, ::Type{Quaternion{S}}) where {T <: Real, S <: Real} = DualQuaternion{promote_type(T, S)}
promote_rule(::Type{DualQuaternion{T}}, ::Type{DualQuaternion{S}}) where {T <: Real, S <: Real} = DualQuaternion{promote_type(T, S)}

dualquat(q1, q2) = DualQuaternion(q1, q2)
Expand Down Expand Up @@ -97,16 +97,16 @@ function normalize(dq::DualQuaternion)
end
end

function normalizea(dq::DualQuaternion)
function normalizea(dq::DualQuaternion{T}) where {T}
if (dq.norm)
return (dq, one(dual))
return (dq, one(Dual{T}))
end
a = abs(dq)
if abs(a) > 0
qa = dq / a
dualquat(qa.q0, qa.qe, true), a
else
dq, zero(dual)
dq, zero(Dual{T})
end
end

Expand All @@ -117,6 +117,8 @@ end
(*)(dq::DualQuaternion, dw::DualQuaternion) = DualQuaternion(dq.q0 * dw.q0,
dq.q0 * dw.qe + dq.qe * dw.q0,
dq.norm && dw.norm)
(*)(dq::DualQuaternion, d::Dual) = (*)(Base.promote(dq, d)...)
(*)(d::Dual, dq::DualQuaternion) = (*)(Base.promote(d, dq)...)
(/)(dq::DualQuaternion, dw::DualQuaternion) = dq * inv(dw)
(==)(q::DualQuaternion, w::DualQuaternion) = (q.q0 == w.q0) & (q.qe == w.qe) # ignore .norm field

Expand Down
32 changes: 18 additions & 14 deletions src/Octonion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ struct Octonion{T<:Real} <: Number
norm::Bool
end

Octonion{T}(x::Real) where {T<:Real} = Octonion(convert(T, x))
Octonion{T}(x::Complex) where {T<:Real} = Octonion(convert(Complex{T}, x))
Octonion{T}(q::Quaternion) where {T<:Real} = Octonion(convert(Quaternion{T}, q))
Octonion{T}(o::Octonion) where {T<:Real} =
Octonion{T}(o.s, o.v1, o.v2, o.v3, o.v4, o.v5, o.v6, o.v7, o.norm)

Octonion(s::Real, v1::Real, v2::Real, v3::Real, v4::Real, v5::Real, v6::Real, v7::Real, n::Bool = false) =
Octonion(promote(s, v1, v2, v3, v4, v5, v6, v7)..., n)
Octonion(x::Real) = Octonion(x, zero(x), zero(x), zero(x), zero(x), zero(x), zero(x), zero(x), abs(x) == one(x))
Expand All @@ -22,18 +28,9 @@ const OctonionF16 = Octonion{Float16}
const OctonionF32 = Octonion{Float32}
const OctonionF64 = Octonion{Float64}

convert(::Type{Octonion{T}}, x::Real) where {T} = Octonion(convert(T, x))
convert(::Type{Octonion{T}}, z::Complex) where {T} = Octonion(convert(Complex{T}, z))
convert(::Type{Octonion{T}}, q::Quaternion) where {T} = Octonion(convert(Quaternion{T}, q))
convert(::Type{Octonion{T}}, o::Octonion{T}) where {T <: Real} = o
convert(::Type{Octonion{T}}, o::Octonion) where {T} =
Octonion(convert(T, o.s), convert(T, o.v1), convert(T, o.v2), convert(T, o.v3), convert(T, o.v4), convert(T, o.v5), convert(T, o.v6), convert(T, o.v7), o.norm)

promote_rule(::Type{Octonion{T}}, ::Type{T}) where {T <: Real} = Octonion{T}
promote_rule(::Type{Octonion}, ::Type{T}) where {T <: Real} = Octonion
promote_rule(::Type{Octonion{T}}, ::Type{S}) where {T <: Real, S <: Real} = Octonion{promote_type(T, S)}
promote_rule(::Type{Complex{T}}, ::Type{Octonion{S}}) where {T <: Real, S <: Real} = Octonion{promote_type(T, S)}
promote_rule(::Type{Quaternion{T}}, ::Type{Octonion{S}}) where {T <: Real, S <: Real} = Octonion{promote_type(T, S)}
promote_rule(::Type{Octonion{T}}, ::Type{Complex{S}}) where {T <: Real, S <: Real} = Octonion{promote_type(T, S)}
promote_rule(::Type{Octonion{T}}, ::Type{Quaternion{S}}) where {T <: Real, S <: Real} = Octonion{promote_type(T, S)}
promote_rule(::Type{Octonion{T}}, ::Type{Octonion{S}}) where {T <: Real, S <: Real} = Octonion{promote_type(T, S)}

octo(p, v1, v2, v3, v4, v5, v6, v7) = Octonion(p, v1, v2, v3, v4, v5, v6, v7)
Expand All @@ -54,9 +51,16 @@ imag(o::Octonion) = [o.v1, o.v2, o.v3, o.v4, o.v5, o.v6, o.v7]
conj(o::Octonion) = Octonion(o.s, -o.v1, -o.v2, -o.v3, -o.v4, -o.v5, -o.v6, -o.v7, o.norm)
abs(o::Octonion) = sqrt(o.s * o.s + o.v1 * o.v1 + o.v2 * o.v2 + o.v3 * o.v3 + o.v4 * o.v4 + o.v5 * o.v5 + o.v6 * o.v6 + o.v7 * o.v7)
float(q::Octonion{T}) where T = convert(Octonion{float(T)}, q)
abs_imag(o::Octonion) = sqrt(o.v1 * o.v1 + o.v2 * o.v2 + o.v3 * o.v3 + o.v4 * o.v4 + o.v5 * o.v5 + o.v6 * o.v6 + o.v7 * o.v7)
abs2(o::Octonion) = o.s * o.s + o.v1 * o.v1 + o.v2 * o.v2 + o.v3 * o.v3 + o.v4 * o.v4 + o.v5 * o.v5 + o.v6 * o.v6 + o.v7 * o.v7
inv(o::Octonion) = o.norm ? conj(o) : conj(o) / abs2(o)

isreal(o::Octonion) = iszero(o.v1) & iszero(o.v2) & iszero(o.v3) & iszero(o.v4) & iszero(o.v5) & iszero(o.v6) & iszero(o.v7)
isfinite(o::Octonion) = o.norm | (isfinite(real(o)) & isfinite(o.v1) & isfinite(o.v2) & isfinite(o.v3) & isfinite(o.v4) & isfinite(o.v5) & isfinite(o.v6) & isfinite(o.v7))
iszero(o::Octonion) = ~o.norm & iszero(real(o)) & iszero(o.v1) & iszero(o.v2) & iszero(o.v3) & iszero(o.v4) & iszero(o.v5) & iszero(o.v6) & iszero(o.v7)
isnan(o::Octonion) = isnan(real(o)) | isnan(o.v1) | isnan(o.v2) | isnan(o.v3) | isnan(o.v4) | isnan(o.v5) | isnan(o.v6) | isnan(o.v7)
isinf(o::Octonion) = ~o.norm & (isinf(real(o)) | isinf(o.v1) | isinf(o.v2) | isinf(o.v3) | isinf(o.v4) | isinf(o.v5) | isinf(o.v6) | isinf(o.v7))

function normalize(o::Octonion)
if (o.norm)
return o
Expand Down Expand Up @@ -122,7 +126,7 @@ function exp(o::Octonion)
s = o.s
se = exp(s)
scale = se
th = abs(Octonion(imag(o)))
th = abs_imag(o)
if th > 0
scale *= sin(th) / th
end
Expand All @@ -140,7 +144,7 @@ end
function log(o::Octonion)
o, a = normalizea(o)
s = o.s
M = abs(Octonion(imag(o)))
M = abs_imag(o)
th = atan(M, s)
if M > 0
M = th / M
Expand All @@ -153,7 +157,7 @@ function log(o::Octonion)
o.v6 * M,
o.v7 * M)
else
return Octonion(log(a), th, 0.0, 0.0)
return Octonion(complex(log(a), ifelse(iszero(a), zero(th), th)))
end
end

Expand Down
51 changes: 15 additions & 36 deletions src/Quaternion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,18 @@ const QuaternionF16 = Quaternion{Float16}
const QuaternionF32 = Quaternion{Float32}
const QuaternionF64 = Quaternion{Float64}

(::Type{Quaternion{T}})(x::Real) where {T<:Real} = Quaternion(convert(T, x))
(::Type{Quaternion{T}})(q::Quaternion{T}) where {T<:Real} = q
(::Type{Quaternion{T}})(q::Quaternion) where {T<:Real} = Quaternion{T}(q.s, q.v1, q.v2, q.v3, q.norm)
Quaternion{T}(x::Real) where {T<:Real} = Quaternion(convert(T, x))
Quaternion{T}(x::Complex) where {T<:Real} = Quaternion(convert(Complex{T}, x))
Quaternion{T}(q::Quaternion) where {T<:Real} = Quaternion{T}(q.s, q.v1, q.v2, q.v3, q.norm)
Quaternion(s::Real, v1::Real, v2::Real, v3::Real, n::Bool = false) =
Quaternion(promote(s, v1, v2, v3)..., n)
Quaternion(x::Real) = Quaternion(x, zero(x), zero(x), zero(x), abs(x) == one(x))
Quaternion(z::Complex) = Quaternion(z.re, z.im, zero(z.re), zero(z.re), abs(z) == one(z.re))
Quaternion(s::Real, a::AbstractVector) = Quaternion(s, a[1], a[2], a[3])
Quaternion(a::AbstractVector) = Quaternion(0, a[1], a[2], a[3])

convert(::Type{Quaternion{T}}, x::Real) where {T} = Quaternion(convert(T, x))
convert(::Type{Quaternion{T}}, z::Complex) where {T} = Quaternion(convert(Complex{T}, z))
convert(::Type{Quaternion{T}}, q::Quaternion{T}) where {T <: Real} = q
convert(::Type{Quaternion{T}}, q::Quaternion) where {T} =
Quaternion(convert(T, q.s), convert(T, q.v1), convert(T, q.v2), convert(T, q.v3), q.norm)

promote_rule(::Type{Quaternion{T}}, ::Type{T}) where {T <: Real} = Quaternion{T}
promote_rule(::Type{Quaternion}, ::Type{T}) where {T <: Real} = Quaternion
promote_rule(::Type{Quaternion{T}}, ::Type{S}) where {T <: Real, S <: Real} = Quaternion{promote_type(T, S)}
promote_rule(::Type{Complex{T}}, ::Type{Quaternion{S}}) where {T <: Real, S <: Real} = Quaternion{promote_type(T, S)}
promote_rule(::Type{Quaternion{T}}, ::Type{Complex{S}}) where {T <: Real, S <: Real} = Quaternion{promote_type(T, S)}
promote_rule(::Type{Quaternion{T}}, ::Type{Quaternion{S}}) where {T <: Real, S <: Real} = Quaternion{promote_type(T, S)}

quat(p, v1, v2, v3) = Quaternion(p, v1, v2, v3)
Expand Down Expand Up @@ -240,32 +232,19 @@ function linpol(p::Quaternion, q::Quaternion, t::Real)
q = qm
end
c = p.s * q.s + p.v1 * q.v1 + p.v2 * q.v2 + p.v3 * q.v3
if c > - 1.0
if c < 1.0
o = acos(c)
s = sin(o)
sp = sin((1 - t) * o) / s
sq = sin(t * o) / s
else
sp = 1 - t
sq = t
end
Quaternion(sp * p.s + sq * q.s,
sp * p.v1 + sq * q.v1,
sp * p.v2 + sq * q.v2,
sp * p.v3 + sq * q.v3, true)
if c < 1.0
o = acos(c)
s = sin(o)
sp = sin((1 - t) * o) / s
sq = sin(t * o) / s
else
s = p.v3
v1 = -p.v2
v2 = p.v1
v3 = -p.s
sp = sin((0.5 - t) * pi)
sq = sin(t * pi)
Quaternion(s,
sp * p.v1 + sq * v1,
sp * p.v2 + sq * v2,
sp * p.v3 + sq * v3, true)
sp = 1 - t
sq = t
end
Quaternion(sp * p.s + sq * q.s,
sp * p.v1 + sq * q.v1,
sp * p.v2 + sq * q.v2,
sp * p.v3 + sq * q.v3, true)
end

quatrand(rng = Random.GLOBAL_RNG) = quat(randn(rng), randn(rng), randn(rng), randn(rng))
Expand Down
1 change: 1 addition & 0 deletions src/Quaternions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Quaternions
import Base: convert, promote_rule, float
import Base: rand, randn
import LinearAlgebra: lyap, norm, normalize, sylvester
using LinearAlgebra: cross, dot
using Random

Base.@irrational INV_SQRT_EIGHT 0.3535533905932737622004 sqrt(big(0.125))
Expand Down
Loading