diff --git a/README.md b/README.md index 579fefa..7e82794 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://jollywatt.github.io/GeometricAlgebra.jl/dev/) ![Build Status](https://github.com/Jollywatt/GeometricAlgebra.jl/actions/workflows/CI.yml/badge.svg) [![Coverage](https://codecov.io/gh/jollywatt/GeometricAlgebra.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/jollywatt/GeometricAlgebra.jl) -![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/jollywatt/GeometricAlgebra.jl) [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) +![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/jollywatt/GeometricAlgebra.jl) A Julia package for working with multivectors from geometric (or Clifford) algebra. diff --git a/src/generated.jl b/src/generated.jl index cba927d..03f6915 100644 --- a/src/generated.jl +++ b/src/generated.jl @@ -16,6 +16,9 @@ use_symbolic_optim(sig) = dimension(sig) <= 8 Create an array of symbolic values of the specified shape. +See also [`make_symbolic`](@ref). + + # Example ```jldoctest julia> GeometricAlgebra.symbolic_components(:a, 2, 3) @@ -33,6 +36,23 @@ function symbolic_components(label::Symbol, dims::Integer...) Any[SymbolicUtils.Term{Real}(getindex, [var, I...]) for I in indices] end +""" + make_symbolic(a, label) + +Multivector with symbolic components of the same type as the `Multivector` instance or type `a`. + +See also [`symbolic_components`](@ref). + +# Example + +```jldoctest +julia> GeometricAlgebra.make_symbolic(Multivector{3,1}, :A) +3-component Multivector{3, 1, Vector{Any}}: + A[1] v1 + A[2] v2 + A[3] v3 +``` +""" make_symbolic(::OrType{<:Multivector{Sig,K}}, label) where {Sig,K} = Multivector{Sig,K}(symbolic_components(label, ncomponents(Multivector{Sig,K}))) make_symbolic(::OrType{F}, label) where {F<:Function} = F.instance make_symbolic(::OrType{Val{V}}, label) where {V} = Val(V) @@ -218,28 +238,34 @@ end Evaluate a symbolically optimised geometric algebra expression. -On macro expansion, `expr` is evaluated with symbolic multivectors (specified by `mv_grades`) +Upon macro expansion, `expr` is evaluated with symbolic multivectors (specified by `mv_grades`) in the algebra defined by metric signature `sig`. The resulting symbolic expression -is then compiled and executed at runtime. If `result_type` is given, the components array -of the resulting `Multivector` is returned and converted to that type. +is then compiled and executed at runtime. + +The `mv_grades` argument is a `NamedTuple` where `keys(mv_grades)` +defines the identifiers in `expr` to be interpreted as `Multivector`s, +while `values(mv_grades)` defines their respective grades. +The identifiers must exist at runtime, and can be a `Multivector` with matching +signature/grade or any iterable with the correct number of components. -The `grades` argument is a `NamedTuple` where `keys(grades)` defines the -identifiers in `expr` to be interpreted as `Multivector`s and `values(grades)` -defines their respective grades. -The identifiers must exist at runtime, and can be a `Multivector` of matching -signature and grade or any iterable with the correct number of components. +If `result_type` is given, then the components of the resulting multivector +are converted to that type. The result type `T` should implement `T(::Tuple)`, +e.g., `Tuple` or `MVector`. -Operations for which components do not have simple closed forms -(such as `exp` or `log`) are not amenable to symbolic evaluation. +!!! warning + Operations that are not amenable to symbolic evaluation + (such as `exp`, `log`, `sqrt`, etc) are not supported. + + (You may test if operations work on symbolic multivectors + created with [`GeometricAlgebra.make_symbolic`](@ref).) # Examples ```jldoctest -julia> let v = (1, 2, 0), R = exp(Multivector{3,2}([0, π/8, 0])) - # Rotate a tuple (viewed as a grade 1 vector) by a rotor. - @symbolicga 3 (v=1, R=0:2:4) grade(R*v*~R, 1) Tuple - # The compiled code evaluates the sandwich product and - # grade projection from a single analytic expression. - end +julia> v = (1, 2, 0); R = exp(Multivector{3,2}([0, π/8, 0])); + +julia> # Rotate a tuple (interpreted as a grade 1 vector) + # by a rotor, returning a tuple. + @symbolicga 3 (v=1, R=0:2:4) grade(R*v*~R, 1) Tuple (0.7071067811865475, 2.0, -0.7071067811865476) ``` ```julia @@ -256,9 +282,11 @@ end ``` """ macro symbolicga(sig, mv_grades, expr, result_type=nothing) - Sig = @eval $sig - mv_grades = @eval $mv_grades + Sig = eval(sig) + mv_grades = eval(mv_grades) @assert mv_grades isa NamedTuple + result_type = eval(result_type) + labels = keys(mv_grades) symbolic_assignments = map(labels, mv_grades) do label, K @@ -269,13 +297,12 @@ macro symbolicga(sig, mv_grades, expr, result_type=nothing) $expr end - # TODO: this should be improved. Assumes that result is a Multivector if isnothing(result_type) - result_expr = toexpr(symbolic_result, MVector) - elseif result_type == :Tuple - result_expr = toexpr(Tuple(symbolic_result.comps), nothing) + result_expr = toexpr(symbolic_result, componentstype(Sig, 0)) else - result_expr = :($result_type($(toexpr.(symbolic_result.comps, MVector)...))) + symbolic_result isa Multivector || error("@symbolicga expression must evaluate to a Multivector when result_type ($result_type) is given; got $(typeof(symbolic_result)).") + symbolic_comps = Tuple(toexpr(comp, nothing) for comp in symbolic_result.comps) + result_expr = :($result_type(($(symbolic_comps...),))) end expr_assignments = map(labels, mv_grades) do label, K diff --git a/test/generated.jl b/test/generated.jl index d1be986..987b181 100644 --- a/test/generated.jl +++ b/test/generated.jl @@ -1,7 +1,10 @@ using GeometricAlgebra: zeroslike, make_symbolic -using GeometricAlgebra.SymbolicUtils +import GeometricAlgebra.SymbolicUtils +using GeometricAlgebra.StaticArrays: + MVector, + SVector @testset "symbolic components" begin @@ -46,11 +49,19 @@ end y = (0, 1, 0) z = (0, 0, 1) - @test @symbolicga(3, (x=1, y=1), x + y, Tuple) == (1, 1, 0) + @test @symbolicga(3, (x=1, y=1), x + y, SVector) === SVector(1, 1, 0) @test @symbolicga(3, (x=1, y=1), x*y) == v12 R = exp(π/4*v12) @assert grade(R) == 0:2:3 @test @symbolicga(3, (x=1, R=0:2:3), grade(~R*x*R, 1)) ≈ v2 + joinpoints(a::Tuple, b::Tuple) = @symbolicga 3 (a=1, b=1) a∧b Tuple + meetlines(a::Tuple, b::Tuple) = @symbolicga 3 (a=2, b=2) a∨b Tuple + from_homogeneous((x, y, z)) = (x/z, y/z) + + l1 = @inferred joinpoints((1, 0, 1), (0, 1, 1)) # line y = 1 - x + l2 = @inferred joinpoints((0, 0, 1), (1, 1, 1)) # line y = x + @test from_homogeneous(meetlines(l1, l2)) == (0.5, 0.5) + end \ No newline at end of file