Integration with user-defined types

If you want to use values of a custom type in expressions for @ga (or any specific geometric space generated by @geometric_space), you are totally free to do so. By default, component extraction relies on 1-based indexing via SymbolicGA.getcomponent, which should be fine most of the time. If that won't work for you for any reason, you can still extend this method with your own types. The choices are therefore the following:

  • Extend Base.getindex(::MyType, ::Int)
  • Extend SymbolicGA.getcomponent(::MyType, ::Int)
using SymbolicGA

struct MyType{T}
  components::Dict{Symbol, T}
end
index_to_symbol(i) = (:x, :y, :z)[i]

SymbolicGA.getcomponent(x::MyType, i) = x.components[index_to_symbol(i)]

x = MyType(Dict(:x => 1.5, :y => 2.0, :z => 3.0))
y = MyType(Dict(:x => 1.2, :y => -1.4, :z => 0.1))
@ga 3 dual(x::1 ∧ y::1)
KVector{1, Float64, 3, 3}(4.3999999999999995, 3.4499999999999997, -4.5)

Note that we still got a KVector out by default; if you want, you may provide MyType as output, but you also need to define how to reconstruct a type given a tuple of components. By default, SymbolicGA.construct calls the provided type constructor on the tuple of components. You may therefore either:

  • Define a MyType(::Tuple) constructor
  • Extend SymbolicGA.construct(::Type{MyType}, ::Tuple)
SymbolicGA.construct(T::Type{<:MyType}, components::Tuple) = T(Dict((:x, :y, :z) .=> components))

@ga 3 MyType dual(x::1 ∧ y::1)
Main.MyType{Float64}(Dict(:y => 3.4499999999999997, :z => -4.5, :x => 4.3999999999999995))

Of course, you can also omit the return type and operate with a KVector. In fact, that is recommended especially if you don't know what kind of object you'll end up with. For example, would you know what dual((x::1 ⟑ y::1) ⋅ y::1) + exp(x::1 ∧ y::1) returns? Well, let's see:

@ga 3 dual((x::1 ⟑ y::1) ⋅ y::1) + exp(x::1 ∧ y::1)
(KVector{0, Float64, 3, 1}(-0.3440881217009017), Bivector{Float64, 3, 3}(10.52536392668556, -4.934004959947117, 5.855062757428548))

Looks like we got a scalar and a bivector! We could manipulate this KVector around, which is a mere allocation-free wrapper over a tuple, but if you already know what type of data structure you want to put your data into, you may find it more convenient to specify it when calling the macro.


This page was generated using Literate.jl.