diff --git a/Project.toml b/Project.toml index 8ad3aef..d2d57ce 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,7 @@ ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" +StructEquality = "6ec83bb0-ed9f-11e9-3b4c-2b04cb4e219c" SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" @@ -17,6 +18,7 @@ ACSets = "0.2" Catlab = "0.15, 0.16" DataStructures = "0.18.13" MLStyle = "0.4.17" +StructEquality = "2.1.0" SymbolicUtils = "3.1.2" Unicode = "1.6" julia = "1.6" diff --git a/src/ThDEC.jl b/src/ThDEC.jl index 52f5198..9e5f3e2 100644 --- a/src/ThDEC.jl +++ b/src/ThDEC.jl @@ -1,5 +1,6 @@ module ThDEC using MLStyle +using StructEquality import Base: +, -, * @@ -7,22 +8,46 @@ struct SortError <: Exception message::String end +@struct_hash_equal struct Space + name::Symbol + dim::Int +end +export Space + +dim(s::Space) = s.dim +Base.nameof(s::Space) = s.name + +struct SpaceLookup + default::Space + named::Dict{Symbol,Space} +end + @data Sort begin Scalar() - Form(dim::Int, isdual::Bool) - VField(isdual::Bool) + Form(dim::Int, isdual::Bool, space::Space) + VField(isdual::Bool, space::Space) end export Sort, Scalar, Form, VField -const SORT_LOOKUP = Dict( - :Form0 => Form(0, false), - :Form1 => Form(1, false), - :Form2 => Form(2, false), - :DualForm0 => Form(0, true), - :DualForm1 => Form(1, true), - :DualForm2 => Form(2, true), - :Constant => Scalar() -) +function fromexpr(lookup::SpaceLookup, e, ::Type{Sort}) + (name, spacename) = @match e begin + name::Symbol => (name, nothing) + :($name{$spacename}) => (name, spacename) + end + space = @match spacename begin + ::Nothing => lookup.default + name::Symbol => lookup.named[name] + end + @match name begin + :Form0 => Form(0, false, space) + :Form1 => Form(1, false, space) + :Form2 => Form(2, false, space) + :DualForm0 => Form(0, true, space) + :DualForm1 => Form(1, true, space) + :DualForm2 => Form(2, true, space) + :Constant => Scalar() + end +end function Base.nameof(s::Scalar) :Constant @@ -30,15 +55,19 @@ end function Base.nameof(f::Form) dual = isdual(f) ? "Dual" : "" - Symbol("$(dual)Form$(dim(f))") + formname = Symbol("$(dual)Form$(dim(f))") + Expr(:curly, formname, dim(space(f))) end const VF = VField dim(ω::Form) = ω.dim isdual(ω::Form) = ω.isdual +space(ω::Form) = ω.space +export space isdual(v::VField) = v.isdual +space(v::VField) = v.space # convenience functions PrimalForm(i::Int) = Form(i, false) @@ -57,20 +86,26 @@ export DualVF show_duality(ω::Form) = isdual(ω) ? "dual" : "primal" function Base.show(io::IO, ω::Form) - print(io, isdual(ω) ? "DualForm($(dim(ω)))" : "PrimalForm($(dim(ω)))") + print(io, isdual(ω) ? "DualForm($(dim(ω))) on $(space(ω))" : "PrimalForm($(dim(ω))) on $(space(ω))") end +# TODO: VField @nospecialize function +(s1::Sort, s2::Sort) @match (s1, s2) begin (Scalar(), Scalar()) => Scalar() - (Scalar(), Form(i, isdual)) || - (Form(i, isdual), Scalar()) => Form(i, isdual) - (Form(i1, isdual1), Form(i2, isdual2)) => - if (i1 == i2) && (isdual1 == isdual2) + (Scalar(), Form(i, isdual, space)) || + (Form(i, isdual, space), Scalar()) => Form(i, isdual, space) + (Form(i1, isdual1, space1), Form(i2, isdual2, space2)) => + if (i1 == i2) && (isdual1 == isdual2) && (space1 == space2) Form(i1, isdual1) else - throw(SortError("Cannot add two forms of different dimensions/dualities: $((i1,isdual1)) and $((i2,isdual2))")) + throw(SortError( + """ + Can not add two forms of different dimensions/dualities/spaces: + $((i1,isdual1,space1)) and $((i2,isdual2,space2)) + """) + ) end end end @@ -83,13 +118,15 @@ end # Negation is always valid -(s::Sort) = s +# TODO: VField @nospecialize function *(s1::Sort, s2::Sort) @match (s1, s2) begin (Scalar(), Scalar()) => Scalar() - (Scalar(), Form(i, isdual)) || - (Form(i, isdual), Scalar()) => Form(i, isdual) - (Form(_, _), Form(_, _)) => throw(SortError("Cannot scalar multiply a form with a form. Maybe try `∧`??")) + (Scalar(), Form(i, isdual, space)) || + (Form(i, isdual, space), Scalar()) => Form(i, isdual) + (Form(_, _, _), Form(_, _, _)) => + throw(SortError("Cannot scalar multiply a form with a form. Maybe try `∧`??")) end end @@ -99,17 +136,20 @@ function as_sub(n::Int) join(map(d -> SUBSCRIPT_DIGIT_0 + d, digits(n))) end +# TODO: VField @nospecialize function ∧(s1::Sort, s2::Sort) @match (s1, s2) begin - (Form(i, isdual), Scalar()) || (Scalar(), Form(i, isdual)) => Form(i, isdual) - (Form(i1, isdual), Form(i2, isdual)) => - if i1 + i2 <= 2 - Form(i1 + i2, isdual) + (Form(i, isdual, space), Scalar()) || (Scalar(), Form(i, isdual, space)) => + Form(i, isdual, space) + (Form(i1, isdual1, space1), Form(i2, isdual2, space2)) => begin + (isdual1 == isdual2) && (space1 == space2) || throw(SortError("Can only take a wedge product of two forms of the same duality on the same space")) + if i1 + i2 <= dim(space) + Form(i1 + i2, isdual, space) else - throw(SortError("Can only take a wedge product when the dimensions of the forms add to less than 2: tried to wedge product $i1 and $i2")) + throw(SortError("Can only take a wedge product when the dimensions of the forms add to less than n, where n = $(dim(space)) is the dimension of the ambient space: tried to wedge product $i1 and $i2")) end - _ => throw(SortError("Can only take a wedge product of two forms of the same duality")) + end end end @@ -124,11 +164,11 @@ end function d(s::Sort) @match s begin Scalar() => throw(SortError("Cannot take exterior derivative of a scalar")) - Form(i, isdual) => - if i <= 1 - Form(i + 1, isdual) + Form(i, isdual, space) => + if i < dim(space) + Form(i + 1, isdual, space) else - throw(SortError("Cannot take exterior derivative of a n-form for n >= 1")) + throw(SortError("Cannot take exterior derivative of a k-form for k >= n, where n = $(dim(space)) is the dimension of its ambient space")) end end end @@ -141,20 +181,20 @@ end function ★(s::Sort) @match s begin Scalar() => throw(SortError("Cannot take Hodge star of a scalar")) - Form(i, isdual) => Form(2 - i, !isdual) + Form(i, isdual, space) => Form(dim(space) - i, !isdual, space) end end function Base.nameof(::typeof(★), s) inv = isdual(s) ? "⁻¹" : "" - Symbol("★$(as_sub(isdual(s) ? 2 - dim(s) : dim(s)))$(inv)") + Symbol("★$(as_sub(isdual(s) ? dim(space(s)) - dim(s) : dim(s)))$(inv)") end @nospecialize function ι(s1::Sort, s2::Sort) @match (s1, s2) begin - (VF(true), Form(i, true)) => PrimalForm() # wrong - (VF(true), Form(i, false)) => DualForm() + (VF(true, space), Form(i, true, space)) => PrimalForm() # wrong + (VF(true, space), Form(i, false, space)) => DualForm() _ => throw(SortError("Can only define the discrete interior product on: PrimalVF, DualForm(i) DualVF(), PrimalForm(i) @@ -166,7 +206,7 @@ end function ♯(s::Sort) @match s begin Scalar() => PrimalVF() - Form(1, isdual) => VF(isdual) + Form(1, isdual, space) => VF(isdual, space) _ => throw(SortError("Can only take ♯ to 1-forms")) end end @@ -174,7 +214,7 @@ end function ♭(s::Sort) @match s begin - VF(true) => PrimalForm(1) + VF(true, space) => PrimalForm(1, false, space) _ => throw(SortError("Can only apply ♭ to dual vector fields")) end end @@ -183,7 +223,7 @@ end function ♭♯(s::Sort) @match s begin - Form(i, isdual) => Form(i, !isdual) + Form(i, isdual, space) => Form(i, !isdual, space) _ => throw(SortError("♭♯ is only defined on forms.")) end end @@ -191,7 +231,7 @@ end # Δ = ★d⋆d, but we check signature here to throw a more helpful error function Δ(s::Sort) @match s begin - Form(0, isdual) => Form(0, isdual) + Form(0, isdual, space) => Form(0, isdual, space) _ => throw(SortError("Δ is not defined for $s")) end end diff --git a/src/decasymbolic.jl b/src/decasymbolic.jl index 3b86fa6..5ccd25b 100644 --- a/src/decasymbolic.jl +++ b/src/decasymbolic.jl @@ -12,43 +12,47 @@ abstract type DECType <: Number end """ i: dimension: 0,1,2, etc. d: duality: true = dual, false = primal +s: name of the space (a symbol) +n: dimension of the space """ -struct FormT{i,d} <: DECType +struct FormT{i,d,s,n} <: DECType end +export FormT -struct VFieldT{d} <: DECType +struct VFieldT{d,s,n} <: DECType end +export VFieldT dim(::Type{<:FormT{d}}) where {d} = d isdual(::Type{FormT{i,d}}) where {i,d} = d # convenience functions -const PrimalFormT{i} = FormT{i,false} +const PrimalFormT{i,s,n} = FormT{i,false,s,n} export PrimalFormT -const DualFormT{i} = FormT{i,true} +const DualFormT{i,s,n} = FormT{i,true,s,n} export DualFormT -const PrimalVFT = VFieldT{false} +const PrimalVFT{s,n} = VFieldT{false,s,n} export PrimalVFT -const DualVFT = VFieldT{true} +const DualVFT{s,n} = VFieldT{true,s,n} export DualVFT -function Sort(::Type{FormT{i,d}}) where {i,d} - Form(i, d) +function Sort(::Type{FormT{i,d,s,n}}) where {i,d,s,n} + Form(i, d, Space(s, n)) end function Number(f::Form) - FormT{dim(f),isdual(f)} + FormT{dim(f),isdual(f), nameof(space(f)), dim(space(f))} end -function Sort(::Type{VFieldT{d}}) where {d} - VField(d) +function Sort(::Type{VFieldT{d,s,n}}) where {d,s,n} + VField(d, Space(s, n)) end function Number(v::VField) - VFieldT{isdual(v)} + VFieldT{isdual(v), nameof(space(v)), dim(space(v))} end function Sort(::Type{<:Real})