From 5eae0fee84eeaa8f833cbf0036adda1dd6a87cfd Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Mon, 14 Oct 2024 17:58:09 -0500 Subject: [PATCH 1/9] Added package extension for Unitful.jl --- Project.toml | 7 +++++++ ext/CliffordNumbersUnitfulExt.jl | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 ext/CliffordNumbersUnitfulExt.jl diff --git a/Project.toml b/Project.toml index 7dcb3f6..b075153 100644 --- a/Project.toml +++ b/Project.toml @@ -3,9 +3,16 @@ uuid = "3998ac73-6bd4-4031-8035-f167dd3ed523" authors = ["Brandon Flores"] version = "0.1.9-dev" +[weakdeps] +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[extensions] +CliffordNumbersUnitfulExt = "Unitful" + [compat] Aqua = "0.8" Test = "1" +Unitful = "1" julia = "1.8" [extras] diff --git a/ext/CliffordNumbersUnitfulExt.jl b/ext/CliffordNumbersUnitfulExt.jl new file mode 100644 index 0000000..7901247 --- /dev/null +++ b/ext/CliffordNumbersUnitfulExt.jl @@ -0,0 +1,7 @@ +module CliffordNumbersUnitfulExt + +using CliffordNumbers +using Unitful +import CliffordNumbers: ∧ + +end From ed9529fc56f6c772f7f37aefcd6c35349d8b207f Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Mon, 14 Oct 2024 18:03:17 -0500 Subject: [PATCH 2/9] Added wedge product definitions --- ext/CliffordNumbersUnitfulExt.jl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ext/CliffordNumbersUnitfulExt.jl b/ext/CliffordNumbersUnitfulExt.jl index 7901247..bc24248 100644 --- a/ext/CliffordNumbersUnitfulExt.jl +++ b/ext/CliffordNumbersUnitfulExt.jl @@ -2,6 +2,22 @@ module CliffordNumbersUnitfulExt using CliffordNumbers using Unitful -import CliffordNumbers: ∧ + +import CliffordNumbers: ∧, ∨, ⨼, ⨽, dot, ×, ⨰ +import Unitful: AbstractQuantity, AffineError, AffineQuantity + +∧(x::AbstractQuantity, y::AbstractQuantity) = Quantity(x.val ∧ y.val, unit(x) * unit(y)) + +function ∧(x::Number, y::AbstractQuantity) + y isa AffineQuantity && + throw(AffineError("an invalid operation was attempted with affine quantities: $x ∧ $y")) + return Quantity(x ∧ y.val, unit(y)) +end + +function ∧(x::AbstractQuantity, y::Number) + x isa AffineQuantity && + throw(AffineError("an invalid operation was attempted with affine quantities: $x ∧ $y")) + return Quantity(x.val ∧ y, unit(x)) +end end From 22172a8d91cae09f80f8a10e6156011dd4089998 Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Thu, 17 Oct 2024 18:43:53 -0500 Subject: [PATCH 3/9] Fixed printing methods for `AbstractQuantity{<:AbstractCliffordNumber}` --- ext/CliffordNumbersUnitfulExt.jl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ext/CliffordNumbersUnitfulExt.jl b/ext/CliffordNumbersUnitfulExt.jl index bc24248..d172a96 100644 --- a/ext/CliffordNumbersUnitfulExt.jl +++ b/ext/CliffordNumbersUnitfulExt.jl @@ -6,6 +6,27 @@ using Unitful import CliffordNumbers: ∧, ∨, ⨼, ⨽, dot, ×, ⨰ import Unitful: AbstractQuantity, AffineError, AffineQuantity +#---Fixing printing methods------------------------------------------------------------------------# + +# Probably not needed, but included anyway +Unitful.BracketStyle(::Type{<:AbstractCliffordNumber}) = Unitful.RoundBrackets() + +function Base.print(io::IO, x::AbstractQuantity{<:AbstractCliffordNumber}) + # Functions with qualified names are part of the internal Unitful API. + # This should be fine for now, but may break unexpectedly. + if unit(x) isa Unitful.Units{()} + print(io, x.val, " (no units)") + else + print(io, '(', x.val, ')') + # This one is based on SI convention and should not change. I hope. + Unitful.has_unit_spacing(unit(x)) && print(io, ' ') + print(io, unit(x)) + end +end + +Base.show(io::IO, ::MIME"text/plain", x::Quantity{<:AbstractCliffordNumber}) = print(io, x) + +#---Definitions for different products-------------------------------------------------------------# ∧(x::AbstractQuantity, y::AbstractQuantity) = Quantity(x.val ∧ y.val, unit(x) * unit(y)) function ∧(x::Number, y::AbstractQuantity) From d737790fc05e78dcfdf2f2d711d1aff5a8e824dc Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Thu, 17 Oct 2024 19:07:39 -0500 Subject: [PATCH 4/9] Added tests for package extension --- Project.toml | 3 ++- test/ext/Unitful.jl | 9 +++++++++ test/runtests.jl | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 test/ext/Unitful.jl diff --git a/Project.toml b/Project.toml index b075153..bf38a10 100644 --- a/Project.toml +++ b/Project.toml @@ -18,6 +18,7 @@ julia = "1.8" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [targets] -test = ["Aqua", "Test"] +test = ["Aqua", "Test", "Unitful"] diff --git a/test/ext/Unitful.jl b/test/ext/Unitful.jl new file mode 100644 index 0000000..c4d3a5d --- /dev/null +++ b/test/ext/Unitful.jl @@ -0,0 +1,9 @@ +@testset "Unitful extension" begin + x = KVector{1,VGA(3)}(1, 0, 0) + y = KVector{1,VGA(3)}(0, 1, 0) + xu = x*u"m" + yu = y*u"m" + @test xu ∧ yu === KVector{2,VGA(3)}(1, 0, 0) * u"m^2" + @test_throws Unitful.AffineError 2 ∧ 2u"°F" + @test_throws Unitful.AffineError 2u"°F" ∧ 2 +end diff --git a/test/runtests.jl b/test/runtests.jl index a8eebd2..cf29c5e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,5 @@ using CliffordNumbers -using Aqua, Test +using Aqua, Test, Unitful Aqua.test_all(CliffordNumbers; unbound_args = false) @@ -15,4 +15,5 @@ Aqua.test_all(CliffordNumbers; unbound_args = false) include("indexing.jl") include("conversion.jl") include("operations.jl") + include("ext/Unitful.jl") end From f4c55136ee9dc02d98fcedd7c7ab2ba5b0c1ec5f Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Thu, 17 Oct 2024 19:16:44 -0500 Subject: [PATCH 5/9] Added package extension information to changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f859512..182f83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added + - A package extension for interoperability with [Unitful.jl] (only supported on julia 1.9 and up). + It provides support for wedge products for `Quantity`, including `Quantity{<:Real}` and + `Quantity{<:AbstractCliffordNumber}` (geometric products were already supported). - `nonzero_grades(::Complex)` is defined and returns `0:0`, like `nonzero_grades(::Real)`. - `print(::IO, ::AbstractCliffordNumber)` shows a prettier (but not parseable) representation. @@ -128,3 +131,4 @@ Initial release of CliffordNumbers.jl [0.1.2]: https://github.com/brainandforce/CliffordNumbers.jl/releases/tag/v0.1.2 [0.1.1]: https://github.com/brainandforce/CliffordNumbers.jl/releases/tag/v0.1.1 [0.1.0]: https://github.com/brainandforce/CliffordNumbers.jl/releases/tag/v0.1.0 +[Unitful.jl]: https://github.com/PainterQubits/Unitful.jl From b893ccca3f561d947f7066c478cb0313dcd43577 Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Thu, 17 Oct 2024 20:20:16 -0500 Subject: [PATCH 6/9] Throw errors for invalid construction of unitful Clifford numbers --- ext/CliffordNumbersUnitfulExt.jl | 30 ++++++++++++++++++++++++++++++ test/ext/Unitful.jl | 2 ++ 2 files changed, 32 insertions(+) diff --git a/ext/CliffordNumbersUnitfulExt.jl b/ext/CliffordNumbersUnitfulExt.jl index d172a96..7ef486e 100644 --- a/ext/CliffordNumbersUnitfulExt.jl +++ b/ext/CliffordNumbersUnitfulExt.jl @@ -6,6 +6,36 @@ using Unitful import CliffordNumbers: ∧, ∨, ⨼, ⨽, dot, ×, ⨰ import Unitful: AbstractQuantity, AffineError, AffineQuantity +#---Constructors-----------------------------------------------------------------------------------# + +# Error thrown when units are incommensurate +function (::Type{<:AbstractCliffordNumber})(::Tuple{Vararg{Quantity}}) + throw( + ArgumentError( + "\n$(Quantity) does not subtype $(Real) or $(Complex), so it cannot be used as the " * + "scalar type for $(AbstractCliffordNumber) instances.\n" * + "Additionally, mixed units are disallowed because $(AbstractCliffordNumber) " * + "currently assumes an orthonormal basis." + ) + ) +end + +# Error thrown when units are commensurate +function (::Type{C})(x::Tuple{Vararg{Quantity{<:Number,D,U}}}) where {C<:AbstractCliffordNumber,D,U} + unit_string = "u\"" * replace(repr(unit(first(x))), " " => " * ") * "\"" + multivector_string = "$C$(ustrip.(x))" + throw( + ArgumentError( + "\n$(Quantity) does not subtype $(Real) or $(Complex), so it cannot be used as the " * + "scalar type for $(AbstractCliffordNumber) instances.\n" * + "Instead, construct a $(Quantity{<:C}) by multiplying a $C by a unit:\n" * + "\n " * multivector_string * unit_string * "\n" + ) + ) +end + +(::Type{C})(x::Quantity...) where C<:AbstractCliffordNumber = C(x) + #---Fixing printing methods------------------------------------------------------------------------# # Probably not needed, but included anyway diff --git a/test/ext/Unitful.jl b/test/ext/Unitful.jl index c4d3a5d..736c85a 100644 --- a/test/ext/Unitful.jl +++ b/test/ext/Unitful.jl @@ -1,6 +1,8 @@ @testset "Unitful extension" begin x = KVector{1,VGA(3)}(1, 0, 0) y = KVector{1,VGA(3)}(0, 1, 0) + @test_throws ArgumentError KVector{1,VGA(3)}(1u"m", 0u"m", 0u"m") + @test_throws ArgumentError KVector{1,VGA(3)}(0u"m", 1u"m/s", 0u"K") xu = x*u"m" yu = y*u"m" @test xu ∧ yu === KVector{2,VGA(3)}(1, 0, 0) * u"m^2" From 4f3d2109041cfaead93c5576aba95cc4ea85a660 Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Thu, 17 Oct 2024 20:34:08 -0500 Subject: [PATCH 7/9] Added basic docs for Unitful extension --- docs/make.jl | 1 + docs/src/extensions.md | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 docs/src/extensions.md diff --git a/docs/make.jl b/docs/make.jl index b527475..1461000 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -26,6 +26,7 @@ makedocs(; "Indexing" => "indexing.md", "Operations" => "operations.md", "Performance tips" => "performance.md", + "Extensions" => "extensions.md", "API" => Any[ "CliffordNumbers" => "api/clifford.md", "Indexing" => "api/indexing.md", diff --git a/docs/src/extensions.md b/docs/src/extensions.md new file mode 100644 index 0000000..7366bad --- /dev/null +++ b/docs/src/extensions.md @@ -0,0 +1,37 @@ +# Extensions + +CliffordNumbers.jl provides some extensions to allow for interoperability with other packages which +we anticipate to be commonly used alongside it. + +!!! note + Extensions require Julia 1.9; support for previous versions will be dropped with the 0.2.0 + release of CliffordNumbers.jl. + +## [Unitful.jl] + +[Unitful.jl] provides support for quantities with associated units. + +To make a Clifford number into a unitful quantity, simply multiply it by a unit or other unitful +quantity: +```julia-repl +julia> KVector{1, VGA(3)}(1, 2, 3)u"m" +(1e₁ + 2e₂ + 3e₃) m + +julia> KVector{1, VGA(3)}(1, 2, 3) * 1.5u"m" +(1.5e₁ + 3.0e₂ + 4.5e₃) m +``` + +!!! warn "Constructing Clifford numbers from `Quantity` coefficients fails" + Attempts to construct a Clifford number from `Quantity` objects will fail, because `Quantity` + does not subtype `Real` or `Complex`: + ```julia-repl + julia> KVector{1,VGA(3)}(1u"m", 2u"m", 3u"m") + ERROR: ArgumentError: + ... + ``` + For this reason, Clifford numbers with mixed units cannot be constructed, and this is + intentional: `AbstractCliffordNumber` assumes an orthonormal basis. + +Supported operations include the geometric product and wedge product. + +[Unitful.jl]: https://github.com/PainterQubits/Unitful.jl From 4e89b66bc215d13a773e79bd699237326d37533b Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Sun, 20 Oct 2024 17:58:41 -0500 Subject: [PATCH 8/9] Don't attempt to test extension if Julia < v1.9.0 --- test/runtests.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index cf29c5e..ea8f88b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,5 +15,7 @@ Aqua.test_all(CliffordNumbers; unbound_args = false) include("indexing.jl") include("conversion.jl") include("operations.jl") - include("ext/Unitful.jl") + if VERSION >= v"1.9.0" + include("ext/Unitful.jl") + end end From fd93a9c257da58180c1047eadcd22b783b131ac6 Mon Sep 17 00:00:00 2001 From: Brandon Flores Date: Sun, 20 Oct 2024 18:19:37 -0500 Subject: [PATCH 9/9] Added some missing tests --- test/ext/Unitful.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/ext/Unitful.jl b/test/ext/Unitful.jl index 736c85a..cb75648 100644 --- a/test/ext/Unitful.jl +++ b/test/ext/Unitful.jl @@ -6,6 +6,15 @@ xu = x*u"m" yu = y*u"m" @test xu ∧ yu === KVector{2,VGA(3)}(1, 0, 0) * u"m^2" + @test 2 ∧ xu === KVector{1,VGA(3)}(2, 0, 0) * u"m" + @test yu ∧ 2 === KVector{1,VGA(3)}(0, 2, 0) * u"m" @test_throws Unitful.AffineError 2 ∧ 2u"°F" @test_throws Unitful.AffineError 2u"°F" ∧ 2 + # Printing + @test Unitful.BracketStyle(x) === Unitful.RoundBrackets() + @test string(xu) == "(1e₁) m" + @test string(xu ∧ yu) == "(1e₁e₂) m^2" + unitless = Quantity{KVector{1,VGA(3),Int,3}, NoDims, Unitful.FreeUnits{(), NoDims, nothing}}(y) + @test string(unitless) == "1e₂ (no units)" + @test repr("text/plain", unitless) == string(unitless) end