diff --git a/src/DiagrammaticEquations.jl b/src/DiagrammaticEquations.jl index 1a9f133..913b6d3 100644 --- a/src/DiagrammaticEquations.jl +++ b/src/DiagrammaticEquations.jl @@ -44,7 +44,14 @@ import Unicode normalize_unicode(s::String) = Unicode.normalize(s, compose=true, stable=true, chartransform=Unicode.julia_chartransform) normalize_unicode(s::Symbol) = Symbol(normalize_unicode(String(s))) DerivOp = Symbol("∂ₜ") -append_dot(s::Symbol) = Symbol(string(s)*'\U0307') +# append_dot(s::Symbol) = Symbol(string(s)*'\U0307') +append_dot(s::Symbol) = Symbol(string(s)*'\U0209C') +append_dot(s::Symbol, wrt::Symbol) = + @match wrt begin + :t => Symbol(string(s)*'\U0209C') + :x => Symbol(string(s)*'\U02093') + _ => s + end include("acset.jl") include("language.jl") diff --git a/src/colanguage.jl b/src/colanguage.jl index d89f9b0..f9746de 100644 --- a/src/colanguage.jl +++ b/src/colanguage.jl @@ -15,7 +15,7 @@ function Term(s::SummationDecapode) y = Var(s[op, [:tgt, :name]]) f = s[op, :op1] if f == :∂ₜ - Eq(y, Tan(x)) + Eq(y, Partial(x, :t, 1)) elseif typeof(f) == Vector{Symbol} Eq(y, AppCirc1(f, x)) else diff --git a/src/decapodes.it b/src/decapodes.it index a6b23b6..ee02903 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -7,7 +7,7 @@ Plus(args::Vector{Term}) Mult(args::Vector{Term}) Tan(var::Term) - Partial(var::Term, wrt::Symbol, order::Int64, iteration::Int64) + Partial(var::Term, wrt::Symbol, order::Int64) end struct Judgement diff --git a/src/language.jl b/src/language.jl index d07cb7b..1ab0209 100644 --- a/src/language.jl +++ b/src/language.jl @@ -9,9 +9,10 @@ term(s::Number) = Lit(Symbol(s)) term(expr::Expr) = begin @match expr begin #TODO: Would we want ∂ₜ to be used with general expressions or just Vars? - Expr(:call, :∂ₜ, b) => Tan(Var(b)) - Expr(:call, :dt, b) => Tan(Var(b)) - Expr(:call, ∂, b) && if ishigherorderpartial(∂) end => Partial(term(b), ∂s[∂]..., 0) + Expr(:call, :∂ₜ, b) => Partial(term(b), :t, 1) + Expr(:call, :dt, b) => Partial(term(b), :t, 1) + # Tan(Var(b)) + Expr(:call, ∂, b) && if ishigherorderpartial(∂) end => Partial(term(b), ∂s[∂]...) Expr(:call, Expr(:call, :∘, a...), b) => AppCirc1(a, term(b)) Expr(:call, a, b) => App1(a, term(b)) @@ -33,10 +34,8 @@ ishigherorderpartial(t::Expr) = @match t begin end ∂s = Dict(:∂ₜ¹ => (:t, 1), :∂ₜ² => (:t, 2), :∂ₜ³ => (:t, 3), :∂ₜ⁴ => (:t, 4), :∂ₜ⁵ => (:t, 5)) -ps = Dict(1 => :∂ₜ, 2 => :∂ₜ², 3 => :∂ₜ³) function parse_decapode(expr::Expr) - # XXX how does flatmap affect maps in this match expression? stmts = map(expr.args) do line @match line begin ::LineNumberNode => missing @@ -96,63 +95,55 @@ function reduce_term_plus!(ts::Vector{Term}, d::AbstractDecapode, syms::Dict{Sym return res_var end -const MULT_SYM = Symbol("*") - -# TODO: res=res_var undefined +# TODO this can probably be a fold function reduce_term_mult!(ts::Vector{Term}, d::AbstractDecapode, syms::Dict{Symbol, Int}) - multiplicands = [reduce_term!(t,d,syms) for t in ts] - res_var = add_part!(d, :Var, type=:infer, name=:mult) - m1,m2 = multiplicands[1:2] - add_part!(d, :Op2, proj1=m1, proj2=m2, res=res_var, op2=Symbol("*")) - for m in multiplicands[3:end] - m1 = res_var - m2 = m - res_var = add_part!(d, :Var, type=:infer, name=:mult) - add_part!(d, :Op2, proj1=m1, proj2=m2, res=res_var, op2=Symbol("*")) - end - return res_var -# multiplicands = [reduce_term!.(ts, Ref(d), Ref(syms))] -# out = foldl(((m1, m2) -> add_part!(d, :Op2, proj1=m1, proj2=m2, res=m1, op2=MULT_SYM)) -# ,multiplicands, add_part!(d, :Var, type=:infer, name=:mult)) -# @info "MULT" out -# out + multiplicands = [reduce_term!(t,d,syms) for t in ts] + res_var = add_part!(d, :Var, type=:infer, name=:mult) + m1, m2 = multiplicands[1:2] + add_part!(d, :Op2, proj1=m1, proj2=m2, res=res_var, op2=Symbol("*")) + for m in multiplicands[3:end] + m1 = res_var + m2 = m + res_var = add_part!(d, :Var, type=:infer, name=:mult) + add_part!(d, :Op2, proj1=m1, proj2=m2, res=res_var, op2=Symbol("*")) + end + return res_var end -append_doot(s) = Symbol(string(s)*"_1") - -# TODO do we want to 1) continue using DerivOp and 2) create a spurious var. with same name? +# TODO change TVar table function reduce_term_tan!(t::Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) - txv = add_part!(d, :Var, type=:infer, name=append_doot(t.name)) - tx = add_part!(d, :TVar, incl=txv) # cache tvars in a list - src = incident(d, t.name, :name)[1] - tanop = add_part!(d, :Op1, src=src, tgt=txv, op1=DerivOp) + txv = add_part!(d, :Var, type=:infer, name=append_dot(t.name)) + tx = add_part!(d, :TVar, incl=txv) + tanop = add_part!(d, :Op1, src=reduce_term!(t, d, syms), tgt=txv, op1=DerivOp) return txv end -function _reduce_term_partial!(t::Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) - txv = reduce_term_tan!(t.var, d, syms) - reduce_term!(Partial(Var(d[txv,:name]), t.wrt, t.order-1, t.iteration+1), d, syms) +function reduce_term_partial!(t::Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) + src = reduce_term!(Partial(t.var, t.wrt, t.order - 1), d, syms) + txv = add_part!(d, :Var, type=:infer, name=append_dot(d[src,:name])) + tx = add_part!(d, :TVar, incl=txv) + tanop = add_part!(d, :Op1, src=src, tgt=txv, op1=DerivOp) return txv end -# TODO george said something about this -function reduce_term_partial!(t::Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) - txv = reduce_term!(Partial(t.var, t.wrt, t.order - 1, 0), d, syms) - txv = reduce_term_tan!(Var(d[txv,:name]), d, syms) - return txv +function throw_reduce_error(t::Term) + @match t begin + Partial(expr, _, _) => throw("Partial time derivatives of this expression '$expr' is not yet supported") + _ => throw("Inline judgements are not supported") + end end function reduce_term!(t::Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) @match t begin - Var(x) => reduce_term_var!(x, d, syms) + Var(x) || Partial(Var(x), wrt, 0) => reduce_term_var!(x, d, syms) Lit(x) => reduce_term_lit!(x, d, syms) App1(f, t) || AppCirc1(f, t) => reduce_term_app1circ!(f, t, d, syms) App2(f, t1, t2) => reduce_term_app2!(f, t1, t2, d, syms) Plus(ts) => reduce_term_plus!(ts, d, syms) Mult(ts) => reduce_term_mult!(ts, d, syms) - Tan(t) || Partial(t, wrt, 1, k) => reduce_term_tan!(t, d, syms) - Partial(u, wrt, n, k) => reduce_term_partial!(t, d, syms) - _ => throw("Inline type Judgements not yet supported!") + Tan(t) => reduce_term_tan!(t, d, syms) + Partial(Var(x), wrt, n) => reduce_term_partial!(t, d, syms) + e => throw_reduce_error(e) end end diff --git a/src/pretty.jl b/src/pretty.jl index a5a93ff..736ed3c 100644 --- a/src/pretty.jl +++ b/src/pretty.jl @@ -41,6 +41,7 @@ pprint(io::IO, exp::Term, pad=0) = begin Plus(args) => print(io, "$(join(map(!, args), " + "))") Mult(args) => print(io, "($(join(map(!, args), " * "))") Tan(var) => print(io, "∂ₜ($(!var))") + Partial(var, wrt, order) => print(io, "∂ₜ($(!var))") _ => error("printing $exp") end end diff --git a/test/collages.jl b/test/collages.jl index 90cbda8..4510dfc 100644 --- a/test/collages.jl +++ b/test/collages.jl @@ -29,7 +29,7 @@ end DiffusionSymbols = Dict( :C => :K, - :Ċ => :K̇, + :Ċ => :Kₜ, :Cb1 => :Kb1, :Cb2 => :Kb2, :Zero => :Null) @@ -54,7 +54,7 @@ DiffusionCollage = DiagrammaticEquations.collate( op1 = Any[:∂ₜ, [:d, :⋆, :d, :⋆]] op2 = [:rb1_leftwall, :rb2_rightwall, :rb3] type = [:Form0, :infer, :Form0, :Form0, :Form0, :Form0, :infer, :Form0] - name = [:r1_K, :r3_K̇, :r2_K, :Kb1, :K, :Kb2, :K̇, :Null] + name = [:r1_K, :r3_Kₜ, :r2_K, :Kb1, :K, :Kb2, :Kₜ, :Null] end # Note: Since the order does not matter in which rb1 and rb2 are applied, it diff --git a/test/language.jl b/test/language.jl index 1de119b..e773eab 100644 --- a/test/language.jl +++ b/test/language.jl @@ -14,6 +14,57 @@ using DiagrammaticEquations @testset "Parsing" begin + # does +_3pt = @decapode begin + U::Form0 + # + ∂ₜ³(U) == ∂ₜ³U +end +@test _3pt[:name] == [:U, :Uₜ, :Uₜₜ, :∂ₜ³U] +@test _3pt[:incl] == [2, 3, 4] +@test _3pt[:src] == [1, 2, 3] +@test _3pt[:tgt] == [2, 3, 4] + +_3pt_k = @decapode begin + U::Form0 + k::Constant + # + ∂ₜ³(U) == k*U +end +@test _3pt_k[:name] == [:U, :k, :Uₜ, :Uₜₜ, :Uₜₜₜ] +@test _3pt_k[:incl] == [3, 4, 5] +@test _3pt_k[:src] == [1, 3, 4] +@test _3pt_k[:tgt] == [3, 4, 5] +@test _3pt_k[:proj1] == [2] +@test _3pt_k[:proj2] == [1] +@test _3pt_k[:res] == [5] + +_ab = @decapode begin + (A,B)::Form0 + # + A == dt(B) +end +@test _ab[:name] == [:A, :B] +@test _ab[:incl] == [1] +@test _ab[:src] == [2] +@test _ab[:tgt] == [1] + +_abΣ = @decapode begin + (A,B,C)::Form0 + # + A == dt(B) + C == A + B +end + +@test _abΣ[:name] == [:A, :B, :C] +@test _abΣ[:incl] == [1] +@test _abΣ[:src] == [2] +@test _abΣ[:tgt] == [1] +@test _abΣ[:summand] == [1, 2] +@test _abΣ[:summation] == [1, 1] + + + # @present DiffusionSpace2D(FreeExtCalc2D) begin # X::Space # k::Hom(Form1(X), Form1(X)) # diffusivity of space, usually constant (scalar multiplication) @@ -151,7 +202,7 @@ using DiagrammaticEquations end diffExpr7 = parse_decapode(DiffusionExprBody7) ddp7 = SummationDecapode(diffExpr7) - @test ddp7[:name] == [:ϕ, :Ċ, Symbol("2"), Symbol("•2"), :C] + @test ddp7[:name] == [:ϕ, :Cₜ, Symbol("2"), Symbol("•1"), :C] @test ddp7[:incl] == [2] # Vars can only be of certain types. @@ -192,13 +243,13 @@ using DiagrammaticEquations E == ∂ₜ(D) end pt3 = SummationDecapode(parse_decapode(ParseTest3)) - @test pt3[:name] == [:D, :E, :C] - @test pt3[:incl] == [1,2] - @test pt3[:src] == [3, 1] - @test pt3[:tgt] == [1, 2] + @test pt3[:name] == [:D, :C, :E] + @test pt3[:incl] == [1, 3] + @test pt3[:src] == [2, 1] + @test pt3[:tgt] == [1, 3] dot_rename!(pt3) - @test pt3[:name] == [Symbol('C'*'\U0307'), Symbol('C'*'\U0307'*'\U0307'), :C] + @test pt3[:name] == [Symbol('C'*'\U0209C'), :C, Symbol('C'*'\U0209C'*'\U0209C')] # TODO: We should eventually recognize this equivalence #= ParseTest4 = quote @@ -218,9 +269,9 @@ using DiagrammaticEquations ∂ₜ(V) == -1*k*(X) end)) - @test pt5[:name] == [:X, :V, :k, :mult_1, Symbol('V'*'\U0307'), Symbol("-1")] + @test pt5[:name] == [:X, :V, :k, :mult_1, Symbol('V'*'\U0209C'), Symbol("-1")] dot_rename!(pt5) - @test pt5[:name] == [:X, Symbol('X'*'\U0307'), :k, :mult_1, Symbol('X'*'\U0307'*'\U0307'), Symbol("-1")] + @test pt5[:name] == [:X, Symbol('X'*'\U0209C'), :k, :mult_1, Symbol('X'*'\U0209C'*'\U0209C'), Symbol("-1")] end Deca = quote @@ -237,7 +288,7 @@ end # @test term(:(∘(k, d₀)(C))) == AppCirc1([:k, :d₀], Var(:C)) #(:App1, ((:Circ, :k, :d₀), Var(:C))) # @test term(:(∘(k, d₀{X})(C))) == (:App1, ((:Circ, :k, :(d₀{X})), Var(:C))) @test_throws MethodError term(:(Ċ == ∘(⋆₀⁻¹{X}, dual_d₁{X}, ⋆₁{X})(ϕ))) - @test term(:(∂ₜ(C))) == Tan(Var(:C)) + @test term(:(∂ₜ(C))) == Partial(Var(:C), :t, 1) # @test term(:(∂ₜ{Form0}(C))) == App1(:Tan, Var(:C)) end @@ -870,7 +921,57 @@ end names_types_hx = zip(HeatXfer[:name], HeatXfer[:type]) names_types_expected_hx = [ - (:T, :Form0), (:continuity_Ṫ₁, :Form0), (:continuity_diffusion_ϕ, :DualForm1), (:continuity_diffusion_k, :Parameter), (Symbol("continuity_diffusion_•1"), :DualForm2), (Symbol("continuity_diffusion_•2"), :Form1), (Symbol("continuity_diffusion_•3"), :Form1), (:M, :Form1), (:continuity_advection_V, :Form1), (:ρ, :Form0), (:P, :Form0), (:continuity_Ṫₐ, :Form0), (Symbol("continuity_advection_•1"), :Form0), (Symbol("continuity_advection_•2"), :Form1), (Symbol("continuity_advection_•3"), :DualForm2), (Symbol("continuity_advection_•4"), :Form0), (Symbol("continuity_advection_•5"), :DualForm2), (:continuity_Ṫ, :Form0), (:navierstokes_Ṁ, :Form1), (:navierstokes_G, :Form1), (:navierstokes_V, :Form1), (:navierstokes_ṗ, :Form0), (:navierstokes_two, :Parameter), (:navierstokes_three, :Parameter), (:navierstokes_kᵥ, :Parameter), (Symbol("navierstokes_•1"), :DualForm2), (Symbol("navierstokes_•2"), :Form1), (Symbol("navierstokes_•3"), :Form1), (Symbol("navierstokes_•4"), :DualForm1), (Symbol("navierstokes_•5"), :DualForm1), (Symbol("navierstokes_•6"), :DualForm1), (Symbol("navierstokes_•7"), :Form1), (Symbol("navierstokes_•8"), :Form1), (Symbol("navierstokes_•9"), :Form1), (Symbol("navierstokes_•10"), :Form1), (Symbol("navierstokes_•11"), :Form1), (:navierstokes_sum_1, :Form1), (Symbol("navierstokes_•12"), :Form0), (Symbol("navierstokes_•13"), :Form1), (Symbol("navierstokes_•14"), :Form1), (Symbol("navierstokes_•15"), :Form0), (Symbol("navierstokes_•16"), :DualForm0), (Symbol("navierstokes_•17"), :DualForm1), (Symbol("navierstokes_•18"), :Form1), (Symbol("navierstokes_•19"), :Form1), (Symbol("navierstokes_•20"), :Form1), (:navierstokes_sum_2, :Form0), (Symbol("navierstokes_•21"), :Form1), (Symbol("navierstokes_•22"), :Form1), (Symbol("navierstokes_•23"), :DualForm2)] + (:T, :Form0), + (:continuity_Ṫ₁, :Form0), + (:continuity_diffusion_ϕ, :DualForm1), + (:continuity_diffusion_k, :Parameter), + (Symbol("continuity_diffusion_•1"), :DualForm2), + (Symbol("continuity_diffusion_•2"), :Form1), + (Symbol("continuity_diffusion_•3"), :Form1), + (:M, :Form1), + (:continuity_advection_V, :Form1), + (:ρ, :Form0), + (:P, :Form0), + (:continuity_Ṫₐ, :Form0), + (Symbol("continuity_advection_•1"), :Form0), + (Symbol("continuity_advection_•2"), :Form1), + (Symbol("continuity_advection_•3"), :DualForm2), + (Symbol("continuity_advection_•4"), :Form0), + (Symbol("continuity_advection_•5"), :DualForm2), + (:continuity_Ṫ, :Form0), + (:navierstokes_Ṁ, :Form1), + (:navierstokes_G, :Form1), + (:navierstokes_V, :Form1), + (:navierstokes_ṗ, :Form0), + (:navierstokes_two, :Parameter), + (:navierstokes_three, :Parameter), + (:navierstokes_kᵥ, :Parameter), + (Symbol("navierstokes_•1"), :DualForm2), + (Symbol("navierstokes_•2"), :Form1), + (Symbol("navierstokes_•3"), :Form1), + (Symbol("navierstokes_•4"), :DualForm1), + (Symbol("navierstokes_•5"), :DualForm1), + (Symbol("navierstokes_•6"), :DualForm1), + (Symbol("navierstokes_•7"), :Form1), + (Symbol("navierstokes_•8"), :Form1), + (Symbol("navierstokes_•9"), :Form1), + (Symbol("navierstokes_•10"), :Form1), + (Symbol("navierstokes_•11"), :Form1), + (:navierstokes_sum_1, :Form1), + (Symbol("navierstokes_•12"), :Form0), + (Symbol("navierstokes_•13"), :Form1), + (Symbol("navierstokes_•14"), :Form1), + (Symbol("navierstokes_•15"), :Form0), + (Symbol("navierstokes_•16"), :DualForm0), + (Symbol("navierstokes_•17"), :DualForm1), + (Symbol("navierstokes_•18"), :Form1), + (Symbol("navierstokes_•19"), :Form1), + (Symbol("navierstokes_•20"), :Form1), + (:navierstokes_sum_2, :Form0), + (Symbol("navierstokes_•21"), :Form1), + (Symbol("navierstokes_•22"), :Form1), + (Symbol("navierstokes_sum_2"), :Form0), + (Symbol("navierstokes_Mₜ"), :DualForm2)] @test issetequal(names_types_hx, names_types_expected_hx) @@ -1077,7 +1178,7 @@ end ∂ₜ(B) == ⋆₂(d₁(E)) end)) - Veronis_rec_del = recursive_delete_parents(Veronis, incident(Veronis, :Ḃ, :name)) + Veronis_rec_del = recursive_delete_parents(Veronis, incident(Veronis, :Bₜ, :name)) @test Veronis_rec_del == SummationDecapode(parse_decapode(quote B::DualForm0{X} E::Form1{X} @@ -1100,7 +1201,7 @@ end ∂ₜ(B) == ⋆₂(d₁(E)) end)) - Veronis_rec_del = recursive_delete_parents(Veronis, incident(Veronis, :Ė, :name)) + Veronis_rec_del = recursive_delete_parents(Veronis, incident(Veronis, :Eₜ, :name)) # We should test if Decapodes are isomorphic, but Catlab # doesn't do this yet. #@test Veronis_rec_del == SummationDecapode(parse_decapode(quote @@ -1115,7 +1216,7 @@ end Op1 = 3 type = [:DualForm0, :Form1, :infer, :infer] - name = [:B, :E, :sum_1, :Ḃ] + name = [:B, :E, :sum_1, :Bₜ] incl = [4] diff --git a/test/pretty.jl b/test/pretty.jl index 0d2846b..fbdb108 100644 --- a/test/pretty.jl +++ b/test/pretty.jl @@ -4,16 +4,17 @@ using DiagrammaticEquations using DiagrammaticEquations.decapodes @testset "Pretty Printing" begin + mdl = parse_decapode(quote (Q, Tₛ, ASR, OLR, HT)::Form0 (α, A,B,C)::Constant (D,cosϕᵖ,cosϕᵈ)::Constant - + # Tₛ̇ == ∂ₜ(Tₛ) Tₛ̇ == (ASR - OLR + HT) / C ASR == (1 - α) * Q OLR == A + (B * Tₛ) - + # HT == (D ./ cosϕᵖ) * ⋆(d(cosϕᵈ * ⋆(d(Tₛ)))) end) output = "Context:\n Q::Form0 over I\n Tₛ::Form0 over I\n ASR::Form0 over I\n OLR::Form0 over I\n HT::Form0 over I\n α::Constant over I\n A::Constant over I\n B::Constant over I\n C::Constant over I\n D::Constant over I\n cosϕᵖ::Constant over I\n cosϕᵈ::Constant over I\nEquations:\nTₛ̇ = ∂ₜ(Tₛ)\nTₛ̇ = ASR - OLR + HT / C\nASR = 1 - α * Q\nOLR = A + B * Tₛ\nHT = D ./ cosϕᵖ * ⋆(d(cosϕᵈ * ⋆(d(Tₛ))))\n"