A fast, lightweight and easy-to-use Julia package for Clifford Algebras and Geometric Algebras.
Contributions welcome—see CONTRIBUTING.md. See CHANGELOG.md for notable changes. Many examples in the docs are doctested.
If you're contributing, see the Developer Guide in the docs: https://atell-soundtheory.github.io/CliffordAlgebras.jl/dev/developer/
CliffordAlgebras.jl uses compile time code generation, sparse multivector representation and special case identification to handle arbitrary geometric algebras efficiently. Lazy evaluation of expressions is not yet supported.
CliffordAlgebras provides a low level implementation that is common to all Clifford algebras. Functions that are specific to certain algebras can be added on top using the tools provided here.
Optional integrations: PrettyTables is supported via a package extension. If PrettyTables is present in your environment, cayleytable and signaturetable use it for rendering; otherwise, a Unicode fallback renderer is used with the same layout tested in CI.
Install via the Julia package manager:
import Pkg
Pkg.add(url="https://github.com/ATell-SoundTheory/CliffordAlgebras.jl")Or develop locally during experimentation:
import Pkg
Pkg.develop(path="/absolute/path/to/CliffordAlgebras.jl")Minimum Julia version: 1.6
PrettyTables extension (optional):
import Pkg
Pkg.add("PrettyTables") # enables PrettyTables-backed table rendering via extensionGenerate a Clifford algebra:
julia> cl2 = CliffordAlgebra(2)
Cl(2,0,0)
julia> cayleytable(stdout, cl2)
┌───────┬──────────────┬───────┐
│ +1 │ +e1 +e2 │ +e1e2 │
├───────┼──────────────┼───────┤
│ +e1 │ +1 +e1e2 │ +e2 │
│ +e2 │ -e1e2 +1 │ -e1 │
├───────┼──────────────┼───────┤
│ +e1e2 │ -e2 +e1 │ -1 │
└───────┴──────────────┴───────┘
julia> cl101 = CliffordAlgebra(1,0,1)
Cl(1,0,1)
julia> cayleytable(stdout, cl101)
┌───────┬──────────────┬───────┐
│ +1 │ +e1 +e2 │ +e1e2 │
├───────┼──────────────┼───────┤
│ +e1 │ +1 +e1e2 │ +e2 │
│ +e2 │ -e1e2 0 │ 0 │
├───────┼──────────────┼───────┤
│ +e1e2 │ -e2 0 │ 0 │
└───────┴──────────────┴───────┘
julia> sta = CliffordAlgebra(:Spacetime)
Cl(1,3,0)
julia> signaturetable(stdout, sta)
┌───┬────┐
│ t │ +1 │
│ x │ -1 │
│ y │ -1 │
│ z │ -1 │
└───┴────┘
julia> cayleytable(stdout, sta)
┌───────┬────────────────────────────┬──────────────────────────────────────────┬────────────────────────────┬───────┐
│ +1 │ +t +x +y +z │ +tx +ty +xy +tz +zx +yz │ +tyx +txz +tzy +xyz │ +txyz │
├───────┼────────────────────────────┼──────────────────────────────────────────┼────────────────────────────┼───────┤
│ +t │ +1 +tx +ty +tz │ +x +y -tyx +z -txz -tzy │ -xy -zx -yz +txyz │ +xyz │
│ +x │ -tx -1 +xy -zx │ +t +tyx -y -txz +z +xyz │ -ty +tz +txyz -yz │ -tzy │
│ +y │ -ty -xy -1 +yz │ -tyx +t +x +tzy +xyz -z │ +tx +txyz -tz -zx │ -txz │
│ +z │ -tz +zx -yz -1 │ +txz -tzy +xyz +t -x +y │ +txyz -tx +ty -xy │ -tyx │
├───────┼────────────────────────────┼──────────────────────────────────────────┼────────────────────────────┼───────┤
│ +tx │ -x -t -tyx +txz │ +1 -xy -ty +zx +tz +txyz │ -y +z +xyz +tzy │ +yz │
│ +ty │ -y +tyx -t -tzy │ +xy +1 +tx -yz +txyz -tz │ +x +xyz -z +txz │ +zx │
│ +xy │ -tyx +y -x +xyz │ +ty -tx -1 +txyz -yz +zx │ +t -tzy +txz -z │ -tz │
│ +tz │ -z -txz +tzy -t │ -zx +yz +txyz +1 -tx +ty │ +xyz -x +y +tyx │ +xy │
│ +zx │ -txz -z +xyz +x │ -tz +txyz +yz +tx -1 -xy │ +tzy +t -tyx -y │ -ty │
│ +yz │ -tzy +xyz +z -y │ +txyz +tz -zx -ty +xy -1 │ -txz +tyx +t -x │ -tx │
├───────┼────────────────────────────┼──────────────────────────────────────────┼────────────────────────────┼───────┤
│ +tyx │ -xy -ty +tx -txyz │ -y +x +t -xyz -tzy +txz │ -1 -yz +zx +tz │ +z │
│ +txz │ -zx +tz -txyz -tx │ +z -xyz +tzy -x +t -tyx │ +yz -1 -xy +ty │ +y │
│ +tzy │ -yz -txyz -tz +ty │ -xyz -z -txz +y +tyx +t │ -zx +xy -1 +tx │ +x │
│ +xyz │ -txyz -yz -zx -xy │ -tzy -txz -z -tyx -y -x │ -tz -ty -tx +1 │ -t │
├───────┼────────────────────────────┼──────────────────────────────────────────┼────────────────────────────┼───────┤
│ +txyz │ -xyz +tzy +txz +tyx │ +yz +zx -tz +xy -ty -tx │ -z -y -x +t │ -1 │
└───────┴────────────────────────────┴──────────────────────────────────────────┴────────────────────────────┴───────┘
Get the basis vector names from the generated algebra and use the base vectors to construct multivectors:
julia> propertynames(cl2)
(:𝟏, :e1, :e2, :e1e2)
julia> mv1 = 2.0 * cl2.e1+ cl2.e1e2 + 1
+1.0+2.0×e1+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> mv2 = mv1 - cl2.e1e2
+1.0+2.0×e1 ∈ Cl(2, 0, 0)
Use the geometric product and derived products with the multivectors:
julia> mv1 * mv2
+5.0+4.0×e1-2.0×e2+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> mv1 ∧ mv2
+1.0+4.0×e1+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> mv1 ∨ mv2
+1.0+2.0×e1 ∈ Cl(2, 0, 0)
julia> mv1 ⋅ mv2
+5.0+4.0×e1-2.0×e2+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> mv1 ⋆ mv2
+5.0 ∈ Cl(2, 0, 0)
julia> mv1 ⨼ mv2
+5.0+2.0×e1 ∈ Cl(2, 0, 0)
julia> mv1 ⨽ mv2
+5.0+2.0×e1-2.0×e2+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> mv1 ×₋ mv2
-2.0×e2 ∈ Cl(2, 0, 0)
julia> mv1 ×₊ mv2
+5.0+4.0×e1+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> mv1 ≀ mv2
+14.0+12.0×e1-8.0×e2 ∈ Cl(2, 0, 0)
Calculate the inverse of a multivector and divide multivectors:
julia> inv(mv2)
-0.33333333333333326+0.6666666666666665×e1 ∈ Cl(2, 0, 0)
julia> mv1 / mv2
+0.9999999999999998-0.6666666666666665×e2-0.33333333333333326×e1e2 ∈ Cl(2, 0, 0)
julia> mv2 \ mv1
+0.9999999999999998+0.6666666666666665×e2-0.33333333333333326×e1e2 ∈ Cl(2, 0, 0)
Calculate duals and involutions:
julia> dual(mv1)
+1.0+2.0×e2+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> grin(mv1)
+1.0-2.0×e1+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> conj(mv1)
+1.0-2.0×e1-1.0×e1e2 ∈ Cl(2, 0, 0)
julia> reverse(mv1)
+1.0+2.0×e1-1.0×e1e2 ∈ Cl(2, 0, 0)
julia> ~mv1
+1.0+2.0×e1-1.0×e1e2 ∈ Cl(2, 0, 0)
julia> polarize(mv1)
-1.0+2.0×e2+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> mv1'
-1.0+2.0×e2+1.0×e1e2 ∈ Cl(2, 0, 0)
Extract subspaces:
julia> scalar(mv1)
1.0
julia> even(mv1)
+1.0+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> odd(mv1)
+2.0×e1 ∈ Cl(2, 0, 0)
julia> grade(mv1,2)
+1.0×e1e2 ∈ Cl(2, 0, 0)
julia> Λᵏ(mv1,2)
+1.0×e1e2 ∈ Cl(2, 0, 0)
Calculate the norm of a multivector, evaluate the exponential function and use the sandwich product:
julia> R = cl2.e1e2 * π/8
+0.39269908169872414×e1e2 ∈ Cl(2, 0, 0)
julia> norm(R)
0.39269908169872414
julia> exp(R)
+0.9238795325112867+0.38268343236508984×e1e2 ∈ Cl(2, 0, 0)
julia> exp(R) ≀ cl2.e1
+0.7071067811865475×e1-0.7071067811865477×e2 ∈ Cl(2, 0, 0)
Map multivectors to their matrix algebra counterparts:
julia> vector(mv1)
4-element Vector{Float64}:
1.0
2.0
0.0
1.0
julia> matrix(mv1)
4×4 Matrix{Float64}:
1.0 2.0 0.0 -1.0
2.0 1.0 1.0 0.0
0.0 -1.0 1.0 2.0
1.0 0.0 2.0 1.0
julia> matrix(mv1)*matrix(mv2) == matrix(mv1*mv2)
true
julia> matrix(mv2)*vector(mv1) == vector(mv2*mv1)
true
Apply outermorphisms:
julia> M = [0 1 ; 1 0]
2×2 Matrix{Int64}:
0 1
1 0
julia> outermorphism(M, mv1)
+1.0+2.0×e2-1.0×e1e2 ∈ Cl(2, 0, 0)
This package exports several Unicode operators like ∧ ∨ ⋅ ⨼ ⨽ ⋆ ×₋ ×₊ ≀. In the Julia REPL and most editors, you can type them using LaTeX-style tab completions, for example:
\wedge<Tab>→∧\vee<Tab>→∨\cdot<Tab>→⋅\lrcorner<Tab>→⨼(left contraction)\llcorner<Tab>→⨽(right contraction)\star<Tab>→⋆\times<Tab>\_ -<Tab>→×₋(commutator)\times<Tab>\_ +<Tab>→×₊(anti-commutator)\Vert<Tab>→≀(sandwich)
As a non-Unicode fallback, you can call the corresponding functions directly:
exteriorprod(a,b)fora ∧ bfatdotprod(a,b)fora ⋅ bleftcontractionprod(a,b)fora ⨼ brightcontractionprod(a,b)fora ⨽ bscalarprod(a,b)fora ⋆ bcommutatorprod(a,b)fora ×₋ banticommutatorprod(a,b)fora ×₊ bsandwichproduct(a,b)fora ≀ b
This repo includes a small benchmark suite in benchmark/.
- Quick run:
- julia --project=benchmark benchmark/run.jl
- Full comparison with PkgBenchmark:
- julia --project=benchmark -e 'using PkgBenchmark, CliffordAlgebras; benchmarkpkg(CliffordAlgebras)'
See benchmark/README.md for details.
Additional docs: