From 883a465ebc29ae3103385a2cd26a4846fdd0a369 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Fri, 1 Dec 2023 09:46:50 -0500 Subject: [PATCH 01/35] Started making versions with InterTypes, amr.it and decapodes.it, and a corresponding test script intertype_examples.jl --- Project.toml | 2 +- src/amr.it | 25 +++++++++++ src/decapodes.it | 39 +++++++++++++++++ test/intertype_examples.jl | 89 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/amr.it create mode 100644 src/decapodes.it create mode 100644 test/intertype_examples.jl diff --git a/Project.toml b/Project.toml index fa157a5..c3fd07f 100644 --- a/Project.toml +++ b/Project.toml @@ -17,7 +17,7 @@ StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" [compat] ACSets = "0.2" Catlab = "0.15, 0.16" -Decapodes = "0.5" +Decapodes = "0.4, 0.5" JSON = "0.21" JSON3 = "1" MLStyle = "0.4" diff --git a/src/amr.it b/src/amr.it new file mode 100644 index 0000000..77bf525 --- /dev/null +++ b/src/amr.it @@ -0,0 +1,25 @@ +@sum MathML begin + Math(str::String) + Presentation(str::String) +end + +@sum Note begin + Name(str::String) + Description(str::String) + Grounding(ontology::String, identifier::String) + Units(expression::String) +end + +struct Annotation + entity::Symbol + type::Symbol + note::Note +end + +struct Header + name::String + schema::String + description::String + schema_name::String + model_version::String +end diff --git a/src/decapodes.it b/src/decapodes.it new file mode 100644 index 0000000..10abb78 --- /dev/null +++ b/src/decapodes.it @@ -0,0 +1,39 @@ +@sum Term begin + Var(name::Symbol) + Lit(name::Symbol) + AppCirc1(fs::Vector{Symbol}, arg::Term) + App1(f::Symbol, arg::Term) + App2(f::Symbol, arg1::Term, arg2::Term) + Plus(args::Vector{Term}) + Mult(args::Vector{Term}) + Tan(var::Term) +end + +struct Judgement + var::Symbol + dim::Symbol + space::Symbol +end + +@sum Equation begin + Eq(lhs::Term, rhs::Term) +end + +struct DecaExpr + context::Vector{Judgement} + equations::Vector{Equation} +end + +struct Header + id::Union{String, Nothing} + description::String, + name::String, + model_version::String, + schema::String + schema_name::String +end + +@sum ASKEMDeca begin + ASKEMDecaExpr(header::Header, model::DecaExpr, annotations::Vector{Annotation{Symbol,Symbol}}) + ASKEMDecapode(header::Header, model::SummationDecapode, annotations::Vector{Annotation{Symbol,Symbol}}) +end \ No newline at end of file diff --git a/test/intertype_examples.jl b/test/intertype_examples.jl new file mode 100644 index 0000000..79dbc34 --- /dev/null +++ b/test/intertype_examples.jl @@ -0,0 +1,89 @@ + +using SyntacticModels + +using ACSets +using ACSets.InterTypes +using Test +using OrderedCollections +import JSON +import JSON3 +import JSONSchema + +include("src/SyntacticModels.jl") + +using .SyntacticModels +# using .SyntacticModels.ASKEMDecapodes + + + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface +using StructTypes + +# using ..SyntacticModelsBase + + +@intertypes "../src/amr.it" module amr end + +using .amr + + + +@intertypes "../src/decapodes.it" module decapodes + import ..amr +end + +using .decapodes + + + +#= +@intertypes "simpleast.it" module simpleast end + +using .simpleast + +t = Plus([Constant(ConstInt(1)), Constant(ConstInt(2))]) + +s = jsonwrite(t) + +@test s isa String + +@test jsonread(s, Term) == t + +generate_jsonschema_module(simpleast, ".") + +simpleast_schema = JSONSchema.Schema(read("simpleast_schema.json", String)) + +@test JSONSchema._validate(simpleast_schema, JSON.parse(s), "Term") === nothing + +@intertypes "model.it" module model + import ..simpleast +end + +using .model + +e = Equation(t, t) + +m = Model([:x], [e]) + +@test testjson(m) + +@intertypes "wgraph.it" module wgraph end + +using .wgraph + +g = EDWeightedGraph() +add_parts!(g, :V, 2) +add_part!(g, :E, src=1, tgt=2, weight=EdgeData(:mass_ave, 42)) + +@test testjson(m) + +generate_jsonschema_module(wgraph, ".") + +wgraph_schema = JSONSchema.Schema(read("wgraph_schema.json", String)) + +@test JSONSchema._validate(wgraph_schema, JSON.parse(jsonwrite(g)), "EDWeightedGraph") === nothing +=# \ No newline at end of file From 1c91145560ebe1b542842605113ca7ba4bd4ab7c Mon Sep 17 00:00:00 2001 From: p-stokes Date: Fri, 1 Dec 2023 11:58:21 -0500 Subject: [PATCH 02/35] Fixed typos in decapodes.it and updated intertype_examples.jl --- Project.toml | 1 + src/decapodes.it | 36 ++++++++++++++++++------------------ test/intertype_examples.jl | 10 +++++----- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Project.toml b/Project.toml index c3fd07f..0081594 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" diff --git a/src/decapodes.it b/src/decapodes.it index 10abb78..0f24493 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -10,30 +10,30 @@ end struct Judgement - var::Symbol - dim::Symbol - space::Symbol -end - -@sum Equation begin - Eq(lhs::Term, rhs::Term) -end + var::Symbol + dim::Symbol + space::Symbol + end + + @sum Equation begin + Eq(lhs::Term, rhs::Term) + end + + struct DecaExpr + context::Vector{Judgement} + equations::Vector{Equation} + end -struct DecaExpr - context::Vector{Judgement} - equations::Vector{Equation} -end struct Header - id::Union{String, Nothing} - description::String, - name::String, - model_version::String, + id::String + description::String + name::String + model_version::String schema::String schema_name::String end @sum ASKEMDeca begin - ASKEMDecaExpr(header::Header, model::DecaExpr, annotations::Vector{Annotation{Symbol,Symbol}}) - ASKEMDecapode(header::Header, model::SummationDecapode, annotations::Vector{Annotation{Symbol,Symbol}}) + ASKEMDecaExpr(header::Header, model::DecaExpr, annotations::Vector{amr.Annotation}) end \ No newline at end of file diff --git a/test/intertype_examples.jl b/test/intertype_examples.jl index 79dbc34..79cea3e 100644 --- a/test/intertype_examples.jl +++ b/test/intertype_examples.jl @@ -1,5 +1,5 @@ -using SyntacticModels +# using SyntacticModels using ACSets using ACSets.InterTypes @@ -7,12 +7,12 @@ using Test using OrderedCollections import JSON import JSON3 -import JSONSchema +# import JSONSchema -include("src/SyntacticModels.jl") +# include("src/SyntacticModels.jl") -using .SyntacticModels -# using .SyntacticModels.ASKEMDecapodes +# using .SyntacticModels +# # using .SyntacticModels.ASKEMDecapodes From 81d1d9cb44099e3e1277b6039a5c6203f684aa8b Mon Sep 17 00:00:00 2001 From: p-stokes Date: Fri, 1 Dec 2023 13:14:13 -0500 Subject: [PATCH 03/35] Removed redundant Header def from decapodes.it and added id field to amr.it version. Changed amr module name to AMR. --- src/amr.it | 1 + src/decapodes.it | 14 ++------------ test/intertype_examples.jl | 33 +++++++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/amr.it b/src/amr.it index 77bf525..ee19989 100644 --- a/src/amr.it +++ b/src/amr.it @@ -17,6 +17,7 @@ struct Annotation end struct Header + id::String name::String schema::String description::String diff --git a/src/decapodes.it b/src/decapodes.it index 0f24493..2841a9f 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -19,21 +19,11 @@ struct Judgement Eq(lhs::Term, rhs::Term) end - struct DecaExpr +struct DecaExpr context::Vector{Judgement} equations::Vector{Equation} - end - - -struct Header - id::String - description::String - name::String - model_version::String - schema::String - schema_name::String end @sum ASKEMDeca begin - ASKEMDecaExpr(header::Header, model::DecaExpr, annotations::Vector{amr.Annotation}) + ASKEMDecaExpr(header::AMR.Header, model::DecaExpr, annotations::Vector{AMR.Annotation}) end \ No newline at end of file diff --git a/test/intertype_examples.jl b/test/intertype_examples.jl index 79cea3e..72db2a5 100644 --- a/test/intertype_examples.jl +++ b/test/intertype_examples.jl @@ -13,7 +13,7 @@ import JSON3 # using .SyntacticModels # # using .SyntacticModels.ASKEMDecapodes - +using Decapodes using Reexport @@ -26,20 +26,45 @@ using StructTypes # using ..SyntacticModelsBase -@intertypes "../src/amr.it" module amr end +@intertypes "../src/amr.it" module AMR end -using .amr +using .AMR @intertypes "../src/decapodes.it" module decapodes - import ..amr + import ..AMR end using .decapodes +h = AMR.Header("", "harmonic_oscillator", + "modelreps.io/DecaExpr", + "A Simple Harmonic Oscillator as a Diagrammatic Equation", + "DecaExpr", + "v1.0") + +# The easiest way to write down a DecaExpr is in our DSL and calling the parser. +dexpr = Decapodes.parse_decapode(quote + X::Form0{Point} + V::Form0{Point} + + k::Constant{Point} + + ∂ₜ(X) == V + ∂ₜ(V) == -1*k*(X) +end +) + +annot = [AMR.Annotation(:X,:Form0,AMR.Name("The X variable."))] + +# Bundle the DecaExpr with the header metadata. +mexpr = decapodes.ASKEMDecaExpr(h, dexpr, annot) + + + #= @intertypes "simpleast.it" module simpleast end From 6f3df6fd7c21e8420a2fbeef34b8031c56d56052 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Fri, 1 Dec 2023 17:46:42 -0500 Subject: [PATCH 04/35] Added decapode parsing functions and test example for ASKEMDecaExpr with jsonread and jsonwrite --- src/decapodes.it | 20 +++++++------- test/intertype_examples.jl | 56 +++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/decapodes.it b/src/decapodes.it index 2841a9f..1d6dba9 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -10,20 +10,20 @@ end struct Judgement - var::Symbol - dim::Symbol - space::Symbol - end + var::Symbol + dim::Symbol + space::Symbol +end - @sum Equation begin - Eq(lhs::Term, rhs::Term) - end +@sum Equation begin + Eq(lhs::Term, rhs::Term) +end struct DecaExpr - context::Vector{Judgement} - equations::Vector{Equation} + context::Vector{Judgement} + equations::Vector{Equation} end @sum ASKEMDeca begin - ASKEMDecaExpr(header::AMR.Header, model::DecaExpr, annotations::Vector{AMR.Annotation}) + ASKEMDecaExpr(header::AMR.Header, model::DecaExpr, annotations::Vector{AMR.Annotation}) end \ No newline at end of file diff --git a/test/intertype_examples.jl b/test/intertype_examples.jl index 72db2a5..8f4df3f 100644 --- a/test/intertype_examples.jl +++ b/test/intertype_examples.jl @@ -47,7 +47,7 @@ h = AMR.Header("", "harmonic_oscillator", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = Decapodes.parse_decapode(quote +dexpr = my_parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -65,6 +65,60 @@ mexpr = decapodes.ASKEMDecaExpr(h, dexpr, annot) +my_term(s::Symbol) = decapodes.Var(normalize_unicode(s)) +my_term(s::Number) = decapodes.Lit(Symbol(s)) + +my_term(expr::Expr) = begin + @match expr begin + #TODO: Would we want ∂ₜ to be used with general expressions or just Vars? + Expr(:call, :∂ₜ, b) => decapodes.Tan(decapodes.Var(b)) + Expr(:call, :dt, b) => decapodes.Tan(decapodes.Var(b)) + + Expr(:call, Expr(:call, :∘, a...), b) => decapodes.AppCirc1(a, my_term(b)) + Expr(:call, a, b) => decapodes.App1(a, my_term(b)) + + Expr(:call, :+, xs...) => decapodes.Plus(my_term.(xs)) + Expr(:call, f, x, y) => decapodes.App2(f, my_term(x), my_term(y)) + + # TODO: Will later be converted to Op2's or schema has to be changed to include multiplication + Expr(:call, :*, xs...) => decapodes.Mult(my_term.(xs)) + + x => error("Cannot construct term from $x") + end +end + +function my_parse_decapode(expr::Expr) + stmts = map(expr.args) do line + @match line begin + ::LineNumberNode => missing + # TODO: If user doesn't provide space, this gives a temp space so we can continue to construction + # For now spaces don't matter so this is fine but if they do, this will need to change + Expr(:(::), a::Symbol, b::Symbol) => decapodes.Judgement(decapodes.Var(a).name, b, :I) + Expr(:(::), a::Expr, b::Symbol) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b, :I), a.args) + + Expr(:(::), a::Symbol, b) => decapodes.Judgement(decapodes.Var(a).name, b.args[1], b.args[2]) + Expr(:(::), a::Expr, b) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b.args[1], b.args[2]), a.args) + + Expr(:call, :(==), lhs, rhs) => decapodes.Eq(my_term(lhs), my_term(rhs)) + _ => error("The line $line is malformed") + end + end |> skipmissing |> collect + judges = [] + eqns = [] + foreach(stmts) do s + @match s begin + ::decapodes.Judgement => push!(judges, s) + ::Vector{decapodes.Judgement} => append!(judges, s) + ::decapodes.Eq => push!(eqns, s) + _ => error("Statement containing $s of type $(typeof(s)) was not added.") + end + end + decapodes.DecaExpr(judges, eqns) +end + +(mexpr == jsonread(jsonwrite(mexpr), decapodes.ASKEMDeca)) + + #= @intertypes "simpleast.it" module simpleast end From 84a37dcbf3e8e1ab95bc9372f0a9cbf0036a4008 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Mon, 4 Dec 2023 08:44:57 -0500 Subject: [PATCH 05/35] Started setting up src files to export the intertype structures. Started the uwd intertypes. --- src/amr.it | 48 +++ src/amr_it.jl | 391 ++++++++++++++++++ src/decapodes_it.jl | 61 +++ src/uwd.it | 11 + src/uwd_it.jl | 185 +++++++++ test/amr_it_ex.jl | 83 ++++ ...tertype_examples.jl => decapodes_it_ex.jl} | 0 test/uwd_it_ex.jl | 90 ++++ 8 files changed, 869 insertions(+) create mode 100644 src/amr_it.jl create mode 100644 src/decapodes_it.jl create mode 100644 src/uwd.it create mode 100644 src/uwd_it.jl create mode 100644 test/amr_it_ex.jl rename test/{intertype_examples.jl => decapodes_it_ex.jl} (100%) create mode 100644 test/uwd_it_ex.jl diff --git a/src/amr.it b/src/amr.it index ee19989..37aeca9 100644 --- a/src/amr.it +++ b/src/amr.it @@ -3,6 +3,47 @@ Presentation(str::String) end + +struct ExpressionFormula + expression::String + expression_mathml::MathML +end + +struct Unit + expression::String + expression_mathml::MathML +end + + +@sum Distribution begin + StandardUniform(str::String) + Uniform(min::Float64, max::Float64) + StandardNormal(str::String) + Normal(mean::Float64, variance::Float64) + PointMass(value::Float64) +end + +struct Observable + id::Symbol + name::String + states::Vector{Symbol} + f::ExpressionFormula +end + +@sum Expression begin + Rate(target::Symbol, f::ExpressionFormula) + Initial(target::Symbol, f::ExpressionFormula) + Parameter(id::Symbol, name::String, description::String, units::Unit, value::Float64, distribution::Distribution) + Time(id::Symbol, units::Unit) +end + +@sum Semantic begin + ODEList(statements::Vector{Expression}) + ODERecord(rates::Vector{Rate}, initials::Vector{Initial}, parameters::Vector{Parameter}, time::Time) + # Typing(system::ACSetSpec, map::Vector{Pair}) +end + + @sum Note begin Name(str::String) Description(str::String) @@ -24,3 +65,10 @@ struct Header schema_name::String model_version::String end + + +struct ASKEModel + header::Header + # model::ACSetSpec + semantics::Vector{Semantic} +end \ No newline at end of file diff --git a/src/amr_it.jl b/src/amr_it.jl new file mode 100644 index 0000000..0d16509 --- /dev/null +++ b/src/amr_it.jl @@ -0,0 +1,391 @@ +module AMR + +export Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, + Rate, Initial, Parameter, Time, + StandardUniform, Uniform, StandardNormal, Normal, PointMass, + Semantic, Header, ODERecord, ODEList, ASKEModel, # Typing, + distro_string, # amr_to_string, + Annotation, Note, Name, Description, Grounding, Units + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface +using StructTypes + +using ..SyntacticModelsBase + + +@intertypes "amr.it" module amr end + +using .amr + + +function distro_string(d::amr.Distribution) + @match d begin + amr.StandardUniform(s) => "U(0,1)" + amr.Uniform(min, max) => "U($min,$max)" + amr.StandardNormal(s) => "N(0,1)" + amr.Normal(mu, var) => "N($mu,$var)" + amr.PointMass(value) => "δ($value)" + end +end + +function distro_expr(d::amr.Distribution) + return Base.Meta.parse(distro_string(d)) +end + +#= + +function note_string(n::Note) + @match n begin + Name(n) => "Name($n)" + Description(d) => "Description($d)" + Grounding(ont, ident) => "Grounding($ont,$ident)" + Units(e) => "Units($e)" + end +end + +function note_expr(n::Note) + return Base.Meta.parse(note_string(n)) +end + +padlines(ss::Vector, n) = map(ss) do s + " "^n * s +end +padlines(s::String, n=2) = join(padlines(split(s, "\n"), n), "\n") +=# +function amr_to_string(amr′) + let ! = amr_to_string + @match amr′ begin + s::String => s + amr.Math(s) => !s + amr.Presentation(s) => " $s " + u::amr.Unit => !u.expression + d::amr.Distribution => distro_string(d) + amr.Time(id, u) => "$id::Time{$(!u)}\n" + amr.Rate(t, f) => "$t::Rate = $(f.expression)" + amr.Initial(t, f) => "$t::Initial = $(f.expression)" + amr.Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" + amr.Header(name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" + amr.Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" + m::ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" + amr.ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" + amr.ODERecord(rts, init, para, time) => join(vcat(["ODE_Record = begin\n"], !rts , !init, !para, [!time, "end"]), "\n") + vs::Vector{amr.Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") + vs::Vector{amr.Semantic} => join(map(!, vs), "\n\n") + xs::Vector => map(!, xs) + amr.Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" + amr.ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" + amr.Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_string(n))" + end + end +end +#= +block(exprs) = begin + q = :(begin + + end) + append!(q.args, exprs) + return q +end + +extract_acsetspec(s::String) = join(split(s, " ")[2:end], " ") |> Meta.parse + + +function amr_to_expr(amr) + let ! = amr_to_expr + @match amr begin + s::String => s + Math(s) => :(Math($(!s))) + Presentation(s) => :(Presentation($(!s))) + u::Unit => u.expression + d::Distribution => distro_expr(d) + Time(id, u) => :($id::Time{$(!u)}) + Rate(t, f) => :($t::Rate = $(f.expression)) + Initial(t, f) => :($t::Initial = $(f.expression)) + Observable(id, n, states, f) => begin "$n"; :(@doc $x $id::Observable = $(f.expression)($states)) end + Header(name, s, d, sn, mv) => begin x = "ASKE Model Representation: $name$mv :: $sn \n $s\n\n$d"; :(@doc $x) end + Parameter(t, n, d, u, v, dist) => begin x = "$n-- $d"; :(@doc $x $t::Parameter{$(!u)} = $v ~ $(!dist)) end + m::ACSetSpec => :(Model = begin $(extract_acsetspec(sprint(show, m))) end) + ODEList(l) => :(ODE_Equations = $(block(map(!, l)))) + ODERecord(rts, init, para, time) => :(ODE_Record = (rates=$(!rts), initials=$(!init), parameters=$(!para), time=!time)) + vs::Vector{Pair} => begin ys = map(vs) do v; :($(v[1]) => $(v[2])) end; block(ys) end + vs::Vector{Semantic} => begin ys = map(!, vs); block(ys) end + xs::Vector => begin ys = map(!, xs); block(ys) end + Typing(system, map) => :(Typing = $(!system); TypeMap = $(block(map))) + ASKEModel(h, m, s) => :($(!h);$(!m);$(!s)) + Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_expr(n))" + end + end +end + +optload(d, path, default=nothing) = begin + let ! = optload + @match path begin + s::Symbol => !(d, string(s), default) + s::String => get(d, s, default) + [addr] => !(d, addr, default) + [head, args...] => !(get(d, head, Dict()), args, default) + _=> error("Bad recursion in optload($d, $path)") + end + end +end + +function petrispec(dict::AbstractDict) + findkwarg(kwarg::Symbol, d::AbstractDict, path, default=nothing) = Kwarg(kwarg, Value(optload(d, path, default))) + loadstate(s) = begin + Statement(:S, [findkwarg(k, s, p, :nothing) for (k,p) in [(:id, "id"), (:name, "name"), (:units, ["units", "expression"])]]) + end + states = [loadstate(s) for s in dict["states"]] + transi = [ + Statement(:T, + [Kwarg(:id, Value(Symbol(t["id"]))), Kwarg(:name, Value(t["properties"]["name"])), Kwarg(:desc, Value(t["properties"]["description"])) ] + ) for t in dict["transitions"]] + + inputs = [[ + Statement(:I, + [Kwarg(:is, Value(i)), Kwarg(:it, Value(t["id"]))]) for i in t["input"]] for t in dict["transitions"] + ] |> Base.Flatten |> collect + outputs = [[ + Statement(:O, + [Kwarg(:os, Value(i)), Kwarg(:ot, Value(t["id"]))]) for i in t["output"]] for t in dict["transitions"] + ] |> Base.Flatten |> collect + ACSetSpec(:AMRPetriNet, vcat(states, transi, inputs, outputs)) +end + +function load(::Type{Unit}, d::AbstractDict) + ud = get(d, "units", Dict("expression"=>"", "expression_mathml"=>"")) + u = Unit(ud["expression"], Presentation(ud["expression_mathml"])) +end + +function load(::Type{Time}, t::AbstractDict) + Time(Symbol(t["id"]), load(Unit, t)) +end + +function load(::Type{Rate}, r::AbstractDict) + f = ExpressionFormula(r["expression"], Presentation(r["expression_mathml"])) + Rate(Symbol(r["target"]), f) +end + +function load(::Type{Initial}, d::AbstractDict) + f = ExpressionFormula(d["expression"], Presentation(d["expression_mathml"])) + Initial(Symbol(d["target"]), f) +end + +function load(::Type{Distribution}, d::AbstractDict) + @match d begin + Dict("type"=>"StandardUniform1") => StandardUniform + Dict("type"=>"StandardNormal") => StandardNormal + Dict("type"=>"Uniform", "parameters"=>p) => Uniform(p["minimum"], p["maximum"]) + Dict("type"=>"Uniform1", "parameters"=>p) => Uniform(p["minimum"], p["maximum"]) + Dict("type"=>"Normal", "parameters"=>p) => Normal(p["mu"], p["var"]) + Dict("type"=>"PointMass", "parameters"=>p) => PointMass(p["value"]) + end +end + +load(::Type{Distribution}, ::Nothing) = PointMass(missing) + +function load(::Type{Note}, d::AbstractDict) + @match d begin + Dict("type"=>"Name", "parameters"=>p) => Name(p["str"]) + Dict("type"=>"Description", "parameters"=>p) => Description(p["str"]) + Dict("type"=>"Grounding", "parameters"=>p) => Grounding(p["ontology"], p["identifier"]) + Dict("type"=>"Units", "parameters"=>p) => Units(p["expression"]) + end +end +function load(::Type{Annotation}, d::AbstractDict) + Annotation(d["entity"], d["type"], load(Note,d["note"])) +end + +function load(::Type{Parameter}, d::AbstractDict) + u = load(Unit, d) + Parameter( + Symbol(d["id"]), + d["name"], + d["description"], + u, + d["value"], + load(Distribution, get(d,"distribution", nothing)) + ) +end + +function load(::Type{ODERecord}, d::AbstractDict) + time = load(Time, d["time"]) + rate(x) = load(Rate, x) + initial(x) = load(Initial, x) + parameter(x) = load(Parameter, x) + rates = rate.(d["rates"]) + initials = initial.(d["initials"]) + parameters = parameter.(d["parameters"]) + + ODERecord(rates, initials, parameters, time) +end + +function load(::Type{Header}, d::AbstractDict) + @match d begin + Dict("name"=>n, "schema"=>s, "description"=>d, "schema_name"=>sn, "model_version"=>mv) => Header(n,s,d,sn,mv) + _ => error("Information for Header was not found in $d") + end +end + +function load(::Type{Typing}, d::AbstractDict) + @match d begin + Dict("type_system"=>s, "type_map"=>m) => begin @show m; Typing(petrispec(s), [x[1]=> x[2] for x in m]) end + _ => error("Typing judgement was not properly encoded in $d") + end +end + +function load(::Type{ASKEModel}, d::AbstractDict) + hdr = load(Header, d) + hdr.schema_name == "petrinet" || error("only petrinet models are supported") + mdl = petrispec(d["model"]) + sem = [] + if haskey(d["semantics"], "ode") + push!(sem, load(ODERecord, d["semantics"]["ode"])) + end + if haskey(d["semantics"], "typing") + push!(sem, load(Typing, d["semantics"]["typing"])) + end + ASKEModel(hdr, mdl, sem) +end + +using MLStyle.Modules.AST + +function load(::Type{Time}, ex::Expr) + @matchast ex quote + $a::Time{} => Time(a, Unit("", Math(""))) + $a::Time{$b} => Time(a, load(Unit, b)) + _ => error("Time was not properly encoded as Expr $ex") + end +end + +function load(::Type{Unit}, ex::Union{Symbol, Expr}) + Unit(string(ex), Math("")) +end + +function load(::Type{ExpressionFormula}, ex::Expr) + ExpressionFormula(string(ex), Math(string(ex))) +end + +function load(::Type{Rate}, ex::Expr) + @matchast ex quote + ($a::Rate = $ex) => Rate(a, load(ExpressionFormula, ex)) + ($a::Rate{$u} = $ex) => Rate(a, load(ExpressionFormula, ex)) + _ => error("Rate was not properly encoded as Expr $ex") + end +end + +function load(::Type{Initial}, ex::Expr) + @matchast ex quote + ($a::Initial = $ex) => Rate(a, load(ExpressionFormula, ex)) + ($a::Initial{$u} = $ex) => Rate(a, load(ExpressionFormula, ex)) + _ => error("Rate was not properly encoded as Expr $ex") + end +end + +function docval(exp::Expr) + s, ex = @match exp begin + Expr(:macrocall, var"@doc", _, s, ex) => (s,ex) + _ => error("Could not match documented value in $exp") + end + name, desc = split(s, "--") + return strip(name), strip(desc), ex +end + +function load(d::Type{Distribution}, ex::Expr) + @matchast ex quote + U(0,1) => StandardUniform + U($min,$max) => Uniform(min, max) + N(0,1) => StandardNormal + N($mu,$var) => Normal(mu, var) + δ($value) => PointMass(value) + _ => error("Failed to find distribution in $ex") + end +end + +function load(::Type{Parameter}, ex::Expr) + name, desc, ex = docval(ex) + id, u, val, dist = @matchast ex quote + ($id::Parameter{} = ($val ~ $d)) => (id, nounit, val, load(Distribution, d)) + ($id::Parameter{$u} = ($val ~ $d)) => (id, load(Unit, u), val, load(Distribution, d)) + ($id::Parameter{} = $val) => (id, nounit, val, PointMass(missing)) + ($id::Parameter{$u} = $val) => (id, load(Unit, u), val, PointMass(missing)) + end + Parameter(id, name, desc, u, val, dist) +end + +function load(::Type{ODEList}, ex::Expr) + map(ex.args[2].args) do arg + try + return load(Rate, arg) + catch ErrorException + try + return load(Initial, arg) + catch ErrorException + try + return load(Parameter, arg) + catch ErrorException + try + return load(Time, arg) + catch + return nothing + end + end + end + end + end |> x->filter(!isnothing, x) |> ODEList +end + +function load(::Type{Header}, ex::String) + hdr, schema, _, desc, _ = split(ex, "\n") + hdr, schema_name = split(hdr, "::") + _, hdr = split(hdr, ":") + name, version = split(hdr, "@") + Header(strip(name), strip(schema), strip(desc), strip(schema_name), strip(version)) +end + +function load(::Type{ACSetSpec}, ex::Expr) + let ! = x->load(ACSetSpec, x) + @match ex begin + Expr(:(=), name, body) => @match body.args[2] begin + Expr(:(=), type, body) => acsetspec(type, body) + end + Expr(:block, lnn, body) => !(body) + _ => ex + end + end +end + +function load(::Type{Typing}, ex::Expr) + let !(x) = load(Typing, x) + @match ex begin + Expr(:(=), :Model, body) => load(ACSetSpec, ex) + Expr(:(=), :TypeMap, list) => !list + Expr(:(=), :Typing, body) => Typing(!(body.args[2]), !(body.args[4])) + Expr(:vect, args...) => map(args) do arg + @match arg begin + Expr(:call, :(=>), a, b) => Pair(a,b) + _ => error("The type map is expected to be pairs defined with a => fa. Got $arg") + end + end + _ => error("Could not processing Typing assignment from $ex") + end + end +end + +function load(::Type{ASKEModel}, ex::Expr) + elts = map(ex.args) do arg + @match arg begin + Expr(:macrocall, var"@doc", _, s, ex) => (load(Header, s), load(ACSetSpec, ex)) + Expr(:(=), :ODE_Record, body) => load(ODEList, arg) + Expr(:(=), :ODE_Equations, body) => load(ODEList, arg) + Expr(:(=), :Typing, body) => load(Typing, arg) + _ => arg + end + end + ASKEModel(elts[2][1], elts[2][2], [elts[4], elts[6]]) +end +=# +end # module end \ No newline at end of file diff --git a/src/decapodes_it.jl b/src/decapodes_it.jl new file mode 100644 index 0000000..1217a31 --- /dev/null +++ b/src/decapodes_it.jl @@ -0,0 +1,61 @@ +module ASKEMDecapodes + +export ASKEMDecaExpr, ASKEMDecapode + +using ..SyntacticModelsBase +using ..AMR + +using StructTypes +using Decapodes +using MLStyle + +@data ASKEMDeca <: AbstractTerm begin + ASKEMDecaExpr(header::AMR.Header, model::Decapodes.DecaExpr, annotations::Vector{AMR.Annotation{Symbol,Symbol}}) + ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode, annotations::Vector{AMR.Annotation{Symbol,Symbol}}) +end + +@doc """ ASKEMDeca + +Stores a Decapode with the model metadata for ASKEM AMR conformance. +""" +ASKEMDeca + +@doc """ ASKEMDecaExpr + +Stores the syntactic expression of a Decapode Expression with the +model metadata for ASKEM AMR conformance. +""" +ASKEMDecaExpr(header::AMR.Header, model::Decapodes.DecaExpr) = ASKEMDecaExpr(header,model,Vector{AMR.Annotation{Symbol,Symbol}}()) + +@doc """ ASKEMDecapode + +Stores the combinatorial representation of a Decapode with the +model metadata for ASKEM AMR conformance. +""" +ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode) = ASKEMDecapode(header,model,Vector{AMR.Annotation{Symbol,Symbol}}()) + +StructTypes.StructType(::Type{ASKEMDeca}) = StructTypes.AbstractType() +StructTypes.subtypekey(::Type{ASKEMDeca}) = :_type +StructTypes.subtypes(::Type{ASKEMDeca}) = (ASKEMDecaExpr=ASKEMDecaExpr, ASKEMDecapode=ASKEMDecapode) + +SyntacticModelsBase._dict(x::T) where {T<:Union{Decapodes.DecaExpr, Decapodes.Equation, Decapodes.Term}} = begin + Dict(:_type => typename_last(T), [k=>_dict(getfield(x, k)) for k in fieldnames(T)]...) +end + +StructTypes.StructType(::Type{Decapodes.Equation}) = StructTypes.AbstractType() +StructTypes.subtypekey(::Type{Decapodes.Equation}) = :_type +StructTypes.subtypes(::Type{Decapodes.Equation}) = (Eq=Eq,) + +StructTypes.StructType(::Type{Decapodes.Term}) = StructTypes.AbstractType() +StructTypes.subtypekey(::Type{Decapodes.Term}) = :_type +StructTypes.subtypes(::Type{Decapodes.Term}) = (Var=Decapodes.Var, + Lit=Decapodes.Lit, + Judgement=Decapodes.Judgement, + AppCirc1=Decapodes.AppCirc1, + App1=Decapodes.App1, + App2=Decapodes.App2, + Plus=Decapodes.Plus, + Mult=Decapodes.Mult, + Tan=Decapodes.Tan) + +end \ No newline at end of file diff --git a/src/uwd.it b/src/uwd.it new file mode 100644 index 0000000..4c1c9d0 --- /dev/null +++ b/src/uwd.it @@ -0,0 +1,11 @@ + +@sum Var begin + Untyped(var::Symbol) + Typed(var::Symbol, type::Symbol) +end + +@sum UWDTerm begin + Statement(relation::Symbol, variables::Vector{Var}) + UWDExpr(context::Vector{Var}, statements::Vector{UWDTerm}) + UWDModel(header::AMR.Header, uwd::UWDTerm) +end \ No newline at end of file diff --git a/src/uwd_it.jl b/src/uwd_it.jl new file mode 100644 index 0000000..70b40aa --- /dev/null +++ b/src/uwd_it.jl @@ -0,0 +1,185 @@ +module ASKEMUWDs + +# include("amr.jl") +export uwd.Var, uwd.Typed, uwd.Untyped, uwd.Statement, uwd.UWDExpr, uwd.UWDModel, uwd.UWDTerm, context + +using ..SyntacticModelsBase +using ..AMR + +using MLStyle +using StructTypes +using Catlab +using Catlab.RelationalPrograms +using Catlab.WiringDiagrams +import Base: show + +using ACSets +using ACSets.InterTypes + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface + + +@intertypes "amr.it" module AMR end + +using .AMR + +@intertypes "uwd.it" module uwd + import ..AMR +end + +using .uwd + + +@doc """ Var + +Variables of a UWD. Types are the domain types, ScalarField, VectorField, Dual1Form, Primal2Form NOT Float64,Complex128 + +Subtypes include: + +1. Untyped(var::Symbol) +1. Typed(var::Symbol, type::Symbol) + +which are used for representing typed or untyped variables. +""" + +@doc """ UWDTerm + +Term specifying UWD. + +Subtypes +======== + +1. UWDModel: A header and UWD Expr +1. UWDExpr: A Context of variables and a list of statements defining a UWD +1. Statement: R(x,y,z) a relation that acts on its arguments (which are Vars) + +Example +======= + +To specify the following relation macro: +```julia +@relation (x:X, z:Z) where y:Y begin + R(x,y) + S(y,z) + T(z,y,u) +end +``` + +Use the following SyntacticModels UWDTerm: + +```julia +v1 = Typed(:x, :X) +v2 = Typed(:y, :Y) +v3 = Typed(:z, :Z) +v4 = Untyped(:u) +c = [v1, v3] +s = [Statement(:R, [v1,v2]), + Statement(:S, [v2,v3]), + Statement(:T, [v3,v2, v4])] +u = UWDExpr(c, s) +``` +""" + +varname(v::uwd.Var) = @match v begin + uwd.Untyped(v) => v + uwd.Typed(v, t) => v +end + +vartype(v::uwd.Var) = @match v begin + uwd.Typed(v, t) => t + uwd.Untyped(v) => :untyped +end + +context(t::uwd.UWDTerm) = @match t begin + uwd.Statement(R, xs) => xs + uwd.UWDExpr(context, statements) => context + uwd.UWDModel(h, uwd) => context(uwd) +end + +""" show(io::IO, s::UWDTerm) + +generates a human readable string of the `UWDTerm` (or any sub-term). +""" +function show(io::IO, s::uwd.UWDTerm) + let ! = show + @match s begin + uwd.Statement(r, v) => begin print(io, "$r("); show(io, v, wrap=false); print(io, ")") end + uwd.UWDExpr(c, body) => begin + map(enumerate(body)) do (i,s) + if i == 1 + print(io, "{ ") + show(io, s) + print(io, "\n") + elseif i == length(body) + print(io, " ") + show(io, s) + print(io, " }") + else + print(io, " ") + show(io, s) + print(io, "\n") + end + end + print(io, " where ") + show(io, c) + end + uwd.UWDModel(h, uwd′) => begin println(io, amr_to_string(h)); println(io, "UWD:"); !(io, uwd′); end + end + end +end + +function show(io::IO, c::Vector{uwd.Var}; wrap=true) + if wrap + print(io, "{") + end + map(enumerate(c)) do (i,s) + @match s begin + uwd.Untyped(v) => print(io, v) + uwd.Typed(v, T) => print(io, "$v:$T") + end + if i != length(c) + print(io, ", ") + end + end + if wrap + print(io, "}") + end +end + +""" construct(::Type{RelationDiagram}, ex::UWDExpr) + +Builds a RelationDiagram from a UWDExpr like the `@relation` macro does for Julia Exprs. +""" +function construct(::Type{RelationDiagram}, ex::uwd.UWDExpr) + # If you want to understand this code, look at the schema for Relation Diagrams + # to_graphviz(RelationalPrograms.SchRelationDiagram) + uwd = RelationDiagram(map(varname, ex.context)) + junctions = Dict() + # first we add in all the outer ports and make junctions for them. + for (i,j) in enumerate(ex.context) + k = add_part!(uwd, :Junction, variable=varname(j), junction_type=vartype(j)) + junctions[varname(j)] = k + set_subpart!(uwd, i, :outer_junction, k) + end + + # then for each statement we add a box, and its ports + for s in ex.statements + b = add_part!(uwd, :Box, name=s.relation) + for a in s.variables + # if a junction is missing, we have to add it. This is for nonexported variables + if !(varname(a) ∈ keys(junctions)) + k = add_part!(uwd, :Junction, variable=varname(a), junction_type=vartype(a)) + junctions[varname(a)] = k + end + # every port connects to the junction with the same variable name + add_part!(uwd, :Port, box=b, port_type=vartype(a), junction=junctions[varname(a)]) + end + end + return uwd +end + +end \ No newline at end of file diff --git a/test/amr_it_ex.jl b/test/amr_it_ex.jl new file mode 100644 index 0000000..0d95ff8 --- /dev/null +++ b/test/amr_it_ex.jl @@ -0,0 +1,83 @@ +module AMRExamples +using ..SyntacticModels.AMR +using Test +using ACSets +using ACSets.ADTs + +using ACSets.InterTypes +using Test +using OrderedCollections +import JSON +import JSON3 + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using StructTypes + + + +@intertypes "../src/amr.it" module AMR end + +using .AMR + + + +nomath = AMR.Math("") +header = AMR.Header("","SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") +model = acsetspec(:(LabelledPetriNet{Symbol}), quote + S(label=:S) + S(label=:I) + S(label=:R) + + T(label=:inf) + T(label=:rec) + + I(is=:S, it=:inf) + I(is=:I, it=:inf) + I(is=:I, it=:rec) + + O(os=:I, it=:inf) + O(os=:I, it=:inf) + O(os=:R, it=:rec) +end) + +ode = AMR.ODERecord([AMR.Rate(:inf, AMR.ExpressionFormula( "S*I*β", nomath)), + AMR.Rate(:rec, AMR.ExpressionFormula("I*γ", nomath))], + + [AMR.Initial(:S, AMR.ExpressionFormula("S₀", nomath)), + AMR.Initial(:I, AMR.ExpressionFormula("I₀", nomath)), + AMR.Initial(:R, AMR.ExpressionFormula("R₀", nomath)),], + + [AMR.Parameter(:β, "β", "the beta parameter", AMR.Unit("1/(persons^2*day)", nomath), 1e-2, AMR.Uniform(1e-3, 2e-2)), + AMR.Parameter(:γ, "γ", "the gama parameter", AMR.Unit("1/(persons*day)", nomath), 3, AMR.Uniform(1, 2e+2)), + + AMR.Parameter(:S₀, "S₀", "the initial susceptible population", AMR.Unit("persons", nomath), 300000000.0, AMR.Uniform(1e6, 4e6)), + AMR.Parameter(:I₀, "I₀", "the initial infected population", AMR.Unit("persons", nomath), 1.0, AMR.Uniform(1, 1)), + AMR.Parameter(:R₀, "R₀", "the initial recovered population", AMR.Unit("persons", nomath), 0.0, AMR.Uniform(0, 4)), + ], + AMR.Time(:t, AMR.Unit("day", nomath))) + +odelist = AMR.ODEList([ + AMR.Time(:t, AMR.Unit("day", nomath)), + AMR.Parameter(:β, "β", "the beta parameter", AMR.Unit("1/(persons^2*day)", nomath), 1e-2, AMR.Uniform(1e-3, 2e-2)), + AMR.Rate(:inf, AMR.ExpressionFormula("S*I*β", nomath)), + + AMR.Parameter(:γ, "γ", "the gama parameter", AMR.Unit("1/(persons*day)", nomath), 3, AMR.Uniform(1, 2e+2)), + AMR.Rate(:rec, AMR.ExpressionFormula("I*γ", nomath)), + + AMR.Parameter(:S₀, "S₀", "the initial susceptible population", AMR.Unit("persons", nomath), 300000000.0, AMR.Uniform(1e6, 4e6)), + AMR.Initial(:S₀, AMR.ExpressionFormula("S₀", nomath)), + + AMR.Parameter(:I₀, "I₀", "the initial infected population", AMR.Unit("persons", nomath), 1.0, AMR.Uniform(1, 1)), + AMR.Initial(:I₀, AMR.ExpressionFormula("I₀", nomath)), + + AMR.Parameter(:R₀, "R₀", "the initial recovered population", AMR.Unit("persons", nomath), 0.0, AMR.Uniform(0, 4)), + AMR.Initial(:R₀, AMR.ExpressionFormula("R₀", nomath)), + + ]) + +amr₁ = AMR.ASKEModel(header, + model, + [ode] +) diff --git a/test/intertype_examples.jl b/test/decapodes_it_ex.jl similarity index 100% rename from test/intertype_examples.jl rename to test/decapodes_it_ex.jl diff --git a/test/uwd_it_ex.jl b/test/uwd_it_ex.jl new file mode 100644 index 0000000..ec9d614 --- /dev/null +++ b/test/uwd_it_ex.jl @@ -0,0 +1,90 @@ +using Test +using JSON3 +using Catlab.RelationalPrograms +using Catlab.WiringDiagrams +using Catlab.Graphics + +using ACSets +using ACSets.InterTypes +using Test +using OrderedCollections +import JSON + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface +using StructTypes + +# using ..SyntacticModelsBase + + +@intertypes "../src/amr.it" module AMR end + +using .AMR + + + +@intertypes "../src/uwd.it" module uwd + import ..AMR +end + +using .uwd + + + +# This example follows what in current catlab would be given as + +#= +@relation (x:X, z:Z) where y:Y begin + R(x,y) + S(y,z) + T(z,y,u) +end +=# + +v1 = uwd.Typed(:x, :X) +v2 = uwd.Typed(:y, :Y) +v3 = uwd.Typed(:z, :Z) +v4 = uwd.Untyped(:u) +c = [v1, v3] +s = [uwd.Statement(:R, [v1,v2]), + uwd.Statement(:S, [v2,v3]), + uwd.Statement(:T, [v3,v2, v4])] +u = uwd.UWDExpr(c, s) + +@testset "UWDExpr Readback" begin + s = JSON3.write(u) + ujson = JSON3.read(s, uwd.UWDTerm) + # FIXME: can't compare u and ujson, because they aren't same object + # but they have the same JSON string + @test s == JSON3.write(ujson) +end + +uwd′ = construct(RelationDiagram, u) + + +h = AMR.Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") + +mexpr = uwd.UWDModel(h, u) +write_json_model(mexpr) +mexpr′ = readback(mexpr) +@testset "UWD Readback" begin + @test mexpr.header == mexpr′.header + @test mexpr.uwd.context == mexpr′.uwd.context + @test mexpr.uwd.context == mexpr′.uwd.context + @test mexpr.uwd.statements[1].relation == mexpr′.uwd.statements[1].relation + @test mexpr.uwd.statements[1].variables == mexpr′.uwd.statements[1].variables + @test mexpr.uwd.statements[2].relation == mexpr′.uwd.statements[2].relation + @test mexpr.uwd.statements[2].variables == mexpr′.uwd.statements[2].variables + @test mexpr.uwd.statements[3].relation == mexpr′.uwd.statements[3].relation + @test mexpr.uwd.statements[3].variables == mexpr′.uwd.statements[3].variables + # TODO: overload == for statements + # @test all(mexpr.uwd.statements .== mexpr′.uwd.statements) + # @test mexpr == mexpr′ +end + +to_graphviz(uwd′, box_labels=:name, junction_labels=:variable) + +display(uwd′) \ No newline at end of file From 361199c2531eb2205c6fb8029e71c2e0fb214a62 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Mon, 4 Dec 2023 11:07:46 -0500 Subject: [PATCH 06/35] uwd write and read test example working. needed to change write_json_model and readback in core_it.jl. readback currently won't work because intertypes parsing doesn't recognize the typeof variant sum types. --- test/core_it.jl | 38 ++++++++++++++++++++++++++++++++++++++ test/uwd_it_ex.jl | 23 +++++++++++++---------- 2 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 test/core_it.jl diff --git a/test/core_it.jl b/test/core_it.jl new file mode 100644 index 0000000..6a29262 --- /dev/null +++ b/test/core_it.jl @@ -0,0 +1,38 @@ +include("../src/SyntacticModels.jl") + +using .SyntacticModels +using .SyntacticModels.ASKEMDecapodes + +using Test +using JSON3 + +jsondir = joinpath(@__DIR__, "json") + +write_json_model(m, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp + JSON3.pretty(fp, jsonwrite(m)) +end + +readback(m, prefix=joinpath(@__DIR__, "json")) = jsonread(open(joinpath(jsondir, "$(m.header.name).json"), "r"),typeof(m)) + +#= +write_json_model(m::ASKEMDecapodes.ASKEMDecapode, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp + d = Dict("header"=>m.header, "model"=>generate_json_acset(m.model), "_type"=> "ASKEMDecapode") + JSON3.pretty(fp, d) +end + +sm_write_json_acset(X, fname, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(fname).json"), "w") do fp + JSON3.pretty(fp, generate_json_acset(X)) +end +=# + +try + mkdir(joinpath(@__DIR__, "json")) +catch + @info "JSON DIR already exists, you might want to rm -r it to clean up" +end + +include("amr_it_ex.jl") +include("decapodes_it_ex.jl") +include("uwd_it_ex.jl") +# include("composite_models_it_ex.jl") +# include("serialization_it_mwe.jl") \ No newline at end of file diff --git a/test/uwd_it_ex.jl b/test/uwd_it_ex.jl index ec9d614..3f02f7a 100644 --- a/test/uwd_it_ex.jl +++ b/test/uwd_it_ex.jl @@ -55,22 +55,25 @@ s = [uwd.Statement(:R, [v1,v2]), u = uwd.UWDExpr(c, s) @testset "UWDExpr Readback" begin - s = JSON3.write(u) - ujson = JSON3.read(s, uwd.UWDTerm) - # FIXME: can't compare u and ujson, because they aren't same object - # but they have the same JSON string - @test s == JSON3.write(ujson) + s = jsonwrite(u) + ujson = jsonread(s, uwd.UWDTerm) + @test u == jsonwrite(ujson) end + uwd′ = construct(RelationDiagram, u) h = AMR.Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") mexpr = uwd.UWDModel(h, u) -write_json_model(mexpr) -mexpr′ = readback(mexpr) @testset "UWD Readback" begin + write_json_model(mexpr) + # NOTE: because the intertype parsing does not recognize the subtypes of a sum type, readback won't currently work + # That is the reason for directly writing the UWDTerm type below. + # mexpr′ = readback(mexpr) + mexpr′ = jsonread(joinpath(joinpath(@__DIR__, "json"), "$(mexpr.header.name).json"), uwd.UWDTerm) + @test mexpr.header == mexpr′.header @test mexpr.uwd.context == mexpr′.uwd.context @test mexpr.uwd.context == mexpr′.uwd.context @@ -80,9 +83,9 @@ mexpr′ = readback(mexpr) @test mexpr.uwd.statements[2].variables == mexpr′.uwd.statements[2].variables @test mexpr.uwd.statements[3].relation == mexpr′.uwd.statements[3].relation @test mexpr.uwd.statements[3].variables == mexpr′.uwd.statements[3].variables - # TODO: overload == for statements - # @test all(mexpr.uwd.statements .== mexpr′.uwd.statements) - # @test mexpr == mexpr′ + + @test all(mexpr.uwd.statements .== mexpr′.uwd.statements) + @test mexpr == mexpr′ end to_graphviz(uwd′, box_labels=:name, junction_labels=:variable) From aa5cf1afce101b33ba3fdc8562e0d036463f6cef Mon Sep 17 00:00:00 2001 From: p-stokes Date: Mon, 4 Dec 2023 15:49:07 -0500 Subject: [PATCH 07/35] Added intertype version of SummationDecapode function and its helpers. Can now construct from an intertype version of DecaExpr. Still need to add SummationDecapode schema so it can be a field type for an ASKEMDecapode. --- src/decapodes.it | 1 + test/decapodes_it_ex.jl | 234 +++++++++++++++++++++++++++++++--------- 2 files changed, 182 insertions(+), 53 deletions(-) diff --git a/src/decapodes.it b/src/decapodes.it index 1d6dba9..79778bb 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -26,4 +26,5 @@ end @sum ASKEMDeca begin ASKEMDecaExpr(header::AMR.Header, model::DecaExpr, annotations::Vector{AMR.Annotation}) + # ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode, annotations::Vector{AMR.Annotation}) end \ No newline at end of file diff --git a/test/decapodes_it_ex.jl b/test/decapodes_it_ex.jl index 8f4df3f..bc3a5e4 100644 --- a/test/decapodes_it_ex.jl +++ b/test/decapodes_it_ex.jl @@ -15,6 +15,7 @@ import JSON3 # # using .SyntacticModels.ASKEMDecapodes using Decapodes +import Decapodes: recognize_types, make_sum_mult_unique! using Reexport @reexport using MLStyle @@ -39,32 +40,6 @@ end using .decapodes - -h = AMR.Header("", "harmonic_oscillator", - "modelreps.io/DecaExpr", - "A Simple Harmonic Oscillator as a Diagrammatic Equation", - "DecaExpr", - "v1.0") - -# The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = my_parse_decapode(quote - X::Form0{Point} - V::Form0{Point} - - k::Constant{Point} - - ∂ₜ(X) == V - ∂ₜ(V) == -1*k*(X) -end -) - -annot = [AMR.Annotation(:X,:Form0,AMR.Name("The X variable."))] - -# Bundle the DecaExpr with the header metadata. -mexpr = decapodes.ASKEMDecaExpr(h, dexpr, annot) - - - my_term(s::Symbol) = decapodes.Var(normalize_unicode(s)) my_term(s::Number) = decapodes.Lit(Symbol(s)) @@ -116,53 +91,206 @@ function my_parse_decapode(expr::Expr) decapodes.DecaExpr(judges, eqns) end -(mexpr == jsonread(jsonwrite(mexpr), decapodes.ASKEMDeca)) +h = AMR.Header("", "harmonic_oscillator", + "modelreps.io/DecaExpr", + "A Simple Harmonic Oscillator as a Diagrammatic Equation", + "DecaExpr", + "v1.0") +# The easiest way to write down a DecaExpr is in our DSL and calling the parser. +dexpr = my_parse_decapode(quote + X::Form0{Point} + V::Form0{Point} -#= -@intertypes "simpleast.it" module simpleast end + k::Constant{Point} -using .simpleast + ∂ₜ(X) == V + ∂ₜ(V) == -1*k*(X) +end +) + +annot = [AMR.Annotation(:X,:Form0,AMR.Name("The X variable."))] + +# Bundle the DecaExpr with the header metadata. +mexpr = decapodes.ASKEMDecaExpr(h, dexpr, annot) -t = Plus([Constant(ConstInt(1)), Constant(ConstInt(2))]) -s = jsonwrite(t) +# NOTE: the var in a Judgement in decapodes.it is just a Symbol. It does not have a name field, so that was removed +# to_decapode helper functions +reduce_term!(t::decapodes.Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) = + let ! = reduce_term! + @match t begin + decapodes.Var(x) => begin + if haskey(syms, x) + syms[x] + else + res_var = add_part!(d, :Var, name = x, type=:infer) + syms[x] = res_var + end + end + decapodes.Lit(x) => begin + if haskey(syms, x) + syms[x] + else + res_var = add_part!(d, :Var, name = x, type=:Literal) + syms[x] = res_var + end + end + decapodes.App1(f, t) || decapodes.AppCirc1(f, t) => begin + res_var = add_part!(d, :Var, type=:infer) + add_part!(d, :Op1, src=!(t,d,syms), tgt=res_var, op1=f) + return res_var + end + decapodes.App2(f, t1, t2) => begin + res_var = add_part!(d, :Var, type=:infer) + add_part!(d, :Op2, proj1=!(t1,d,syms), proj2=!(t2,d,syms), res=res_var, op2=f) + return res_var + end + decapodes.Plus(ts) => begin + summands = [!(t,d,syms) for t in ts] + res_var = add_part!(d, :Var, type=:infer, name=:sum) + n = add_part!(d, :Σ, sum=res_var) + map(summands) do s + add_part!(d, :Summand, summand=s, summation=n) + end + return res_var + end + # TODO: Just for now assuming we have 2 or more terms + decapodes.Mult(ts) => begin + multiplicands = [!(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 + decapodes.Tan(t) => begin + # TODO: this is creating a spurious variable with the same name + txv = add_part!(d, :Var, type=:infer) + tx = add_part!(d, :TVar, incl=txv) + tanop = add_part!(d, :Op1, src=!(t,d,syms), tgt=txv, op1=DerivOp) + return txv #syms[x[1]] + end + _ => throw("Inline type judgements not yet supported!") + end + end + +function eval_eq!(eq::decapodes.Eq, d::AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) + @match eq begin + decapodes.Eq(t1, t2) => begin + lhs_ref = reduce_term!(t1,d,syms) + rhs_ref = reduce_term!(t2,d,syms) + + # Always let the a named variable take precedence + # TODO: If we have variable to variable equality, we want + # some kind of way to check track of this equality + ref_pair = (t1, t2) + @match ref_pair begin + (decapodes.Var(a), decapodes.Var(b)) => return d + (t1, decapodes.Var(b)) => begin + lhs_ref, rhs_ref = rhs_ref, lhs_ref + end + _ => nothing + end + + # Make rhs_ref equal to lhs_ref and adjust all its incidents -@test s isa String + # Case rhs_ref is a Tan + # WARNING: Don't push to deletion here because all TanVars should have a + # corresponding Op1. Pushing here would create a duplicate which breaks rem_parts! + for rhs in incident(d, rhs_ref, :incl) + d[rhs, :incl] = lhs_ref + end + # Case rhs_ref is a Op1 + for rhs in incident(d, rhs_ref, :tgt) + d[rhs, :tgt] = lhs_ref + push!(deletions, rhs_ref) + end + # Case rhs_ref is a Op2 + for rhs in incident(d, rhs_ref, :res) + d[rhs, :res] = lhs_ref + push!(deletions, rhs_ref) + end + # Case rhs_ref is a Plus + # FIXME: this typeguard is a subsitute for refactoring into multiple dispatch + if isa(d, SummationDecapode) + for rhs in incident(d, rhs_ref, :sum) + d[rhs, :sum] = lhs_ref + push!(deletions, rhs_ref) + end + end + # TODO: delete unused vars. The only thing stopping me from doing + # this is I don't know if CSet deletion preserves incident relations + #rem_parts!(d, :Var, sort(deletions)) + end + end + return d +end -@test jsonread(s, Term) == t +function SummationDecapode(e::decapodes.DecaExpr) + d = SummationDecapode{Any, Any, Symbol}() + symbol_table = Dict{Symbol, Int}() -generate_jsonschema_module(simpleast, ".") + for judgement in e.context + var_id = add_part!(d, :Var, name=judgement.var, type=judgement.dim) + symbol_table[judgement.var] = var_id + end -simpleast_schema = JSONSchema.Schema(read("simpleast_schema.json", String)) + deletions = Vector{Int}() + for eq in e.equations + eval_eq!(eq, d, symbol_table, deletions) + end + rem_parts!(d, :Var, sort(deletions)) -@test JSONSchema._validate(simpleast_schema, JSON.parse(s), "Term") === nothing + recognize_types(d) -@intertypes "model.it" module model - import ..simpleast + fill_names!(d) + d[:name] = normalize_unicode.(d[:name]) + make_sum_mult_unique!(d) + return d end -using .model -e = Equation(t, t) +# Convert a the DecaExpr to a SummationDecapode which is the +# combinatorial representation. The converter lives in Decapodes/src/language.jl. -m = Model([:x], [e]) +d = SummationDecapode(mexpr.model) -@test testjson(m) +# We want different metadata for this representation. +# The Summation prefix just means that this decapodes have +# specialized support for the handling of summation. +# The summation operator happens in physics so often, +# that you want to bake in some specialized handling to the data structure. + +h = AMR.Header("","harmonic_oscillator", + "modelreps.io/SummationDecapode", + "A Simple Harmonic Oscillator as a Diagrammatic Equation", + "SummationDecapode", + "v1.0") +mpode = ASKEMDecapode(h, d, annot) -@intertypes "wgraph.it" module wgraph end -using .wgraph +# The syntactic representation can be serialized as JSON. +# The resulting structure is like a parse tree of the syntactic +# representation of the DecaExpr +write_json_model(mexpr) -g = EDWeightedGraph() -add_parts!(g, :V, 2) -add_part!(g, :E, src=1, tgt=2, weight=EdgeData(:mass_ave, 42)) +# We could also use the JSON serialization built into Catlab +# to serialize the resulting combinatorial representation +sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") +# end -@test testjson(m) -generate_jsonschema_module(wgraph, ".") + # Can we read back the models we just wrote? +@testset "Decapodes Readback" begin + mexpr′ = readback(mexpr) + @test JSON3.write(mexpr) == JSON3.write(mexpr′) +end -wgraph_schema = JSONSchema.Schema(read("wgraph_schema.json", String)) -@test JSONSchema._validate(wgraph_schema, JSON.parse(jsonwrite(g)), "EDWeightedGraph") === nothing -=# \ No newline at end of file +(mexpr == jsonread(jsonwrite(mexpr), decapodes.ASKEMDeca)) From cd83f9d7f78af7556496d8558a16e811cc7bf8c4 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Mon, 4 Dec 2023 20:24:55 -0500 Subject: [PATCH 08/35] Added intertype structs for SummationDecapode. It will load the intertype module but the SummationDecapode formation function is not yet producing an intertype one. --- src/decapodes.it | 42 ++++++++++++++++++++++++++++++++++++++++- test/decapodes_it_ex.jl | 2 +- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/decapodes.it b/src/decapodes.it index 79778bb..05a174a 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -24,7 +24,47 @@ struct DecaExpr equations::Vector{Equation} end +@schema SchDecapode begin + (Var, TVar, Op1, Op2)::Ob + (Type, Operator)::AttrType(Symbol) + src::Hom(Op1, Var) + tgt::Hom(Op1, Var) + proj1::Hom(Op2, Var) + proj2::Hom(Op2, Var) + res::Hom(Op2, Var) + incl::Hom(TVar, Var) + + op1::Attr(Op1, Operator) + op2::Attr(Op2, Operator) + type::Attr(Var, Type) +end + +@schema SchNamedDecapode <: SchDecapode begin + Name::AttrType(Symbol) + name::Attr(Var, Name) +end + + +@abstract_acset_type AbstractDecapode +@abstract_acset_type AbstractNamedDecapode <: AbstractDecapode + +@acset_type Decapode(SchDecapode, + index=[:src, :tgt, :res, :incl, :op1, :op2, :type]) <: AbstractDecapode + +@acset_type NamedDecapode(SchNamedDecapode, + index=[:src, :tgt, :res, :incl, :op1, :op2, :type, :name]) <: AbstractNamedDecapode + +@schema SchSummationDecapode <: SchNamedDecapode begin + (Σ, Summand)::Ob + summand::Hom(Summand, Var) + summation::Hom(Summand, Σ) + sum::Hom(Σ, Var) +end + +@acset_type SummationDecapode(SchSummationDecapode, + index=[:src, :tgt, :res, :incl, :op1, :op2, :type]) <: AbstractNamedDecapode + @sum ASKEMDeca begin ASKEMDecaExpr(header::AMR.Header, model::DecaExpr, annotations::Vector{AMR.Annotation}) - # ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode, annotations::Vector{AMR.Annotation}) + ASKEMDecapode(header::AMR.Header, model::SummationDecapode, annotations::Vector{AMR.Annotation}) end \ No newline at end of file diff --git a/test/decapodes_it_ex.jl b/test/decapodes_it_ex.jl index bc3a5e4..0e03215 100644 --- a/test/decapodes_it_ex.jl +++ b/test/decapodes_it_ex.jl @@ -272,7 +272,7 @@ h = AMR.Header("","harmonic_oscillator", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "SummationDecapode", "v1.0") -mpode = ASKEMDecapode(h, d, annot) +mpode = decapodes.ASKEMDecapode(h, d, annot) # The syntactic representation can be serialized as JSON. From 692a242cd7900cc5bee34d76b5431fd7e06658bc Mon Sep 17 00:00:00 2001 From: p-stokes Date: Tue, 5 Dec 2023 00:55:57 -0500 Subject: [PATCH 09/35] Added/updated functions to make intertype SummationDecapode. No longer uses Decapodes.jl. Copied in a few utilities from there. Edited readback function in core_it.jl --- src/decapodes.it | 4 +-- test/core_it.jl | 4 +-- test/decapodes_it_ex.jl | 65 +++++++++++++++++++++++++++++++++-------- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/decapodes.it b/src/decapodes.it index 05a174a..6daba07 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -61,10 +61,10 @@ end sum::Hom(Σ, Var) end -@acset_type SummationDecapode(SchSummationDecapode, +@acset_type SymSummationDecapode(SchSummationDecapode, generic=SummationDecapode, index=[:src, :tgt, :res, :incl, :op1, :op2, :type]) <: AbstractNamedDecapode @sum ASKEMDeca begin ASKEMDecaExpr(header::AMR.Header, model::DecaExpr, annotations::Vector{AMR.Annotation}) - ASKEMDecapode(header::AMR.Header, model::SummationDecapode, annotations::Vector{AMR.Annotation}) + ASKEMDecapode(header::AMR.Header, model::SymSummationDecapode, annotations::Vector{AMR.Annotation}) end \ No newline at end of file diff --git a/test/core_it.jl b/test/core_it.jl index 6a29262..dc76616 100644 --- a/test/core_it.jl +++ b/test/core_it.jl @@ -12,8 +12,8 @@ write_json_model(m, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, " JSON3.pretty(fp, jsonwrite(m)) end -readback(m, prefix=joinpath(@__DIR__, "json")) = jsonread(open(joinpath(jsondir, "$(m.header.name).json"), "r"),typeof(m)) - +readback(m, T, prefix=joinpath(@__DIR__, "json")) = jsonread(joinpath(jsondir, "$(m.header.name).json"),T) + #= write_json_model(m::ASKEMDecapodes.ASKEMDecapode, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp d = Dict("header"=>m.header, "model"=>generate_json_acset(m.model), "_type"=> "ASKEMDecapode") diff --git a/test/decapodes_it_ex.jl b/test/decapodes_it_ex.jl index 0e03215..51a71fe 100644 --- a/test/decapodes_it_ex.jl +++ b/test/decapodes_it_ex.jl @@ -13,9 +13,9 @@ import JSON3 # using .SyntacticModels # # using .SyntacticModels.ASKEMDecapodes -using Decapodes -import Decapodes: recognize_types, make_sum_mult_unique! +# using Decapodes +# import Decapodes: recognize_types, make_sum_mult_unique! using Reexport @reexport using MLStyle @@ -39,6 +39,11 @@ end using .decapodes +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') my_term(s::Symbol) = decapodes.Var(normalize_unicode(s)) my_term(s::Number) = decapodes.Lit(Symbol(s)) @@ -117,7 +122,7 @@ mexpr = decapodes.ASKEMDecaExpr(h, dexpr, annot) # NOTE: the var in a Judgement in decapodes.it is just a Symbol. It does not have a name field, so that was removed # to_decapode helper functions -reduce_term!(t::decapodes.Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) = +reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}) = let ! = reduce_term! @match t begin decapodes.Var(x) => begin @@ -180,7 +185,7 @@ reduce_term!(t::decapodes.Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) = end end -function eval_eq!(eq::decapodes.Eq, d::AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) +function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) @match eq begin decapodes.Eq(t1, t2) => begin lhs_ref = reduce_term!(t1,d,syms) @@ -218,7 +223,7 @@ function eval_eq!(eq::decapodes.Eq, d::AbstractDecapode, syms::Dict{Symbol, Int} end # Case rhs_ref is a Plus # FIXME: this typeguard is a subsitute for refactoring into multiple dispatch - if isa(d, SummationDecapode) + if isa(d, decapodes.SummationDecapode) for rhs in incident(d, rhs_ref, :sum) d[rhs, :sum] = lhs_ref push!(deletions, rhs_ref) @@ -232,8 +237,48 @@ function eval_eq!(eq::decapodes.Eq, d::AbstractDecapode, syms::Dict{Symbol, Int} return d end +function recognize_types(d::decapodes.AbstractNamedDecapode) + unrecognized_types = setdiff(d[:type], [:Form0, :Form1, :Form2, :DualForm0, + :DualForm1, :DualForm2, :Literal, :Parameter, + :Constant, :infer]) + isempty(unrecognized_types) || + error("Types $unrecognized_types are not recognized.") +end + +function fill_names!(d::decapodes.AbstractNamedDecapode) + bulletcount = 1 + for i in parts(d, :Var) + if !isassigned(d[:,:name],i) || isnothing(d[i, :name]) + d[i,:name] = Symbol("•$bulletcount") + bulletcount += 1 + end + end + for e in incident(d, :∂ₜ, :op1) + s = d[e,:src] + t = d[e, :tgt] + String(d[t,:name])[1] != '•' && continue + d[t, :name] = append_dot(d[s,:name]) + end + d +end + +function make_sum_mult_unique!(d::decapodes.AbstractNamedDecapode) + snum = 1 + mnum = 1 + for (i, name) in enumerate(d[:name]) + if(name == :sum) + d[i, :name] = Symbol("sum_$(snum)") + snum += 1 + elseif(name == :mult) + d[i, :name] = Symbol("mult_$(mnum)") + mnum += 1 + end + end +end + function SummationDecapode(e::decapodes.DecaExpr) - d = SummationDecapode{Any, Any, Symbol}() + # d = SummationDecapode{Any, Any, Symbol}() + d = decapodes.SummationDecapode{Symbol, Symbol, Symbol}() symbol_table = Dict{Symbol, Int}() for judgement in e.context @@ -282,15 +327,11 @@ write_json_model(mexpr) # We could also use the JSON serialization built into Catlab # to serialize the resulting combinatorial representation -sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") -# end +# sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") # Can we read back the models we just wrote? @testset "Decapodes Readback" begin - mexpr′ = readback(mexpr) + mexpr′ = readback(mexpr,decapodes.ASKEMDeca) @test JSON3.write(mexpr) == JSON3.write(mexpr′) end - - -(mexpr == jsonread(jsonwrite(mexpr), decapodes.ASKEMDeca)) From 2d073e3e1d02b3093e512b26826385226d1293fa Mon Sep 17 00:00:00 2001 From: p-stokes Date: Tue, 5 Dec 2023 13:24:14 -0500 Subject: [PATCH 10/35] Started factoring amr and uwd parts into source and test files, and updating the module and runtest files accordingly. Had to change the amr.it name within uwd.it from AMR to amr. --- src/SyntacticModels.jl | 8 ++++---- src/amr_it.jl | 10 +++++----- src/uwd.it | 2 +- src/uwd_it.jl | 14 ++++++++++---- test/amr_it_ex.jl | 8 ++++++-- test/core_it.jl | 4 ++-- test/runtests.jl | 2 +- test/uwd_it_ex.jl | 41 +++++++++++++++++++++-------------------- 8 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/SyntacticModels.jl b/src/SyntacticModels.jl index a996cdf..d907ddf 100644 --- a/src/SyntacticModels.jl +++ b/src/SyntacticModels.jl @@ -1,9 +1,9 @@ module SyntacticModels include("SyntacticModelsBase.jl") -include("amr.jl") -include("decapodes.jl") -include("uwd.jl") -include("composite_models.jl") +include("amr_it.jl") +# include("decapodes_it.jl") +include("uwd_it.jl") +# include("composite_models_it.jl") end \ No newline at end of file diff --git a/src/amr_it.jl b/src/amr_it.jl index 0d16509..8c88717 100644 --- a/src/amr_it.jl +++ b/src/amr_it.jl @@ -1,6 +1,6 @@ module AMR -export Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, +export amr, Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, Rate, Initial, Parameter, Time, StandardUniform, Uniform, StandardNormal, Normal, PointMass, Semantic, Header, ODERecord, ODEList, ASKEModel, # Typing, @@ -14,7 +14,7 @@ using ACSets.ADTs using ACSets.ACSetInterface using StructTypes -using ..SyntacticModelsBase +# using ..SyntacticModelsBase @intertypes "amr.it" module amr end @@ -68,7 +68,7 @@ function amr_to_string(amr′) amr.Rate(t, f) => "$t::Rate = $(f.expression)" amr.Initial(t, f) => "$t::Initial = $(f.expression)" amr.Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" - amr.Header(name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" + amr.Header(id, name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" amr.Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" m::ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" amr.ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" @@ -76,8 +76,8 @@ function amr_to_string(amr′) vs::Vector{amr.Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") vs::Vector{amr.Semantic} => join(map(!, vs), "\n\n") xs::Vector => map(!, xs) - amr.Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" - amr.ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" + # amr.Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" + # amr.ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" amr.Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_string(n))" end end diff --git a/src/uwd.it b/src/uwd.it index 4c1c9d0..787570e 100644 --- a/src/uwd.it +++ b/src/uwd.it @@ -7,5 +7,5 @@ end @sum UWDTerm begin Statement(relation::Symbol, variables::Vector{Var}) UWDExpr(context::Vector{Var}, statements::Vector{UWDTerm}) - UWDModel(header::AMR.Header, uwd::UWDTerm) + UWDModel(header::amr.Header, uwd::UWDTerm) end \ No newline at end of file diff --git a/src/uwd_it.jl b/src/uwd_it.jl index 70b40aa..85066f9 100644 --- a/src/uwd_it.jl +++ b/src/uwd_it.jl @@ -1,9 +1,9 @@ module ASKEMUWDs # include("amr.jl") -export uwd.Var, uwd.Typed, uwd.Untyped, uwd.Statement, uwd.UWDExpr, uwd.UWDModel, uwd.UWDTerm, context +export Var, Typed, Untyped, Statement, UWDExpr, UWDModel, UWDTerm, context -using ..SyntacticModelsBase +# using ..SyntacticModelsBase using ..AMR using MLStyle @@ -11,7 +11,7 @@ using StructTypes using Catlab using Catlab.RelationalPrograms using Catlab.WiringDiagrams -import Base: show +# import Base: show using ACSets using ACSets.InterTypes @@ -23,12 +23,16 @@ using ACSets.ADTs using ACSets.ACSetInterface +#= @intertypes "amr.it" module AMR end using .AMR +=# + +using ..AMR.amr @intertypes "uwd.it" module uwd - import ..AMR + import ..amr end using .uwd @@ -104,6 +108,7 @@ end generates a human readable string of the `UWDTerm` (or any sub-term). """ +#= function show(io::IO, s::uwd.UWDTerm) let ! = show @match s begin @@ -149,6 +154,7 @@ function show(io::IO, c::Vector{uwd.Var}; wrap=true) print(io, "}") end end +=# """ construct(::Type{RelationDiagram}, ex::UWDExpr) diff --git a/test/amr_it_ex.jl b/test/amr_it_ex.jl index 0d95ff8..b17aaf8 100644 --- a/test/amr_it_ex.jl +++ b/test/amr_it_ex.jl @@ -16,11 +16,11 @@ using Reexport using StructTypes - +#= @intertypes "../src/amr.it" module AMR end using .AMR - +=# nomath = AMR.Math("") @@ -77,7 +77,11 @@ odelist = AMR.ODEList([ ]) +#= amr₁ = AMR.ASKEModel(header, model, [ode] ) +=# + +end # module \ No newline at end of file diff --git a/test/core_it.jl b/test/core_it.jl index dc76616..852577a 100644 --- a/test/core_it.jl +++ b/test/core_it.jl @@ -1,7 +1,7 @@ include("../src/SyntacticModels.jl") using .SyntacticModels -using .SyntacticModels.ASKEMDecapodes +# using .SyntacticModels.ASKEMDecapodes using Test using JSON3 @@ -32,7 +32,7 @@ catch end include("amr_it_ex.jl") -include("decapodes_it_ex.jl") +# include("decapodes_it_ex.jl") include("uwd_it_ex.jl") # include("composite_models_it_ex.jl") # include("serialization_it_mwe.jl") \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 6a3ff72..dfb961c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,5 +3,5 @@ using Test using SyntacticModels @testset "Core" begin - include("core.jl") + include("core_it.jl") end diff --git a/test/uwd_it_ex.jl b/test/uwd_it_ex.jl index 3f02f7a..3f381a2 100644 --- a/test/uwd_it_ex.jl +++ b/test/uwd_it_ex.jl @@ -19,20 +19,21 @@ using StructTypes # using ..SyntacticModelsBase +#= +@intertypes "../src/amr.it" module amr end -@intertypes "../src/amr.it" module AMR end - -using .AMR +using .amr @intertypes "../src/uwd.it" module uwd - import ..AMR + import ..amr end using .uwd - - +=# +using ..SyntacticModels.AMR +using ..SyntacticModels.ASKEMUWDs # This example follows what in current catlab would be given as @@ -44,35 +45,35 @@ using .uwd end =# -v1 = uwd.Typed(:x, :X) -v2 = uwd.Typed(:y, :Y) -v3 = uwd.Typed(:z, :Z) -v4 = uwd.Untyped(:u) +v1 = ASKEMUWDs.uwd.Typed(:x, :X) +v2 = ASKEMUWDs.uwd.Typed(:y, :Y) +v3 = ASKEMUWDs.uwd.Typed(:z, :Z) +v4 = ASKEMUWDs.uwd.Untyped(:u) c = [v1, v3] -s = [uwd.Statement(:R, [v1,v2]), - uwd.Statement(:S, [v2,v3]), - uwd.Statement(:T, [v3,v2, v4])] -u = uwd.UWDExpr(c, s) +s = [ASKEMUWDs.uwd.Statement(:R, [v1,v2]), + ASKEMUWDs.uwd.Statement(:S, [v2,v3]), + ASKEMUWDs.uwd.Statement(:T, [v3,v2, v4])] +u = ASKEMUWDs.uwd.UWDExpr(c, s) @testset "UWDExpr Readback" begin s = jsonwrite(u) - ujson = jsonread(s, uwd.UWDTerm) - @test u == jsonwrite(ujson) + ujson = jsonread(s, ASKEMUWDs.uwd.UWDTerm) + @test s == jsonwrite(ujson) end -uwd′ = construct(RelationDiagram, u) +uwd′ = ASKEMUWDs.construct(RelationDiagram, u) h = AMR.Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") -mexpr = uwd.UWDModel(h, u) +mexpr = ASKEMUWDs.uwd.UWDModel(h, u) @testset "UWD Readback" begin write_json_model(mexpr) # NOTE: because the intertype parsing does not recognize the subtypes of a sum type, readback won't currently work # That is the reason for directly writing the UWDTerm type below. - # mexpr′ = readback(mexpr) - mexpr′ = jsonread(joinpath(joinpath(@__DIR__, "json"), "$(mexpr.header.name).json"), uwd.UWDTerm) + mexpr′ = readback(mexpr,ASKEMUWDs.uwd.UWDTerm) + # mexpr′ = jsonread(joinpath(joinpath(@__DIR__, "json"), "$(mexpr.header.name).json"), ASKEMUWDs.UWDTerm) @test mexpr.header == mexpr′.header @test mexpr.uwd.context == mexpr′.uwd.context From a4b61afb5667498c15ddbc760fd0ba67f0b6c546 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Tue, 5 Dec 2023 13:35:08 -0500 Subject: [PATCH 11/35] Updated Project.toml --- test/Project.toml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/Project.toml b/test/Project.toml index e1e26b3..e36fc96 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,11 +1,13 @@ [deps] -SyntacticModels = "22bb929c-8bcf-4852-b455-eb3e1675e09c" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" +Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" +Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" -ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" -Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" -Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391" -Reexport = "189a3867-3050-52da-a836-e630ba90ab69" \ No newline at end of file +SyntacticModels = "22bb929c-8bcf-4852-b455-eb3e1675e09c" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From c0a34fbbc0c17ce8a4ad1f70f38f61c19b2eabfc Mon Sep 17 00:00:00 2001 From: p-stokes Date: Tue, 5 Dec 2023 14:58:53 -0500 Subject: [PATCH 12/35] Factored decapodes part into src and test. Again had to change AMR name to amr. Changed my_parse_decapode function name as well. --- Project.toml | 1 + src/SyntacticModels.jl | 2 +- src/decapodes.it | 4 +- src/decapodes_it.jl | 289 ++++++++++++++++++++++++++++++++++------ test/Project.toml | 1 + test/core_it.jl | 2 +- test/decapodes_it_ex.jl | 258 ++--------------------------------- 7 files changed, 268 insertions(+), 289 deletions(-) diff --git a/Project.toml b/Project.toml index 0081594..a177425 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" +Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [compat] ACSets = "0.2" diff --git a/src/SyntacticModels.jl b/src/SyntacticModels.jl index d907ddf..87426f1 100644 --- a/src/SyntacticModels.jl +++ b/src/SyntacticModels.jl @@ -2,7 +2,7 @@ module SyntacticModels include("SyntacticModelsBase.jl") include("amr_it.jl") -# include("decapodes_it.jl") +include("decapodes_it.jl") include("uwd_it.jl") # include("composite_models_it.jl") diff --git a/src/decapodes.it b/src/decapodes.it index 6daba07..31d0229 100644 --- a/src/decapodes.it +++ b/src/decapodes.it @@ -65,6 +65,6 @@ end index=[:src, :tgt, :res, :incl, :op1, :op2, :type]) <: AbstractNamedDecapode @sum ASKEMDeca begin - ASKEMDecaExpr(header::AMR.Header, model::DecaExpr, annotations::Vector{AMR.Annotation}) - ASKEMDecapode(header::AMR.Header, model::SymSummationDecapode, annotations::Vector{AMR.Annotation}) + ASKEMDecaExpr(header::amr.Header, model::DecaExpr, annotations::Vector{amr.Annotation}) + ASKEMDecapode(header::amr.Header, model::SymSummationDecapode, annotations::Vector{amr.Annotation}) end \ No newline at end of file diff --git a/src/decapodes_it.jl b/src/decapodes_it.jl index 1217a31..f757620 100644 --- a/src/decapodes_it.jl +++ b/src/decapodes_it.jl @@ -1,61 +1,272 @@ module ASKEMDecapodes -export ASKEMDecaExpr, ASKEMDecapode +export ASKEMDecaExpr, ASKEMDecapode, ASKEMDeca, SummationDecapode, parse_decapode -using ..SyntacticModelsBase +# using ..SyntacticModelsBase using ..AMR using StructTypes -using Decapodes +# using Decapodes using MLStyle -@data ASKEMDeca <: AbstractTerm begin - ASKEMDecaExpr(header::AMR.Header, model::Decapodes.DecaExpr, annotations::Vector{AMR.Annotation{Symbol,Symbol}}) - ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode, annotations::Vector{AMR.Annotation{Symbol,Symbol}}) +using ACSets +using ACSets.InterTypes + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface + +import Unicode + +using ..AMR.amr + +@intertypes "decapodes.it" module decapodes + import ..amr +end + +using .decapodes + + +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') + +term(s::Symbol) = decapodes.Var(normalize_unicode(s)) +term(s::Number) = decapodes.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) => decapodes.Tan(decapodes.Var(b)) + Expr(:call, :dt, b) => decapodes.Tan(decapodes.Var(b)) + + Expr(:call, Expr(:call, :∘, a...), b) => decapodes.AppCirc1(a, term(b)) + Expr(:call, a, b) => decapodes.App1(a, term(b)) + + Expr(:call, :+, xs...) => decapodes.Plus(term.(xs)) + Expr(:call, f, x, y) => decapodes.App2(f, term(x), term(y)) + + # TODO: Will later be converted to Op2's or schema has to be changed to include multiplication + Expr(:call, :*, xs...) => decapodes.Mult(term.(xs)) + + x => error("Cannot construct term from $x") + end end -@doc """ ASKEMDeca +function parse_decapode(expr::Expr) + stmts = map(expr.args) do line + @match line begin + ::LineNumberNode => missing + # TODO: If user doesn't provide space, this gives a temp space so we can continue to construction + # For now spaces don't matter so this is fine but if they do, this will need to change + Expr(:(::), a::Symbol, b::Symbol) => decapodes.Judgement(decapodes.Var(a).name, b, :I) + Expr(:(::), a::Expr, b::Symbol) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b, :I), a.args) -Stores a Decapode with the model metadata for ASKEM AMR conformance. -""" -ASKEMDeca + Expr(:(::), a::Symbol, b) => decapodes.Judgement(decapodes.Var(a).name, b.args[1], b.args[2]) + Expr(:(::), a::Expr, b) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b.args[1], b.args[2]), a.args) -@doc """ ASKEMDecaExpr + Expr(:call, :(==), lhs, rhs) => decapodes.Eq(term(lhs), term(rhs)) + _ => error("The line $line is malformed") + end + end |> skipmissing |> collect + judges = [] + eqns = [] + foreach(stmts) do s + @match s begin + ::decapodes.Judgement => push!(judges, s) + ::Vector{decapodes.Judgement} => append!(judges, s) + ::decapodes.Eq => push!(eqns, s) + _ => error("Statement containing $s of type $(typeof(s)) was not added.") + end + end + decapodes.DecaExpr(judges, eqns) +end + +### + +# NOTE: the var in a Judgement in decapodes.it is just a Symbol. It does not have a name field, so that was removed +# to_decapode helper functions +reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}) = + let ! = reduce_term! + @match t begin + decapodes.Var(x) => begin + if haskey(syms, x) + syms[x] + else + res_var = add_part!(d, :Var, name = x, type=:infer) + syms[x] = res_var + end + end + decapodes.Lit(x) => begin + if haskey(syms, x) + syms[x] + else + res_var = add_part!(d, :Var, name = x, type=:Literal) + syms[x] = res_var + end + end + decapodes.App1(f, t) || decapodes.AppCirc1(f, t) => begin + res_var = add_part!(d, :Var, type=:infer) + add_part!(d, :Op1, src=!(t,d,syms), tgt=res_var, op1=f) + return res_var + end + decapodes.App2(f, t1, t2) => begin + res_var = add_part!(d, :Var, type=:infer) + add_part!(d, :Op2, proj1=!(t1,d,syms), proj2=!(t2,d,syms), res=res_var, op2=f) + return res_var + end + decapodes.Plus(ts) => begin + summands = [!(t,d,syms) for t in ts] + res_var = add_part!(d, :Var, type=:infer, name=:sum) + n = add_part!(d, :Σ, sum=res_var) + map(summands) do s + add_part!(d, :Summand, summand=s, summation=n) + end + return res_var + end + # TODO: Just for now assuming we have 2 or more terms + decapodes.Mult(ts) => begin + multiplicands = [!(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 + decapodes.Tan(t) => begin + # TODO: this is creating a spurious variable with the same name + txv = add_part!(d, :Var, type=:infer) + tx = add_part!(d, :TVar, incl=txv) + tanop = add_part!(d, :Op1, src=!(t,d,syms), tgt=txv, op1=DerivOp) + return txv #syms[x[1]] + end + _ => throw("Inline type judgements not yet supported!") + end + end + +function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) + @match eq begin + decapodes.Eq(t1, t2) => begin + lhs_ref = reduce_term!(t1,d,syms) + rhs_ref = reduce_term!(t2,d,syms) + + # Always let the a named variable take precedence + # TODO: If we have variable to variable equality, we want + # some kind of way to check track of this equality + ref_pair = (t1, t2) + @match ref_pair begin + (decapodes.Var(a), decapodes.Var(b)) => return d + (t1, decapodes.Var(b)) => begin + lhs_ref, rhs_ref = rhs_ref, lhs_ref + end + _ => nothing + end + + # Make rhs_ref equal to lhs_ref and adjust all its incidents + + # Case rhs_ref is a Tan + # WARNING: Don't push to deletion here because all TanVars should have a + # corresponding Op1. Pushing here would create a duplicate which breaks rem_parts! + for rhs in incident(d, rhs_ref, :incl) + d[rhs, :incl] = lhs_ref + end + # Case rhs_ref is a Op1 + for rhs in incident(d, rhs_ref, :tgt) + d[rhs, :tgt] = lhs_ref + push!(deletions, rhs_ref) + end + # Case rhs_ref is a Op2 + for rhs in incident(d, rhs_ref, :res) + d[rhs, :res] = lhs_ref + push!(deletions, rhs_ref) + end + # Case rhs_ref is a Plus + # FIXME: this typeguard is a subsitute for refactoring into multiple dispatch + if isa(d, decapodes.SummationDecapode) + for rhs in incident(d, rhs_ref, :sum) + d[rhs, :sum] = lhs_ref + push!(deletions, rhs_ref) + end + end + # TODO: delete unused vars. The only thing stopping me from doing + # this is I don't know if CSet deletion preserves incident relations + #rem_parts!(d, :Var, sort(deletions)) + end + end + return d +end + +function recognize_types(d::decapodes.AbstractNamedDecapode) + unrecognized_types = setdiff(d[:type], [:Form0, :Form1, :Form2, :DualForm0, + :DualForm1, :DualForm2, :Literal, :Parameter, + :Constant, :infer]) + isempty(unrecognized_types) || + error("Types $unrecognized_types are not recognized.") +end + +function fill_names!(d::decapodes.AbstractNamedDecapode) + bulletcount = 1 + for i in parts(d, :Var) + if !isassigned(d[:,:name],i) || isnothing(d[i, :name]) + d[i,:name] = Symbol("•$bulletcount") + bulletcount += 1 + end + end + for e in incident(d, :∂ₜ, :op1) + s = d[e,:src] + t = d[e, :tgt] + String(d[t,:name])[1] != '•' && continue + d[t, :name] = append_dot(d[s,:name]) + end + d +end + +function make_sum_mult_unique!(d::decapodes.AbstractNamedDecapode) + snum = 1 + mnum = 1 + for (i, name) in enumerate(d[:name]) + if(name == :sum) + d[i, :name] = Symbol("sum_$(snum)") + snum += 1 + elseif(name == :mult) + d[i, :name] = Symbol("mult_$(mnum)") + mnum += 1 + end + end +end -Stores the syntactic expression of a Decapode Expression with the -model metadata for ASKEM AMR conformance. -""" -ASKEMDecaExpr(header::AMR.Header, model::Decapodes.DecaExpr) = ASKEMDecaExpr(header,model,Vector{AMR.Annotation{Symbol,Symbol}}()) +function SummationDecapode(e::decapodes.DecaExpr) + # d = SummationDecapode{Any, Any, Symbol}() + d = decapodes.SummationDecapode{Symbol, Symbol, Symbol}() + symbol_table = Dict{Symbol, Int}() -@doc """ ASKEMDecapode + for judgement in e.context + var_id = add_part!(d, :Var, name=judgement.var, type=judgement.dim) + symbol_table[judgement.var] = var_id + end -Stores the combinatorial representation of a Decapode with the -model metadata for ASKEM AMR conformance. -""" -ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode) = ASKEMDecapode(header,model,Vector{AMR.Annotation{Symbol,Symbol}}()) + deletions = Vector{Int}() + for eq in e.equations + eval_eq!(eq, d, symbol_table, deletions) + end + rem_parts!(d, :Var, sort(deletions)) -StructTypes.StructType(::Type{ASKEMDeca}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{ASKEMDeca}) = :_type -StructTypes.subtypes(::Type{ASKEMDeca}) = (ASKEMDecaExpr=ASKEMDecaExpr, ASKEMDecapode=ASKEMDecapode) + recognize_types(d) -SyntacticModelsBase._dict(x::T) where {T<:Union{Decapodes.DecaExpr, Decapodes.Equation, Decapodes.Term}} = begin - Dict(:_type => typename_last(T), [k=>_dict(getfield(x, k)) for k in fieldnames(T)]...) + fill_names!(d) + d[:name] = normalize_unicode.(d[:name]) + make_sum_mult_unique!(d) + return d end -StructTypes.StructType(::Type{Decapodes.Equation}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{Decapodes.Equation}) = :_type -StructTypes.subtypes(::Type{Decapodes.Equation}) = (Eq=Eq,) -StructTypes.StructType(::Type{Decapodes.Term}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{Decapodes.Term}) = :_type -StructTypes.subtypes(::Type{Decapodes.Term}) = (Var=Decapodes.Var, - Lit=Decapodes.Lit, - Judgement=Decapodes.Judgement, - AppCirc1=Decapodes.AppCirc1, - App1=Decapodes.App1, - App2=Decapodes.App2, - Plus=Decapodes.Plus, - Mult=Decapodes.Mult, - Tan=Decapodes.Tan) end \ No newline at end of file diff --git a/test/Project.toml b/test/Project.toml index e36fc96..4e15c69 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -11,3 +11,4 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" SyntacticModels = "22bb929c-8bcf-4852-b455-eb3e1675e09c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" diff --git a/test/core_it.jl b/test/core_it.jl index 852577a..e7cf732 100644 --- a/test/core_it.jl +++ b/test/core_it.jl @@ -32,7 +32,7 @@ catch end include("amr_it_ex.jl") -# include("decapodes_it_ex.jl") +include("decapodes_it_ex.jl") include("uwd_it_ex.jl") # include("composite_models_it_ex.jl") # include("serialization_it_mwe.jl") \ No newline at end of file diff --git a/test/decapodes_it_ex.jl b/test/decapodes_it_ex.jl index 51a71fe..f832828 100644 --- a/test/decapodes_it_ex.jl +++ b/test/decapodes_it_ex.jl @@ -26,7 +26,7 @@ using StructTypes # using ..SyntacticModelsBase - +#= @intertypes "../src/amr.it" module AMR end using .AMR @@ -38,72 +38,18 @@ using .AMR end using .decapodes +=# +using ..SyntacticModels.AMR +using ..SyntacticModels.ASKEMDecapodes -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') - -my_term(s::Symbol) = decapodes.Var(normalize_unicode(s)) -my_term(s::Number) = decapodes.Lit(Symbol(s)) - -my_term(expr::Expr) = begin - @match expr begin - #TODO: Would we want ∂ₜ to be used with general expressions or just Vars? - Expr(:call, :∂ₜ, b) => decapodes.Tan(decapodes.Var(b)) - Expr(:call, :dt, b) => decapodes.Tan(decapodes.Var(b)) - - Expr(:call, Expr(:call, :∘, a...), b) => decapodes.AppCirc1(a, my_term(b)) - Expr(:call, a, b) => decapodes.App1(a, my_term(b)) - - Expr(:call, :+, xs...) => decapodes.Plus(my_term.(xs)) - Expr(:call, f, x, y) => decapodes.App2(f, my_term(x), my_term(y)) - - # TODO: Will later be converted to Op2's or schema has to be changed to include multiplication - Expr(:call, :*, xs...) => decapodes.Mult(my_term.(xs)) - - x => error("Cannot construct term from $x") - end -end - -function my_parse_decapode(expr::Expr) - stmts = map(expr.args) do line - @match line begin - ::LineNumberNode => missing - # TODO: If user doesn't provide space, this gives a temp space so we can continue to construction - # For now spaces don't matter so this is fine but if they do, this will need to change - Expr(:(::), a::Symbol, b::Symbol) => decapodes.Judgement(decapodes.Var(a).name, b, :I) - Expr(:(::), a::Expr, b::Symbol) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b, :I), a.args) - - Expr(:(::), a::Symbol, b) => decapodes.Judgement(decapodes.Var(a).name, b.args[1], b.args[2]) - Expr(:(::), a::Expr, b) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b.args[1], b.args[2]), a.args) - - Expr(:call, :(==), lhs, rhs) => decapodes.Eq(my_term(lhs), my_term(rhs)) - _ => error("The line $line is malformed") - end - end |> skipmissing |> collect - judges = [] - eqns = [] - foreach(stmts) do s - @match s begin - ::decapodes.Judgement => push!(judges, s) - ::Vector{decapodes.Judgement} => append!(judges, s) - ::decapodes.Eq => push!(eqns, s) - _ => error("Statement containing $s of type $(typeof(s)) was not added.") - end - end - decapodes.DecaExpr(judges, eqns) -end - -h = AMR.Header("", "harmonic_oscillator", +h = amr.Header("", "harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "DecaExpr", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = my_parse_decapode(quote +dexpr = ASKEMDecapodes.parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -114,197 +60,17 @@ dexpr = my_parse_decapode(quote end ) -annot = [AMR.Annotation(:X,:Form0,AMR.Name("The X variable."))] +annot = [amr.Annotation(:X,:Form0,amr.Name("The X variable."))] # Bundle the DecaExpr with the header metadata. -mexpr = decapodes.ASKEMDecaExpr(h, dexpr, annot) - - -# NOTE: the var in a Judgement in decapodes.it is just a Symbol. It does not have a name field, so that was removed -# to_decapode helper functions -reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}) = - let ! = reduce_term! - @match t begin - decapodes.Var(x) => begin - if haskey(syms, x) - syms[x] - else - res_var = add_part!(d, :Var, name = x, type=:infer) - syms[x] = res_var - end - end - decapodes.Lit(x) => begin - if haskey(syms, x) - syms[x] - else - res_var = add_part!(d, :Var, name = x, type=:Literal) - syms[x] = res_var - end - end - decapodes.App1(f, t) || decapodes.AppCirc1(f, t) => begin - res_var = add_part!(d, :Var, type=:infer) - add_part!(d, :Op1, src=!(t,d,syms), tgt=res_var, op1=f) - return res_var - end - decapodes.App2(f, t1, t2) => begin - res_var = add_part!(d, :Var, type=:infer) - add_part!(d, :Op2, proj1=!(t1,d,syms), proj2=!(t2,d,syms), res=res_var, op2=f) - return res_var - end - decapodes.Plus(ts) => begin - summands = [!(t,d,syms) for t in ts] - res_var = add_part!(d, :Var, type=:infer, name=:sum) - n = add_part!(d, :Σ, sum=res_var) - map(summands) do s - add_part!(d, :Summand, summand=s, summation=n) - end - return res_var - end - # TODO: Just for now assuming we have 2 or more terms - decapodes.Mult(ts) => begin - multiplicands = [!(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 - decapodes.Tan(t) => begin - # TODO: this is creating a spurious variable with the same name - txv = add_part!(d, :Var, type=:infer) - tx = add_part!(d, :TVar, incl=txv) - tanop = add_part!(d, :Op1, src=!(t,d,syms), tgt=txv, op1=DerivOp) - return txv #syms[x[1]] - end - _ => throw("Inline type judgements not yet supported!") - end - end - -function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) - @match eq begin - decapodes.Eq(t1, t2) => begin - lhs_ref = reduce_term!(t1,d,syms) - rhs_ref = reduce_term!(t2,d,syms) +mexpr = ASKEMDecapodes.decapodes.ASKEMDecaExpr(h, dexpr, annot) - # Always let the a named variable take precedence - # TODO: If we have variable to variable equality, we want - # some kind of way to check track of this equality - ref_pair = (t1, t2) - @match ref_pair begin - (decapodes.Var(a), decapodes.Var(b)) => return d - (t1, decapodes.Var(b)) => begin - lhs_ref, rhs_ref = rhs_ref, lhs_ref - end - _ => nothing - end - - # Make rhs_ref equal to lhs_ref and adjust all its incidents - - # Case rhs_ref is a Tan - # WARNING: Don't push to deletion here because all TanVars should have a - # corresponding Op1. Pushing here would create a duplicate which breaks rem_parts! - for rhs in incident(d, rhs_ref, :incl) - d[rhs, :incl] = lhs_ref - end - # Case rhs_ref is a Op1 - for rhs in incident(d, rhs_ref, :tgt) - d[rhs, :tgt] = lhs_ref - push!(deletions, rhs_ref) - end - # Case rhs_ref is a Op2 - for rhs in incident(d, rhs_ref, :res) - d[rhs, :res] = lhs_ref - push!(deletions, rhs_ref) - end - # Case rhs_ref is a Plus - # FIXME: this typeguard is a subsitute for refactoring into multiple dispatch - if isa(d, decapodes.SummationDecapode) - for rhs in incident(d, rhs_ref, :sum) - d[rhs, :sum] = lhs_ref - push!(deletions, rhs_ref) - end - end - # TODO: delete unused vars. The only thing stopping me from doing - # this is I don't know if CSet deletion preserves incident relations - #rem_parts!(d, :Var, sort(deletions)) - end - end - return d -end - -function recognize_types(d::decapodes.AbstractNamedDecapode) - unrecognized_types = setdiff(d[:type], [:Form0, :Form1, :Form2, :DualForm0, - :DualForm1, :DualForm2, :Literal, :Parameter, - :Constant, :infer]) - isempty(unrecognized_types) || - error("Types $unrecognized_types are not recognized.") -end - -function fill_names!(d::decapodes.AbstractNamedDecapode) - bulletcount = 1 - for i in parts(d, :Var) - if !isassigned(d[:,:name],i) || isnothing(d[i, :name]) - d[i,:name] = Symbol("•$bulletcount") - bulletcount += 1 - end - end - for e in incident(d, :∂ₜ, :op1) - s = d[e,:src] - t = d[e, :tgt] - String(d[t,:name])[1] != '•' && continue - d[t, :name] = append_dot(d[s,:name]) - end - d -end - -function make_sum_mult_unique!(d::decapodes.AbstractNamedDecapode) - snum = 1 - mnum = 1 - for (i, name) in enumerate(d[:name]) - if(name == :sum) - d[i, :name] = Symbol("sum_$(snum)") - snum += 1 - elseif(name == :mult) - d[i, :name] = Symbol("mult_$(mnum)") - mnum += 1 - end - end -end - -function SummationDecapode(e::decapodes.DecaExpr) - # d = SummationDecapode{Any, Any, Symbol}() - d = decapodes.SummationDecapode{Symbol, Symbol, Symbol}() - symbol_table = Dict{Symbol, Int}() - - for judgement in e.context - var_id = add_part!(d, :Var, name=judgement.var, type=judgement.dim) - symbol_table[judgement.var] = var_id - end - - deletions = Vector{Int}() - for eq in e.equations - eval_eq!(eq, d, symbol_table, deletions) - end - rem_parts!(d, :Var, sort(deletions)) - - recognize_types(d) - - fill_names!(d) - d[:name] = normalize_unicode.(d[:name]) - make_sum_mult_unique!(d) - return d -end # Convert a the DecaExpr to a SummationDecapode which is the # combinatorial representation. The converter lives in Decapodes/src/language.jl. -d = SummationDecapode(mexpr.model) +d = ASKEMDecapodes.SummationDecapode(mexpr.model) # We want different metadata for this representation. # The Summation prefix just means that this decapodes have @@ -312,12 +78,12 @@ d = SummationDecapode(mexpr.model) # The summation operator happens in physics so often, # that you want to bake in some specialized handling to the data structure. -h = AMR.Header("","harmonic_oscillator", +h = amr.Header("","harmonic_oscillator", "modelreps.io/SummationDecapode", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "SummationDecapode", "v1.0") -mpode = decapodes.ASKEMDecapode(h, d, annot) +mpode = ASKEMDecapodes.decapodes.ASKEMDecapode(h, d, annot) # The syntactic representation can be serialized as JSON. @@ -332,6 +98,6 @@ write_json_model(mexpr) # Can we read back the models we just wrote? @testset "Decapodes Readback" begin - mexpr′ = readback(mexpr,decapodes.ASKEMDeca) + mexpr′ = readback(mexpr,ASKEMDecapodes.decapodes.ASKEMDeca) @test JSON3.write(mexpr) == JSON3.write(mexpr′) end From f6e4db13f2d6e9596b49bc1833f901602b0e803c Mon Sep 17 00:00:00 2001 From: p-stokes Date: Tue, 5 Dec 2023 15:33:39 -0500 Subject: [PATCH 13/35] Started making intertype version for composite_models. --- src/composite_models.it | 6 ++ src/composite_models_it.jl | 85 +++++++++++++++++++++ test/composite_models_it_ex.jl | 136 +++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 src/composite_models.it create mode 100644 src/composite_models_it.jl create mode 100644 test/composite_models_it_ex.jl diff --git a/src/composite_models.it b/src/composite_models.it new file mode 100644 index 0000000..51c86a7 --- /dev/null +++ b/src/composite_models.it @@ -0,0 +1,6 @@ + +@sum CompositeModel begin + OpenModel(model::decapodes.ASKEMDecapodes.ASKEMDecaExpr, interface::Vector{amr.Symbol}) + OpenDecapode(model::decapodes.ASKEMDecapodes.ASKEMDecapode, interface::Vector{amr.Symbol}) + CompositeModelExpr(header::amr.Header, composition_pattern::uwd.UWDExpr, components::Vector{CompositeModel}) +end diff --git a/src/composite_models_it.jl b/src/composite_models_it.jl new file mode 100644 index 0000000..df514b6 --- /dev/null +++ b/src/composite_models_it.jl @@ -0,0 +1,85 @@ +module Composites + +export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode + +using MLStyle +using Catlab +# using Decapodes +using StructTypes + +# using ..SyntacticModelsBase +using ..AMR +using ..ASKEMDecapodes +using ..ASKEMUWDs + +using ACSets +using ACSets.InterTypes + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface + + +using ..AMR.amr +using ..ASKEMDecapodes.decapodes +using ..ASKEMUWDs.uwd + +@intertypes "composite_models.it" module composites + import ..amr + import ..decapodes + import ..uwd +end + +using .composites + +""" interface(m::CompositeModel) + +Extract the interface of a composite model. If the model is open, then it is the feet of the cospan. If it is a Composite, then it is the context of the uwd. +""" +interface(m::CompositeModel) = @match m begin + OpenModel(M, I) => I + CompositeModelExpr(h, uwd, components) => map(ASKEMUWDs.varname, context(uwd)) +end + +# Extract an open decapode from the decapode expression and the interface +open_decapode(d, interface) = Open(SummationDecapode(d.model), interface) +open_decapode(d::ASKEMDecaExpr, interface) = Open(SummationDecapode(d.model), interface) +open_decapode(d::ASKEMDecapode, interface) = Open(d.model, interface) + +""" Catlab.oapply(m::CompositeModel) + +CompositeModels can be flattened into a single level of model with the oapply function. + +!!! warning + Because the oapply algorithm operates on the compute graph representation of the equations, it does not produce syntactic equations. + Calls to oapply produce instances of OpenDecapode and not DecaExpr. + Software that expects to consume decapodes should plan to interact with both forms. +""" +function Catlab.oapply(m::CompositeModel) + let ! = oapply + @match m begin + # For a primitive model we just attach the interface + OpenModel(M, I) => open_decapode(M,I) + OpenDecapode(M, I) => open_decapode(M,I) + # For a composite model, we have to recurse + CompositeModelExpr(h, pattern, components) => begin + uwd = ASKEMUWDs.construct(RelationDiagram, pattern) + Ms = map(m.components) do mᵢ; + !(mᵢ) # oapply all the component models recursively + end + # OpenDecapode(ASKEMDecapode(h, apex(!(uwd, Ms))), interface(m)) # Then we call the oapply from Decapodes. + Open(apex(!(uwd, Ms)), uwd[[:outer_junction, :variable]]) # Then we call the oapply from Decapodes. + end + end + end +end + +function OpenDecapode(m::CompositeModel) + composite = oapply(m) + feet = map(l->only(dom(l)[:name]), legs(composite)) + OpenDecapode(ASKEMDecapode(m.header,apex(composite)), feet) +end + +end \ No newline at end of file diff --git a/test/composite_models_it_ex.jl b/test/composite_models_it_ex.jl new file mode 100644 index 0000000..760b15d --- /dev/null +++ b/test/composite_models_it_ex.jl @@ -0,0 +1,136 @@ +using ..SyntacticModels.AMR +using ..SyntacticModels.ASKEMDecapodes +using ..SyntacticModels.ASKEMUWDs +using ..SyntacticModels.Composites + +using MLStyle +using JSON +using Decapodes +using Catlab +using Catlab.RelationalPrograms +using Catlab.WiringDiagrams +using Test + + + +x = Typed(:X, :Form0) +v = Typed(:V, :Form0) +Q = Typed(:Q, :Form0) + +c = [x, Q] +s = [Statement(:oscillator, [x,v]), + Statement(:heating, [v,Q])] +u = ASKEMUWDs.UWDExpr(c, s) + + + +h = AMR.Header("harmonic_oscillator", + "modelreps.io/DecaExpr", + "A Simple Harmonic Oscillator as a Diagrammatic Equation", + "DecaExpr", + "v1.0") + +# The easiest way to write down a DecaExpr is in our DSL and calling the parser. +dexpr = Decapodes.parse_decapode(quote + X::Form0{Point} + V::Form0{Point} + + k::Constant{Point} + + ∂ₜ(X) == V + ∂ₜ(V) == -1*k*(X) +end +) + +# That gave us the first model +d1 = ASKEMDecaExpr(h, dexpr) + +# The second model is: +d2 = ASKEMDecaExpr( + AMR.Header("fricative_heating", + "modelreps.io/SummationDecapode", + "Velocity makes it get hot, but you dissipate heat away from Q₀", + "SummationDecapode", "v1.0"), + Decapodes.parse_decapode(quote + V::Form0{Point} + Q::Form0{Point} + κ::Constant{Point} + λ::Constant{Point} + Q₀::Parameter{Point} + + ∂ₜ(Q) == κ*V + λ(Q - Q₀) + end) +) + +# Now we can assemble this bad boi: +h = AMR.Header("composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") +m = CompositeModelExpr(h, u, [OpenModel(d1, [:X, :V]), OpenModel(d2, [:V, :Q])]) +interface(m) == [:X, :Q] +write_json_model(m) # you can see from this little model (two coupled odes even) that the jsons will not be human editable. + +# now we can interpret this big data structure to execute a composition! +composite = oapply(m) +display(apex(composite)) +to_graphviz(apex(composite)) +sm_write_json_acset(apex(composite),"$(m.header.name)-acset") + + +# TESTING NESTED COMPOSITION + +Q₊ = Untyped(:Q₊) +Q₋ = Untyped(:Q₋) +Q̇ = Untyped(:Q̇) + +uwdʰ = UWDExpr([v, Q], [Statement(:drag, [v, Q₊]), Statement(:cooling, [Q₋, Q]), Statement(:superposition, [Q₊, Q₋, Q̇])]) + +drag = ASKEMDecaExpr( + AMR.Header("DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), + Decapodes.parse_decapode(quote + V::Form0{Point} + Q₊::Form0{Point} + κ::Constant{Point} + + Q₊ == κ*V + end) +) + +cooling = ASKEMDecaExpr( + AMR.Header("NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), + Decapodes.parse_decapode(quote + Q₋::Form0{Point} + Q₀::Parameter{Point} + Q::Form0{Point} + λ::Constant{Point} + + Q₋ == λ(Q-Q₀) + end) +) + +superposition = ASKEMDecaExpr( + AMR.Header("LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), + Decapodes.parse_decapode(quote + X::Form0{Point} + Y::Form0{Point} + T::Form0{Point} + + T == X + Y + end) +) + +h = AMR.Header("hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") +m = CompositeModelExpr(h,u, [OpenModel(d1, [:X, :V]), + CompositeModelExpr(AMR.Header("heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), + uwdʰ, [OpenModel(drag, [:V, :Q₊]), OpenModel(cooling, [:Q₋, :Q]), OpenModel(superposition, [:X, :Y, :T])]) +]) +write_json_model(m) + +@testset "Composite Model Readback" begin + m′ = readback(m) + @test JSON3.write(m) == JSON3.write(m′) +end + +dh = apex(oapply(m)) + +composite = OpenDecapode(m) +hf = composite.model.header +write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) From eaa5c9148cc126336a40d1fa1eeafb135a97bf32 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 15:28:51 -0500 Subject: [PATCH 14/35] Updated composite_models_it src and test. Intertype version of oapply is not yet working; some possibly needed upstream Decapodes and Catlab functions are copied into src and parts of test are commented out. --- src/SyntacticModels.jl | 2 +- src/composite_models.it | 4 +-- src/composite_models_it.jl | 51 ++++++++++++++++++++++++---- test/composite_models_it_ex.jl | 62 ++++++++++++++++++++-------------- 4 files changed, 84 insertions(+), 35 deletions(-) diff --git a/src/SyntacticModels.jl b/src/SyntacticModels.jl index 87426f1..f924b36 100644 --- a/src/SyntacticModels.jl +++ b/src/SyntacticModels.jl @@ -4,6 +4,6 @@ include("SyntacticModelsBase.jl") include("amr_it.jl") include("decapodes_it.jl") include("uwd_it.jl") -# include("composite_models_it.jl") +include("composite_models_it.jl") end \ No newline at end of file diff --git a/src/composite_models.it b/src/composite_models.it index 51c86a7..8f7f2aa 100644 --- a/src/composite_models.it +++ b/src/composite_models.it @@ -1,6 +1,6 @@ @sum CompositeModel begin - OpenModel(model::decapodes.ASKEMDecapodes.ASKEMDecaExpr, interface::Vector{amr.Symbol}) - OpenDecapode(model::decapodes.ASKEMDecapodes.ASKEMDecapode, interface::Vector{amr.Symbol}) + OpenModel(model::decapodes.ASKEMDecaExpr, interface::Vector{Symbol}) + OpenDecapode(model::decapodes.ASKEMDecapode, interface::Vector{Symbol}) CompositeModelExpr(header::amr.Header, composition_pattern::uwd.UWDExpr, components::Vector{CompositeModel}) end diff --git a/src/composite_models_it.jl b/src/composite_models_it.jl index df514b6..11ffdb2 100644 --- a/src/composite_models_it.jl +++ b/src/composite_models_it.jl @@ -1,10 +1,10 @@ module Composites -export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode +export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode, oapply, Open using MLStyle using Catlab -# using Decapodes +using Decapodes # : SummationDecapode using StructTypes # using ..SyntacticModelsBase @@ -26,6 +26,25 @@ using ..AMR.amr using ..ASKEMDecapodes.decapodes using ..ASKEMUWDs.uwd +#= +@intertypes "../src/amr.it" module amr end +using .amr +@intertypes "../src/decapodes.it" module decapodes + import ..amr +end +using .decapodes +@intertypes "../src/uwd.it" module uwd + import ..amr +end +using .uwd +@intertypes "../src/composite_models.it" module composites + import ..amr + import ..decapodes + import ..uwd +end +using .composites +=# + @intertypes "composite_models.it" module composites import ..amr import ..decapodes @@ -38,14 +57,32 @@ using .composites Extract the interface of a composite model. If the model is open, then it is the feet of the cospan. If it is a Composite, then it is the context of the uwd. """ -interface(m::CompositeModel) = @match m begin - OpenModel(M, I) => I - CompositeModelExpr(h, uwd, components) => map(ASKEMUWDs.varname, context(uwd)) +interface(m::composites.CompositeModel) = @match m begin + composites.OpenModel(M, I) => I + composites.CompositeModelExpr(h, uwd′, components) => map(ASKEMUWDs.varname, context(uwd′)) +end + + + +OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(Decapodes.SummationDecapode, :Var) + +# function Decapodes.Open(d::ASKEMDecapodes.decapodes.SummationDecapode, names::Vector{Symbol}) +function Decapodes.Open(d::decapodes.SummationDecapode, names::Vector{Symbol}) + legs = map(names) do name + FinFunction(incident(d, name, :name), nparts(d, :Var)) + end + OpenSummationDecapode(d, legs...) end +#= +apex(decapode::OpenSummationDecapode) = apex(decapode.cospan) +legs(decapode::OpenSummationDecapode) = legs(decapode.cospan) +feet(decapode::OpenSummationDecapode) = decapode.feet +=# + # Extract an open decapode from the decapode expression and the interface -open_decapode(d, interface) = Open(SummationDecapode(d.model), interface) -open_decapode(d::ASKEMDecaExpr, interface) = Open(SummationDecapode(d.model), interface) +open_decapode(d, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) +open_decapode(d::ASKEMDecaExpr, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) open_decapode(d::ASKEMDecapode, interface) = Open(d.model, interface) """ Catlab.oapply(m::CompositeModel) diff --git a/test/composite_models_it_ex.jl b/test/composite_models_it_ex.jl index 760b15d..ab9969f 100644 --- a/test/composite_models_it_ex.jl +++ b/test/composite_models_it_ex.jl @@ -5,7 +5,7 @@ using ..SyntacticModels.Composites using MLStyle using JSON -using Decapodes +# using Decapodes using Catlab using Catlab.RelationalPrograms using Catlab.WiringDiagrams @@ -13,25 +13,33 @@ using Test +using ACSets +using ACSets.InterTypes +using Test +using OrderedCollections +import JSON +import JSON3 + + x = Typed(:X, :Form0) v = Typed(:V, :Form0) Q = Typed(:Q, :Form0) c = [x, Q] -s = [Statement(:oscillator, [x,v]), - Statement(:heating, [v,Q])] -u = ASKEMUWDs.UWDExpr(c, s) +s = [ASKEMUWDs.uwd.Statement(:oscillator, [x,v]), + ASKEMUWDs.uwd.Statement(:heating, [v,Q])] +u = UWDExpr(c, s) -h = AMR.Header("harmonic_oscillator", +h = Header("","harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "DecaExpr", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = Decapodes.parse_decapode(quote +dexpr = ASKEMDecapodes.parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -43,15 +51,15 @@ end ) # That gave us the first model -d1 = ASKEMDecaExpr(h, dexpr) +d1 = ASKEMDecaExpr(h, dexpr, []) # The second model is: d2 = ASKEMDecaExpr( - AMR.Header("fricative_heating", + Header("","fricative_heating", "modelreps.io/SummationDecapode", "Velocity makes it get hot, but you dissipate heat away from Q₀", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + ASKEMDecapodes.parse_decapode(quote V::Form0{Point} Q::Form0{Point} κ::Constant{Point} @@ -59,13 +67,15 @@ d2 = ASKEMDecaExpr( Q₀::Parameter{Point} ∂ₜ(Q) == κ*V + λ(Q - Q₀) - end) + end), + [] ) # Now we can assemble this bad boi: -h = AMR.Header("composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") +h = Header("","composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") m = CompositeModelExpr(h, u, [OpenModel(d1, [:X, :V]), OpenModel(d2, [:V, :Q])]) interface(m) == [:X, :Q] +#= TODO: FIXME write_json_model(m) # you can see from this little model (two coupled odes even) that the jsons will not be human editable. # now we can interpret this big data structure to execute a composition! @@ -73,7 +83,7 @@ composite = oapply(m) display(apex(composite)) to_graphviz(apex(composite)) sm_write_json_acset(apex(composite),"$(m.header.name)-acset") - +=# # TESTING NESTED COMPOSITION @@ -81,47 +91,48 @@ Q₊ = Untyped(:Q₊) Q₋ = Untyped(:Q₋) Q̇ = Untyped(:Q̇) -uwdʰ = UWDExpr([v, Q], [Statement(:drag, [v, Q₊]), Statement(:cooling, [Q₋, Q]), Statement(:superposition, [Q₊, Q₋, Q̇])]) +uwdʰ = UWDExpr([v, Q], [ASKEMUWDs.uwd.Statement(:drag, [v, Q₊]), ASKEMUWDs.uwd.Statement(:cooling, [Q₋, Q]), ASKEMUWDs.uwd.Statement(:superposition, [Q₊, Q₋, Q̇])]) drag = ASKEMDecaExpr( - AMR.Header("DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + Header("","DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), + ASKEMDecapodes.parse_decapode(quote V::Form0{Point} Q₊::Form0{Point} κ::Constant{Point} Q₊ == κ*V - end) + end), [] ) cooling = ASKEMDecaExpr( - AMR.Header("NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + Header("","NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), + ASKEMDecapodes.parse_decapode(quote Q₋::Form0{Point} Q₀::Parameter{Point} Q::Form0{Point} λ::Constant{Point} Q₋ == λ(Q-Q₀) - end) + end), [] ) superposition = ASKEMDecaExpr( - AMR.Header("LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + Header("","LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), + ASKEMDecapodes.parse_decapode(quote X::Form0{Point} Y::Form0{Point} T::Form0{Point} T == X + Y - end) + end), [] ) -h = AMR.Header("hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") +h = Header("","hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") m = CompositeModelExpr(h,u, [OpenModel(d1, [:X, :V]), - CompositeModelExpr(AMR.Header("heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), + CompositeModelExpr(Header("","heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), uwdʰ, [OpenModel(drag, [:V, :Q₊]), OpenModel(cooling, [:Q₋, :Q]), OpenModel(superposition, [:X, :Y, :T])]) ]) +#= TODO: FIXME write_json_model(m) @testset "Composite Model Readback" begin @@ -133,4 +144,5 @@ dh = apex(oapply(m)) composite = OpenDecapode(m) hf = composite.model.header -write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) +write_json_model(ASKEMDecapode(Header("","flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) +=# \ No newline at end of file From aed4d4553bd8c16794624c10abb324ca5eebef2d Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 15:31:36 -0500 Subject: [PATCH 15/35] Started working on intertype version of ACSetSpec and other ADT code to finish the functionality in amr. Copied in commented version of old example code into test. --- src/amr.it | 32 ++- test/amr_it_ex.jl | 488 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 518 insertions(+), 2 deletions(-) diff --git a/src/amr.it b/src/amr.it index 37aeca9..592544a 100644 --- a/src/amr.it +++ b/src/amr.it @@ -1,3 +1,26 @@ + +@sum Args begin + Value(_1::Symbol) + Kwarg(_1::Symbol, _2::Args) +end + +struct Statement + table::Symbol + element::Vector{Args} +end + +#= +@sum union_sym_expr begin + sym(s::Symbol) + expr(e::Expr) +end +=# + +struct ACSetSpec + acstype::Symbol + body::Vector{Statement} +end + @sum MathML begin Math(str::String) Presentation(str::String) @@ -37,10 +60,15 @@ end Time(id::Symbol, units::Unit) end +struct Pair + first::Symbol + second::Symbol +end + @sum Semantic begin ODEList(statements::Vector{Expression}) ODERecord(rates::Vector{Rate}, initials::Vector{Initial}, parameters::Vector{Parameter}, time::Time) - # Typing(system::ACSetSpec, map::Vector{Pair}) + Typing(system::ACSetSpec, map::Vector{Pair}) end @@ -69,6 +97,6 @@ end struct ASKEModel header::Header - # model::ACSetSpec + model::ACSetSpec semantics::Vector{Semantic} end \ No newline at end of file diff --git a/test/amr_it_ex.jl b/test/amr_it_ex.jl index b17aaf8..a68a509 100644 --- a/test/amr_it_ex.jl +++ b/test/amr_it_ex.jl @@ -82,6 +82,494 @@ amr₁ = AMR.ASKEModel(header, model, [ode] ) + + +typesystem = acsetspec(:(LabelledPetriNet{Symbol}), quote + S(label=:Pop) + + T(label=:inf) + T(label=:disease) + T(label=:strata) + + I(is=:Pop, it=:inf) + I(is=:Pop, it=:inf) + I(is=:Pop, it=:disease) + I(is=:Pop, it=:strata) + + O(os=:Pop, it=:inf) + O(os=:Pop, it=:inf) + O(os=:Pop, it=:disease) + O(os=:Pop, it=:strata) +end) + + +typing = Typing( + typesystem, + [ + (:S=>:Pop), + (:I=>:Pop), + (:R=>:Pop), + (:inf=>:inf), + (:rec=>:disease) + ] +) + +amr₂ = ASKEModel(header, + model, + [ + odelist, + typing + ] +) + +println() +println(amr_to_string(amr₁)) +println() +println(amr_to_string(amr₂)) + +AMR.amr_to_expr(amr₁) |> println +AMR.amr_to_expr(amr₂.header) |> println +AMR.amr_to_expr(amr₂.model) |> println +map(AMR.amr_to_expr(amr₂.semantics[1]).args[2].args) do s; println(s) end +AMR.amr_to_expr(amr₂.semantics[1]).args[2] +AMR.amr_to_expr(amr₂.semantics[1]) +AMR.amr_to_expr(amr₂) |> println + +h = AMR.load(Header, Dict("name" => "SIR Model", +"schema" => "https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json", +"description" => "SIR model", +"schema_name" => "petrinet", +"model_version" => "0.1")) + +@test AMR.amr_to_string(h) == "\"\"\"\nASKE Model Representation: SIR Model0.1 :: petrinet \n https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json\n\nSIR model\n\"\"\"" + +mjson = raw"""{ + "states": [ + { + "id": "S", + "name": "Susceptible", + "description": "Number of individuals that are 'susceptible' to a disease infection", + "grounding": { + "identifiers": { + "ido": "0000514" + } + }, + "units": { + "expression": "person", + "expression_mathml": "person" + } + }, + { + "id": "I", + "name": "Infected", + "description": "Number of individuals that are 'infected' by a disease", + "grounding": { + "identifiers": { + "ido": "0000511" + } + }, + "units": { + "expression": "person", + "expression_mathml": "person" + } + }, + { + "id": "R", + "name": "Recovered", + "description": "Number of individuals that have 'recovered' from a disease infection", + "grounding": { + "identifiers": { + "ido": "0000592" + } + }, + "units": { + "expression": "person", + "expression_mathml": "person" + } + } + ], + "transitions": [ + { + "id": "inf", + "input": [ + "S", + "I" + ], + "output": [ + "I", + "I" + ], + "properties": { + "name": "Infection", + "description": "Infective process between individuals" + } + }, + { + "id": "rec", + "input": [ + "I" + ], + "output": [ + "R" + ], + "properties": { + "name": "Recovery", + "description": "Recovery process of a infected individual" + } + } + ] + } + """ +using JSON +modeldict = JSON.parse(mjson) +using ACSets.ADTs + +println(sprint(show, AMR.petrispec(modeldict))) + +semantics_str = raw"""{ + "ode": { + "rates": [ + { + "target": "inf", + "expression": "S*I*beta", + "expression_mathml": "SIbeta" + }, + { + "target": "rec", + "expression": "I*gamma", + "expression_mathml": "Igamma" + } + ], + "initials": [ + { + "target": "S", + "expression": "S0", + "expression_mathml": "S0" + }, + { + "target": "I", + "expression": "I0", + "expression_mathml": "I0" + }, + { + "target": "R", + "expression": "R0", + "expression_mathml": "R0" + } + ], + "parameters": [ + { + "id": "beta", + "name": "β", + "description": "infection rate", + "units": { + "expression": "1/(person*day)", + "expression_mathml": "1personday" + }, + "value": 2.7e-7, + "distribution": { + "type": "StandardUniform1", + "parameters": { + "minimum": 2.6e-7, + "maximum": 2.8e-7 + } + } + }, + { + "id": "gamma", + "name": "γ", + "description": "recovery rate", + "grounding": { + "identifiers": { + "askemo": "0000013" + } + }, + "units": { + "expression": "1/day", + "expression_mathml": "1day" + }, + "value": 0.14, + "distribution": { + "type": "StandardUniform1", + "parameters": { + "minimum": 0.1, + "maximum": 0.18 + } + } + }, + { + "id": "S0", + "name": "S₀", + "description": "Total susceptible population at timestep 0", + "value": 1000 + }, + { + "id": "I0", + "name": "I₀", + "description": "Total infected population at timestep 0", + "value": 1 + }, + { + "id": "R0", + "name": "R₀", + "description": "Total recovered population at timestep 0", + "value": 0 + } + ], + "observables": [ + { + "id": "noninf", + "name": "Non-infectious", + "states": [ + "S", + "R" + ], + "expression": "S+R", + "expression_mathml": "SR" + } + ], + "time": { + "id": "t", + "units": { + "expression": "day", + "expression_mathml": "day" + } + } + } +} +""" +semantics_dict = JSON.parse(semantics_str) +@show semantics_dict + +AMR.load(ODERecord, semantics_dict["ode"]) |> AMR.amr_to_string |> println + +sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir.json"])) +AMR.optload(AMRExamples.sirmodel_dict, ["model", :states], nothing) |> show +sirmodel = AMR.load(ASKEModel, sirmodel_dict) +sirmodel |> AMR.amr_to_string |> println + +sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir_typed.json"])) +semtyp = sirmodel_dict["semantics"]["typing"] + +sirmodel = AMR.load(Typing, semtyp) |> AMR.amr_to_string |> println + +sirmodel = AMR.load(ASKEModel, sirmodel_dict) +sirmodel |> AMR.amr_to_string |> println + +# Deserializing from Human readable strings + + +@testset "Loading Time" begin + @test AMR.load(AMR.Time, Base.Meta.parse("t::Time{}")) == AMR.Time(:t, AMR.Unit("", nomath)) + @test AMR.load(AMR.Time, Base.Meta.parse("t::Time{day}")) == AMR.Time(:t, AMR.Unit("day", nomath)) + @test AMR.load(AMR.Time, Base.Meta.parse("t::Time{day^2}")) == AMR.Time(:t, AMR.Unit("day ^ 2", nomath)) + @test_throws ErrorException AMR.load(AMR.Time, Base.Meta.parse("t::time{}")) + @test_throws ErrorException AMR.load(AMR.Time, Base.Meta.parse("t::time")) +end + +@testset "Loading Rates" begin + infspec = Meta.parse("inf::Rate = S*I*beta") + @test AMR.load(AMR.Rate, infspec).target == :inf + @test AMR.load(AMR.Rate, infspec).f.expression == "S * I * beta" + infspec = Meta.parse("inf::Rate{persons/time} = S*I*beta") + @test AMR.load(AMR.Rate, infspec).target == :inf + @test AMR.load(AMR.Rate, infspec).f.expression == "S * I * beta" +end +@testset "Loading Initials" begin + infspec = Meta.parse("S0::Initial = S*I*beta") + @test AMR.load(AMR.Initial, infspec).target == :S0 + @test AMR.load(AMR.Initial, infspec).f.expression == "S * I * beta" + infspec = Meta.parse("S0::Initial{persons/time} = S*I*beta") + @test AMR.load(AMR.Initial, infspec).target == :S0 + @test AMR.load(AMR.Initial, infspec).f.expression == "S * I * beta" +end + +@testset "Loading Parameters" begin + @testset "No Units" begin + paramstr = raw""" + \"\"\" + R₀ -- Total recovered population at timestep 0 + \"\"\" + R0::Parameter{} = 0.0 ~ δ(missing) + """ + paramexp = Meta.parse(paramstr) + param = AMR.load(Parameter, paramexp) + @test param.units == AMR.nounit + @test param.id == :R0 + end + @testset "Units" begin + + paramstr = raw""" + \"\"\" + R₀ -- Total recovered population at timestep 0 + \"\"\" + R0::Parameter{persons/day} = 0.0 ~ δ(missing) + """ + paramexp = Meta.parse(paramstr) + param = AMR.load(Parameter, paramexp) + @test param.id == :R0 + @test param.units == Unit("persons / day", AMR.nomath) + @test param.distribution == PointMass(:missing) + + paramstr = raw""" + \"\"\" + R₀ -- Total recovered population at timestep 0 + \"\"\" + R0::Parameter{persons/day} = 0.0 ~ δ(0.0) + """ + paramexp = Meta.parse(paramstr) + param = AMR.load(Parameter, paramexp) + @test param.id == :R0 + @test param.units == Unit("persons / day", AMR.nomath) + @test param.distribution == PointMass(0.0) + end +end + +odelist_expr = Meta.parse(raw""" +ODE_Record = begin + +inf::Rate = S*I*beta +rec::Rate = I*gamma +S::Initial = S0 +I::Initial = I0 +R::Initial = R0 + +\"\"\" β -- infection rate \"\"\" +beta::Parameter{} = 0.027 ~ U(0,1) + + +\"\"\" γ -- recovery rate \"\"\" +gamma::Parameter{} = 0.14 ~ U(0,1) + + +\"\"\" S₀ -- Total susceptible population at timestep 0 \"\"\" +S0::Parameter{} = 1000.0 ~ δ(missing) + + +\"\"\" I₀ -- Total infected population at timestep 0\"\"\" +I0::Parameter{} = 1.0 ~ δ(missing) + + +\"\"\" R₀ -- Total recovered population at timestep 0\"\"\" +R0::Parameter{} = 0.0 ~ δ(missing) + +t::Time{day} +end +""") + +ol = AMR.load(ODEList, odelist_expr) +AMR.amr_to_string(ol) |> println + + +header_expr = Meta.parse(raw""" +\"\"\" +ASKE Model Representation: SIR Model@v0.1 :: petrinet + https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json + +Typed SIR model created by Nelson, derived from the one by Ben, Micah, Brandon +\"\"\" +""") + +AMR.load(Header, header_expr) + +modelrep = raw""" +begin +\"\"\" +ASKE Model Representation: SIR Model@0.1 :: petrinet + https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json + +Typed SIR model created by Nelson, derived from the one by Ben, Micah, Brandon +\"\"\" +Model = begin + AMRPetriNet = begin + S(id=S,name=Susceptible,units=nothing) + S(id=I,name=Infected,units=nothing) + S(id=R,name=Recovered,units=nothing) + T(id=inf,name=Infection,desc="Infective process between individuals") + T(id=rec,name=Recovery,desc="Recovery process of a infected individual") + I(is=S,it=inf) + I(is=I,it=inf) + I(is=I,it=rec) + O(os=I,ot=inf) + O(os=I,ot=inf) + O(os=R,ot=rec) + end +end + +ODE_Record = begin + +inf::Rate = S*I*beta +rec::Rate = I*gamma +S::Initial = S0 +I::Initial = I0 +R::Initial = R0 + +\"\"\" β -- infection rate \"\"\" +beta::Parameter{} = 0.027 ~ U(0,1) + + +\"\"\" γ -- recovery rate \"\"\" +gamma::Parameter{} = 0.14 ~ U(0,1) + + +\"\"\" S₀ -- Total susceptible population at timestep 0 \"\"\" +S0::Parameter{} = 1000.0 ~ δ(missing) + + +\"\"\" I₀ -- Total infected population at timestep 0\"\"\" +I0::Parameter{} = 1.0 ~ δ(missing) + + +\"\"\" R₀ -- Total recovered population at timestep 0\"\"\" +R0::Parameter{} = 0.0 ~ δ(missing) + +t::Time{day} +end + +Typing = begin + Model = begin + AMRPetriNet = begin + S(id=Pop,name=Pop,units=nothing) + S(id=Vaccine,name=Vaccine,units=nothing) + T(id=Infect,name=Infect,desc="2-to-2 process that represents infectious contact between two human individuals.") + T(id=Disease,name=Disease,desc="1-to-1 process that represents a change in th edisease status of a human individual.") + T(id=Strata,name=Strata,desc="1-to-1 process that represents a change in the demographic division of a human individual.") + T(id=Vaccinate,name=Vaccinate,desc="2-to-1 process that represents an human individual receiving a vaccine dose.") + T(id=Produce_Vaccine,name="Produce Vaccine",desc="0-to-1 process that represents the production of a single vaccine dose.") + I(is=Pop,it=Infect) + I(is=Pop,it=Infect) + I(is=Pop,it=Disease) + I(is=Pop,it=Strata) + I(is=Pop,it=Vaccinate) + I(is=Vaccine,it=Vaccinate) + O(os=Pop,ot=Infect) + O(os=Pop,ot=Infect) + O(os=Pop,ot=Disease) + O(os=Pop,ot=Strata) + O(os=Pop,ot=Vaccinate) + O(os=Vaccine,ot=Produce_Vaccine) + end + end + TypeMap = [ + S => Pop, + I => Pop, + R => Pop, + inf => Infect, + rec => Disease,] +end +end +""" +println(modelrep) +model_expr = Base.Meta.parse(modelrep) +@test AMR.load(ASKEModel, model_expr).header isa Header +@test AMR.load(ASKEModel, model_expr).model isa ACSetSpec +@test length(AMR.load(ODEList, model_expr.args[4]).statements) == 8 +@test length(AMR.load(ASKEModel, model_expr).semantics[1].statements) == 8 +@test AMR.load(Typing, model_expr.args[6]).system isa ACSetSpec +@test AMR.load(Typing, model_expr.args[6]).map isa Vector{Pair} +println(AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) +@test_skip (AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) == modelrep + =# end # module \ No newline at end of file From 744bbcaeb1e3d21789fc21b0375ce3751948bf63 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 17:36:20 -0500 Subject: [PATCH 16/35] Moved amr_it.jl contents into amr.jl to better view differences. --- src/SyntacticModels.jl | 2 +- src/amr.jl | 150 +++++++++++------------------------------ 2 files changed, 39 insertions(+), 113 deletions(-) diff --git a/src/SyntacticModels.jl b/src/SyntacticModels.jl index f924b36..c387b2c 100644 --- a/src/SyntacticModels.jl +++ b/src/SyntacticModels.jl @@ -1,7 +1,7 @@ module SyntacticModels include("SyntacticModelsBase.jl") -include("amr_it.jl") +include("amr.jl") include("decapodes_it.jl") include("uwd_it.jl") include("composite_models_it.jl") diff --git a/src/amr.jl b/src/amr.jl index 42c7984..35156e9 100644 --- a/src/amr.jl +++ b/src/amr.jl @@ -1,10 +1,10 @@ module AMR -export Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, +export amr, Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, Rate, Initial, Parameter, Time, StandardUniform, Uniform, StandardNormal, Normal, PointMass, - Semantic, Header, ODERecord, ODEList, Typing, ASKEModel, - distro_string, amr_to_string, + Semantic, Header, ODERecord, ODEList, ASKEModel, # Typing, + distro_string, # amr_to_string, Annotation, Note, Name, Description, Grounding, Units using Reexport @@ -14,104 +14,28 @@ using ACSets.ADTs using ACSets.ACSetInterface using StructTypes -using ..SyntacticModelsBase -@data MathML <: AbstractTerm begin - Math(String) - Presentation(String) -end - -nomath = Math("") - -@as_record struct ExpressionFormula{T} <: AbstractTerm - expression::T - expression_mathml::MathML -end - -@as_record struct Unit <: AbstractTerm - expression::String - expression_mathml::MathML -end +@intertypes "amr.it" module amr end +using .amr -nounit = Unit("", nomath) - -@data Distribution <: AbstractTerm begin - StandardUniform - Uniform(min, max) - StandardNormal - Normal(mean, variance) - PointMass(value) -end -@as_record struct Observable{T <: AbstractTerm} - id::Symbol - name::String - states::Vector{Symbol} - f::ExpressionFormula -end - -@data Expression <: AbstractTerm begin - Rate(target::Symbol, f::ExpressionFormula) - Initial(target::Symbol, f::ExpressionFormula) - Parameter(id::Symbol, name::String, description::String, units::Unit, value::Float64, distribution::Distribution) - Time(id::Symbol, units::Unit) -end - -@data Semantic <: AbstractTerm begin - ODEList(statements::Vector{Expression}) - ODERecord(rates::Vector{Rate}, initials::Vector{Initial}, parameters::Vector{Parameter}, time::Time) - # Metadata - Typing(system::ACSetSpec, map::Vector{Pair}) - # Stratification -end - -@data Note <: AbstractTerm begin - Name(str::String) - Description(str::String) - Grounding(ontology::String, identifier::String) - Units(expression::String) -end - -StructTypes.StructType(::Type{Note}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{Note}) = :_type -StructTypes.subtypes(::Type{Note}) = - (Name=Name,Description=Description,Grounding=Grounding,Units=Units,) - -@as_record struct Annotation{E,T} <: AbstractTerm - entity::E - type::T - note::Note -end - -@as_record struct Header <: AbstractTerm - name::String - schema::String - description::String - schema_name::String - model_version::String -end - -@as_record struct ASKEModel <: AbstractTerm - header::Header - model::ACSetSpec - semantics::Vector{Semantic} -end - -function distro_string(d::Distribution) +function distro_string(d::amr.Distribution) @match d begin - StandardUniform => "U(0,1)" - Uniform(min, max) => "U($min,$max)" - StandardNormal => "N(0,1)" - Normal(mu, var) => "N($mu,$var)" - PointMass(value) => "δ($value)" + amr.StandardUniform(s) => "U(0,1)" + amr.Uniform(min, max) => "U($min,$max)" + amr.StandardNormal(s) => "N(0,1)" + amr.Normal(mu, var) => "N($mu,$var)" + amr.PointMass(value) => "δ($value)" end end -function distro_expr(d::Distribution) +function distro_expr(d::amr.Distribution) return Base.Meta.parse(distro_string(d)) end +#= + function note_string(n::Note) @match n begin Name(n) => "Name($n)" @@ -129,34 +53,34 @@ padlines(ss::Vector, n) = map(ss) do s " "^n * s end padlines(s::String, n=2) = join(padlines(split(s, "\n"), n), "\n") - -function amr_to_string(amr) +=# +function amr_to_string(amr′) let ! = amr_to_string - @match amr begin - s::String => s - Math(s) => !s - Presentation(s) => " $s " - u::Unit => !u.expression - d::Distribution => distro_string(d) - Time(id, u) => "$id::Time{$(!u)}\n" - Rate(t, f) => "$t::Rate = $(f.expression)" - Initial(t, f) => "$t::Initial = $(f.expression)" - Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" - Header(name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" - Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" + @match amr′ begin + s::String => s + amr.Math(s) => !s + amr.Presentation(s) => " $s " + u::amr.Unit => !u.expression + d::amr.Distribution => distro_string(d) + amr.Time(id, u) => "$id::Time{$(!u)}\n" + amr.Rate(t, f) => "$t::Rate = $(f.expression)" + amr.Initial(t, f) => "$t::Initial = $(f.expression)" + amr.Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" + amr.Header(id, name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" + amr.Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" m::ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" - ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" - ODERecord(rts, init, para, time) => join(vcat(["ODE_Record = begin\n"], !rts , !init, !para, [!time, "end"]), "\n") - vs::Vector{Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") - vs::Vector{Semantic} => join(map(!, vs), "\n\n") + amr.ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" + amr.ODERecord(rts, init, para, time) => join(vcat(["ODE_Record = begin\n"], !rts , !init, !para, [!time, "end"]), "\n") + vs::Vector{amr.Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") + vs::Vector{amr.Semantic} => join(map(!, vs), "\n\n") xs::Vector => map(!, xs) - Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" - ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" - Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_string(n))" + # amr.Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" + # amr.ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" + amr.Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_string(n))" end end end - +#= block(exprs) = begin q = :(begin @@ -167,6 +91,7 @@ end extract_acsetspec(s::String) = join(split(s, " ")[2:end], " ") |> Meta.parse + function amr_to_expr(amr) let ! = amr_to_expr @match amr begin @@ -460,4 +385,5 @@ function load(::Type{ASKEModel}, ex::Expr) end ASKEModel(elts[2][1], elts[2][2], [elts[4], elts[6]]) end +=# end # module end \ No newline at end of file From d1ad500a785af635db313aad606c635f92dc1975 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 17:58:01 -0500 Subject: [PATCH 17/35] Moved contents of decapodes_it.jl and uwd_it.jl files to respective original files for easier diff view. --- src/SyntacticModels.jl | 4 +- src/decapodes.jl | 287 +++++++++++++++++++++++++++++++++++------ src/uwd.jl | 75 ++++++----- 3 files changed, 286 insertions(+), 80 deletions(-) diff --git a/src/SyntacticModels.jl b/src/SyntacticModels.jl index c387b2c..7fd9f83 100644 --- a/src/SyntacticModels.jl +++ b/src/SyntacticModels.jl @@ -2,8 +2,8 @@ module SyntacticModels include("SyntacticModelsBase.jl") include("amr.jl") -include("decapodes_it.jl") -include("uwd_it.jl") +include("decapodes.jl") +include("uwd.jl") include("composite_models_it.jl") end \ No newline at end of file diff --git a/src/decapodes.jl b/src/decapodes.jl index 1217a31..5629672 100644 --- a/src/decapodes.jl +++ b/src/decapodes.jl @@ -1,61 +1,270 @@ module ASKEMDecapodes -export ASKEMDecaExpr, ASKEMDecapode +export ASKEMDecaExpr, ASKEMDecapode, ASKEMDeca, SummationDecapode, parse_decapode -using ..SyntacticModelsBase using ..AMR using StructTypes -using Decapodes using MLStyle -@data ASKEMDeca <: AbstractTerm begin - ASKEMDecaExpr(header::AMR.Header, model::Decapodes.DecaExpr, annotations::Vector{AMR.Annotation{Symbol,Symbol}}) - ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode, annotations::Vector{AMR.Annotation{Symbol,Symbol}}) +using ACSets +using ACSets.InterTypes + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface + +import Unicode + +using ..AMR.amr + +@intertypes "decapodes.it" module decapodes + import ..amr +end + +using .decapodes + + +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') + +term(s::Symbol) = decapodes.Var(normalize_unicode(s)) +term(s::Number) = decapodes.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) => decapodes.Tan(decapodes.Var(b)) + Expr(:call, :dt, b) => decapodes.Tan(decapodes.Var(b)) + + Expr(:call, Expr(:call, :∘, a...), b) => decapodes.AppCirc1(a, term(b)) + Expr(:call, a, b) => decapodes.App1(a, term(b)) + + Expr(:call, :+, xs...) => decapodes.Plus(term.(xs)) + Expr(:call, f, x, y) => decapodes.App2(f, term(x), term(y)) + + # TODO: Will later be converted to Op2's or schema has to be changed to include multiplication + Expr(:call, :*, xs...) => decapodes.Mult(term.(xs)) + + x => error("Cannot construct term from $x") + end end -@doc """ ASKEMDeca +function parse_decapode(expr::Expr) + stmts = map(expr.args) do line + @match line begin + ::LineNumberNode => missing + # TODO: If user doesn't provide space, this gives a temp space so we can continue to construction + # For now spaces don't matter so this is fine but if they do, this will need to change + Expr(:(::), a::Symbol, b::Symbol) => decapodes.Judgement(decapodes.Var(a).name, b, :I) + Expr(:(::), a::Expr, b::Symbol) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b, :I), a.args) -Stores a Decapode with the model metadata for ASKEM AMR conformance. -""" -ASKEMDeca + Expr(:(::), a::Symbol, b) => decapodes.Judgement(decapodes.Var(a).name, b.args[1], b.args[2]) + Expr(:(::), a::Expr, b) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b.args[1], b.args[2]), a.args) -@doc """ ASKEMDecaExpr + Expr(:call, :(==), lhs, rhs) => decapodes.Eq(term(lhs), term(rhs)) + _ => error("The line $line is malformed") + end + end |> skipmissing |> collect + judges = [] + eqns = [] + foreach(stmts) do s + @match s begin + ::decapodes.Judgement => push!(judges, s) + ::Vector{decapodes.Judgement} => append!(judges, s) + ::decapodes.Eq => push!(eqns, s) + _ => error("Statement containing $s of type $(typeof(s)) was not added.") + end + end + decapodes.DecaExpr(judges, eqns) +end + +### + +# NOTE: the var in a Judgement in decapodes.it is just a Symbol. It does not have a name field, so that was removed +# to_decapode helper functions +reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}) = + let ! = reduce_term! + @match t begin + decapodes.Var(x) => begin + if haskey(syms, x) + syms[x] + else + res_var = add_part!(d, :Var, name = x, type=:infer) + syms[x] = res_var + end + end + decapodes.Lit(x) => begin + if haskey(syms, x) + syms[x] + else + res_var = add_part!(d, :Var, name = x, type=:Literal) + syms[x] = res_var + end + end + decapodes.App1(f, t) || decapodes.AppCirc1(f, t) => begin + res_var = add_part!(d, :Var, type=:infer) + add_part!(d, :Op1, src=!(t,d,syms), tgt=res_var, op1=f) + return res_var + end + decapodes.App2(f, t1, t2) => begin + res_var = add_part!(d, :Var, type=:infer) + add_part!(d, :Op2, proj1=!(t1,d,syms), proj2=!(t2,d,syms), res=res_var, op2=f) + return res_var + end + decapodes.Plus(ts) => begin + summands = [!(t,d,syms) for t in ts] + res_var = add_part!(d, :Var, type=:infer, name=:sum) + n = add_part!(d, :Σ, sum=res_var) + map(summands) do s + add_part!(d, :Summand, summand=s, summation=n) + end + return res_var + end + # TODO: Just for now assuming we have 2 or more terms + decapodes.Mult(ts) => begin + multiplicands = [!(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 + decapodes.Tan(t) => begin + # TODO: this is creating a spurious variable with the same name + txv = add_part!(d, :Var, type=:infer) + tx = add_part!(d, :TVar, incl=txv) + tanop = add_part!(d, :Op1, src=!(t,d,syms), tgt=txv, op1=DerivOp) + return txv #syms[x[1]] + end + _ => throw("Inline type judgements not yet supported!") + end + end + +function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) + @match eq begin + decapodes.Eq(t1, t2) => begin + lhs_ref = reduce_term!(t1,d,syms) + rhs_ref = reduce_term!(t2,d,syms) + + # Always let the a named variable take precedence + # TODO: If we have variable to variable equality, we want + # some kind of way to check track of this equality + ref_pair = (t1, t2) + @match ref_pair begin + (decapodes.Var(a), decapodes.Var(b)) => return d + (t1, decapodes.Var(b)) => begin + lhs_ref, rhs_ref = rhs_ref, lhs_ref + end + _ => nothing + end + + # Make rhs_ref equal to lhs_ref and adjust all its incidents + + # Case rhs_ref is a Tan + # WARNING: Don't push to deletion here because all TanVars should have a + # corresponding Op1. Pushing here would create a duplicate which breaks rem_parts! + for rhs in incident(d, rhs_ref, :incl) + d[rhs, :incl] = lhs_ref + end + # Case rhs_ref is a Op1 + for rhs in incident(d, rhs_ref, :tgt) + d[rhs, :tgt] = lhs_ref + push!(deletions, rhs_ref) + end + # Case rhs_ref is a Op2 + for rhs in incident(d, rhs_ref, :res) + d[rhs, :res] = lhs_ref + push!(deletions, rhs_ref) + end + # Case rhs_ref is a Plus + # FIXME: this typeguard is a subsitute for refactoring into multiple dispatch + if isa(d, decapodes.SummationDecapode) + for rhs in incident(d, rhs_ref, :sum) + d[rhs, :sum] = lhs_ref + push!(deletions, rhs_ref) + end + end + # TODO: delete unused vars. The only thing stopping me from doing + # this is I don't know if CSet deletion preserves incident relations + #rem_parts!(d, :Var, sort(deletions)) + end + end + return d +end + +function recognize_types(d::decapodes.AbstractNamedDecapode) + unrecognized_types = setdiff(d[:type], [:Form0, :Form1, :Form2, :DualForm0, + :DualForm1, :DualForm2, :Literal, :Parameter, + :Constant, :infer]) + isempty(unrecognized_types) || + error("Types $unrecognized_types are not recognized.") +end + +function fill_names!(d::decapodes.AbstractNamedDecapode) + bulletcount = 1 + for i in parts(d, :Var) + if !isassigned(d[:,:name],i) || isnothing(d[i, :name]) + d[i,:name] = Symbol("•$bulletcount") + bulletcount += 1 + end + end + for e in incident(d, :∂ₜ, :op1) + s = d[e,:src] + t = d[e, :tgt] + String(d[t,:name])[1] != '•' && continue + d[t, :name] = append_dot(d[s,:name]) + end + d +end + +function make_sum_mult_unique!(d::decapodes.AbstractNamedDecapode) + snum = 1 + mnum = 1 + for (i, name) in enumerate(d[:name]) + if(name == :sum) + d[i, :name] = Symbol("sum_$(snum)") + snum += 1 + elseif(name == :mult) + d[i, :name] = Symbol("mult_$(mnum)") + mnum += 1 + end + end +end -Stores the syntactic expression of a Decapode Expression with the -model metadata for ASKEM AMR conformance. -""" -ASKEMDecaExpr(header::AMR.Header, model::Decapodes.DecaExpr) = ASKEMDecaExpr(header,model,Vector{AMR.Annotation{Symbol,Symbol}}()) +function SummationDecapode(e::decapodes.DecaExpr) + # d = SummationDecapode{Any, Any, Symbol}() + d = decapodes.SummationDecapode{Symbol, Symbol, Symbol}() + symbol_table = Dict{Symbol, Int}() -@doc """ ASKEMDecapode + for judgement in e.context + var_id = add_part!(d, :Var, name=judgement.var, type=judgement.dim) + symbol_table[judgement.var] = var_id + end -Stores the combinatorial representation of a Decapode with the -model metadata for ASKEM AMR conformance. -""" -ASKEMDecapode(header::AMR.Header, model::Decapodes.SummationDecapode) = ASKEMDecapode(header,model,Vector{AMR.Annotation{Symbol,Symbol}}()) + deletions = Vector{Int}() + for eq in e.equations + eval_eq!(eq, d, symbol_table, deletions) + end + rem_parts!(d, :Var, sort(deletions)) -StructTypes.StructType(::Type{ASKEMDeca}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{ASKEMDeca}) = :_type -StructTypes.subtypes(::Type{ASKEMDeca}) = (ASKEMDecaExpr=ASKEMDecaExpr, ASKEMDecapode=ASKEMDecapode) + recognize_types(d) -SyntacticModelsBase._dict(x::T) where {T<:Union{Decapodes.DecaExpr, Decapodes.Equation, Decapodes.Term}} = begin - Dict(:_type => typename_last(T), [k=>_dict(getfield(x, k)) for k in fieldnames(T)]...) + fill_names!(d) + d[:name] = normalize_unicode.(d[:name]) + make_sum_mult_unique!(d) + return d end -StructTypes.StructType(::Type{Decapodes.Equation}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{Decapodes.Equation}) = :_type -StructTypes.subtypes(::Type{Decapodes.Equation}) = (Eq=Eq,) -StructTypes.StructType(::Type{Decapodes.Term}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{Decapodes.Term}) = :_type -StructTypes.subtypes(::Type{Decapodes.Term}) = (Var=Decapodes.Var, - Lit=Decapodes.Lit, - Judgement=Decapodes.Judgement, - AppCirc1=Decapodes.AppCirc1, - App1=Decapodes.App1, - App2=Decapodes.App2, - Plus=Decapodes.Plus, - Mult=Decapodes.Mult, - Tan=Decapodes.Tan) end \ No newline at end of file diff --git a/src/uwd.jl b/src/uwd.jl index d6bc97b..7b7e405 100644 --- a/src/uwd.jl +++ b/src/uwd.jl @@ -3,7 +3,6 @@ module ASKEMUWDs # include("amr.jl") export Var, Typed, Untyped, Statement, UWDExpr, UWDModel, UWDTerm, context -using ..SyntacticModelsBase using ..AMR using MLStyle @@ -11,14 +10,26 @@ using StructTypes using Catlab using Catlab.RelationalPrograms using Catlab.WiringDiagrams -import Base: show +# import Base: show +using ACSets +using ACSets.InterTypes -@data Var <: AbstractTerm begin - Untyped(var::Symbol) - Typed(var::Symbol, type::Symbol) +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface + +using ..AMR.amr + +@intertypes "uwd.it" module uwd + import ..amr end +using .uwd + + @doc """ Var Variables of a UWD. Types are the domain types, ScalarField, VectorField, Dual1Form, Primal2Form NOT Float64,Complex128 @@ -30,17 +41,6 @@ Subtypes include: which are used for representing typed or untyped variables. """ -Var - -StructTypes.StructType(::Type{Var}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{Var}) = :_type -StructTypes.subtypes(::Type{Var}) = (Untyped=Untyped, Typed=Typed) - -@data UWDTerm <: AbstractTerm begin - Statement(relation::Symbol, variables::Vector{Var}) - UWDExpr(context::Vector{Var}, statements::Vector{Statement}) - UWDModel(header::AMR.Header, uwd::UWDExpr) -end @doc """ UWDTerm @@ -79,37 +79,33 @@ s = [Statement(:R, [v1,v2]), u = UWDExpr(c, s) ``` """ -UWDTerm - -StructTypes.StructType(::Type{UWDTerm}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{UWDTerm}) = :_type -StructTypes.subtypes(::Type{UWDTerm}) = (Statement=Statement, UWDExpr=UWDExpr, UWDModel=UWDModel) -varname(v::Var) = @match v begin - Untyped(v) => v - Typed(v, t) => v +varname(v::uwd.Var) = @match v begin + uwd.Untyped(v) => v + uwd.Typed(v, t) => v end -vartype(v::Var) = @match v begin - Typed(v, t) => t - Untyped(v) => :untyped +vartype(v::uwd.Var) = @match v begin + uwd.Typed(v, t) => t + uwd.Untyped(v) => :untyped end -context(t::UWDTerm) = @match t begin - Statement(R, xs) => xs - UWDExpr(context, statements) => context - UWDModel(h, uwd) => context(uwd) +context(t::uwd.UWDTerm) = @match t begin + uwd.Statement(R, xs) => xs + uwd.UWDExpr(context, statements) => context + uwd.UWDModel(h, uwd) => context(uwd) end """ show(io::IO, s::UWDTerm) generates a human readable string of the `UWDTerm` (or any sub-term). """ -function show(io::IO, s::UWDTerm) +#= +function show(io::IO, s::uwd.UWDTerm) let ! = show @match s begin - Statement(r, v) => begin print(io, "$r("); show(io, v, wrap=false); print(io, ")") end - UWDExpr(c, body) => begin + uwd.Statement(r, v) => begin print(io, "$r("); show(io, v, wrap=false); print(io, ")") end + uwd.UWDExpr(c, body) => begin map(enumerate(body)) do (i,s) if i == 1 print(io, "{ ") @@ -128,19 +124,19 @@ function show(io::IO, s::UWDTerm) print(io, " where ") show(io, c) end - UWDModel(h, uwd) => begin println(io, amr_to_string(h)); println(io, "UWD:"); !(io, uwd); end + uwd.UWDModel(h, uwd′) => begin println(io, amr_to_string(h)); println(io, "UWD:"); !(io, uwd′); end end end end -function show(io::IO, c::Vector{Var}; wrap=true) +function show(io::IO, c::Vector{uwd.Var}; wrap=true) if wrap print(io, "{") end map(enumerate(c)) do (i,s) @match s begin - Untyped(v) => print(io, v) - Typed(v, T) => print(io, "$v:$T") + uwd.Untyped(v) => print(io, v) + uwd.Typed(v, T) => print(io, "$v:$T") end if i != length(c) print(io, ", ") @@ -150,12 +146,13 @@ function show(io::IO, c::Vector{Var}; wrap=true) print(io, "}") end end +=# """ construct(::Type{RelationDiagram}, ex::UWDExpr) Builds a RelationDiagram from a UWDExpr like the `@relation` macro does for Julia Exprs. """ -function construct(::Type{RelationDiagram}, ex::UWDExpr) +function construct(::Type{RelationDiagram}, ex::uwd.UWDExpr) # If you want to understand this code, look at the schema for Relation Diagrams # to_graphviz(RelationalPrograms.SchRelationDiagram) uwd = RelationDiagram(map(varname, ex.context)) From 8c4b0474af7fed16f1878633c0244feae2432fff Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 18:10:23 -0500 Subject: [PATCH 18/35] Moved contents of composite_models_it.jl to original file for easier diff. --- src/SyntacticModels.jl | 2 +- src/composite_models.jl | 66 +++++++++++++++++++++++++---------------- test/core_it.jl | 2 +- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/SyntacticModels.jl b/src/SyntacticModels.jl index 7fd9f83..a996cdf 100644 --- a/src/SyntacticModels.jl +++ b/src/SyntacticModels.jl @@ -4,6 +4,6 @@ include("SyntacticModelsBase.jl") include("amr.jl") include("decapodes.jl") include("uwd.jl") -include("composite_models_it.jl") +include("composite_models.jl") end \ No newline at end of file diff --git a/src/composite_models.jl b/src/composite_models.jl index f2b9aef..efa65c9 100644 --- a/src/composite_models.jl +++ b/src/composite_models.jl @@ -1,52 +1,66 @@ module Composites -export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode +export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode, oapply, Open using MLStyle using Catlab -using Decapodes +using Decapodes # : SummationDecapode using StructTypes -using ..SyntacticModelsBase using ..AMR using ..ASKEMDecapodes using ..ASKEMUWDs -@data CompositeModel <: AbstractTerm begin - OpenModel(model::ASKEMDecapodes.ASKEMDecaExpr, interface::Vector{Symbol}) - OpenDecapode(model::ASKEMDecapodes.ASKEMDecapode, interface::Vector{Symbol}) - CompositeModelExpr(header::Header, composition_pattern::UWDExpr, components::Vector{CompositeModel}) -end +using ACSets +using ACSets.InterTypes -@doc """ CompositeModel +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface -```julia -@data CompositeModel <: AbstractTerm begin - OpenModel(model::ASKEMDecapodes.ASKEMDecaExpr, interface::Vector{Symbol}) - OpenDecapode(model::ASKEMDecapodes.ASKEMDecapode, interface::Vector{Symbol}) - CompositeModelExpr(header::Header, composition_pattern::UWDExpr, components::Vector{CompositeModel}) -end -``` -""" -CompositeModel -StructTypes.StructType(::Type{CompositeModel}) = StructTypes.AbstractType() -StructTypes.subtypekey(::Type{CompositeModel}) = :_type -StructTypes.subtypes(::Type{CompositeModel}) = (OpenModel=OpenModel, OpenDecapode=OpenDecapode, CompositeModelExpr) +using ..AMR.amr +using ..ASKEMDecapodes.decapodes +using ..ASKEMUWDs.uwd + +@intertypes "composite_models.it" module composites + import ..amr + import ..decapodes + import ..uwd +end +using .composites """ interface(m::CompositeModel) Extract the interface of a composite model. If the model is open, then it is the feet of the cospan. If it is a Composite, then it is the context of the uwd. """ -interface(m::CompositeModel) = @match m begin - OpenModel(M, I) => I - CompositeModelExpr(h, uwd, components) => map(ASKEMUWDs.varname, context(uwd)) +interface(m::composites.CompositeModel) = @match m begin + composites.OpenModel(M, I) => I + composites.CompositeModelExpr(h, uwd′, components) => map(ASKEMUWDs.varname, context(uwd′)) end +OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(Decapodes.SummationDecapode, :Var) + +# function Decapodes.Open(d::ASKEMDecapodes.decapodes.SummationDecapode, names::Vector{Symbol}) +function Decapodes.Open(d::decapodes.SummationDecapode, names::Vector{Symbol}) + legs = map(names) do name + FinFunction(incident(d, name, :name), nparts(d, :Var)) + end + OpenSummationDecapode(d, legs...) +end + +#= +apex(decapode::OpenSummationDecapode) = apex(decapode.cospan) +legs(decapode::OpenSummationDecapode) = legs(decapode.cospan) +feet(decapode::OpenSummationDecapode) = decapode.feet +=# + # Extract an open decapode from the decapode expression and the interface -open_decapode(d, interface) = Open(SummationDecapode(d.model), interface) -open_decapode(d::ASKEMDecaExpr, interface) = Open(SummationDecapode(d.model), interface) +open_decapode(d, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) +open_decapode(d::ASKEMDecaExpr, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) open_decapode(d::ASKEMDecapode, interface) = Open(d.model, interface) """ Catlab.oapply(m::CompositeModel) diff --git a/test/core_it.jl b/test/core_it.jl index e7cf732..1e3ed37 100644 --- a/test/core_it.jl +++ b/test/core_it.jl @@ -34,5 +34,5 @@ end include("amr_it_ex.jl") include("decapodes_it_ex.jl") include("uwd_it_ex.jl") -# include("composite_models_it_ex.jl") +include("composite_models_it_ex.jl") # include("serialization_it_mwe.jl") \ No newline at end of file From f4fe83c281f4dce25cd3aa27ec5cefec7b9d4b49 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 18:50:58 -0500 Subject: [PATCH 19/35] Moved contents of _it_ex.jl files to their corresponding _examples.jl files for difference viewing. --- test/amr_examples.jl | 20 +++++++++-- test/composite_models_examples.jl | 60 ++++++++++++++++++------------- test/core.jl | 14 ++++---- test/decapodes_examples.jl | 40 +++++++++++---------- test/runtests.jl | 2 +- test/uwd_examples.jl | 58 ++++++++++++++++++------------ 6 files changed, 116 insertions(+), 78 deletions(-) diff --git a/test/amr_examples.jl b/test/amr_examples.jl index 3d23a90..b2e40fd 100644 --- a/test/amr_examples.jl +++ b/test/amr_examples.jl @@ -4,8 +4,19 @@ using Test using ACSets using ACSets.ADTs +using ACSets.InterTypes +using Test +using OrderedCollections +import JSON +import JSON3 + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using StructTypes + nomath = Math("") -header = Header("SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") +header = Header("","SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") model = acsetspec(:(LabelledPetriNet{Symbol}), quote S(label=:S) S(label=:I) @@ -58,7 +69,8 @@ odelist = ODEList([ ]) -amr₁ = ASKEModel(header, +#= +amr₁ = AMR.ASKEModel(header, model, [ode] ) @@ -549,4 +561,6 @@ model_expr = Base.Meta.parse(modelrep) println(AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) @test_skip (AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) == modelrep -end +=# + +end # module diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index 760b15d..907644a 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -5,12 +5,18 @@ using ..SyntacticModels.Composites using MLStyle using JSON -using Decapodes +# using Decapodes using Catlab using Catlab.RelationalPrograms using Catlab.WiringDiagrams using Test +using ACSets +using ACSets.InterTypes +using Test +using OrderedCollections +import JSON +import JSON3 x = Typed(:X, :Form0) @@ -18,20 +24,20 @@ v = Typed(:V, :Form0) Q = Typed(:Q, :Form0) c = [x, Q] -s = [Statement(:oscillator, [x,v]), - Statement(:heating, [v,Q])] -u = ASKEMUWDs.UWDExpr(c, s) +s = [ASKEMUWDs.uwd.Statement(:oscillator, [x,v]), + ASKEMUWDs.uwd.Statement(:heating, [v,Q])] +u = UWDExpr(c, s) -h = AMR.Header("harmonic_oscillator", +h = Header("","harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "DecaExpr", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = Decapodes.parse_decapode(quote +dexpr = ASKEMDecapodes.parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -43,15 +49,15 @@ end ) # That gave us the first model -d1 = ASKEMDecaExpr(h, dexpr) +d1 = ASKEMDecaExpr(h, dexpr, []) # The second model is: d2 = ASKEMDecaExpr( - AMR.Header("fricative_heating", + Header("","fricative_heating", "modelreps.io/SummationDecapode", "Velocity makes it get hot, but you dissipate heat away from Q₀", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + ASKEMDecapodes.parse_decapode(quote V::Form0{Point} Q::Form0{Point} κ::Constant{Point} @@ -59,13 +65,15 @@ d2 = ASKEMDecaExpr( Q₀::Parameter{Point} ∂ₜ(Q) == κ*V + λ(Q - Q₀) - end) + end), + [] ) # Now we can assemble this bad boi: -h = AMR.Header("composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") +h = Header("","composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") m = CompositeModelExpr(h, u, [OpenModel(d1, [:X, :V]), OpenModel(d2, [:V, :Q])]) interface(m) == [:X, :Q] +#= TODO: FIXME write_json_model(m) # you can see from this little model (two coupled odes even) that the jsons will not be human editable. # now we can interpret this big data structure to execute a composition! @@ -73,7 +81,7 @@ composite = oapply(m) display(apex(composite)) to_graphviz(apex(composite)) sm_write_json_acset(apex(composite),"$(m.header.name)-acset") - +=# # TESTING NESTED COMPOSITION @@ -81,47 +89,48 @@ Q₊ = Untyped(:Q₊) Q₋ = Untyped(:Q₋) Q̇ = Untyped(:Q̇) -uwdʰ = UWDExpr([v, Q], [Statement(:drag, [v, Q₊]), Statement(:cooling, [Q₋, Q]), Statement(:superposition, [Q₊, Q₋, Q̇])]) +uwdʰ = UWDExpr([v, Q], [ASKEMUWDs.uwd.Statement(:drag, [v, Q₊]), ASKEMUWDs.uwd.Statement(:cooling, [Q₋, Q]), ASKEMUWDs.uwd.Statement(:superposition, [Q₊, Q₋, Q̇])]) drag = ASKEMDecaExpr( - AMR.Header("DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + Header("","DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), + ASKEMDecapodes.parse_decapode(quote V::Form0{Point} Q₊::Form0{Point} κ::Constant{Point} Q₊ == κ*V - end) + end), [] ) cooling = ASKEMDecaExpr( - AMR.Header("NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + Header("","NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), + ASKEMDecapodes.parse_decapode(quote Q₋::Form0{Point} Q₀::Parameter{Point} Q::Form0{Point} λ::Constant{Point} Q₋ == λ(Q-Q₀) - end) + end), [] ) superposition = ASKEMDecaExpr( - AMR.Header("LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), - Decapodes.parse_decapode(quote + Header("","LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), + ASKEMDecapodes.parse_decapode(quote X::Form0{Point} Y::Form0{Point} T::Form0{Point} T == X + Y - end) + end), [] ) -h = AMR.Header("hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") +h = Header("","hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") m = CompositeModelExpr(h,u, [OpenModel(d1, [:X, :V]), - CompositeModelExpr(AMR.Header("heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), + CompositeModelExpr(Header("","heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), uwdʰ, [OpenModel(drag, [:V, :Q₊]), OpenModel(cooling, [:Q₋, :Q]), OpenModel(superposition, [:X, :Y, :T])]) ]) +#= TODO: FIXME write_json_model(m) @testset "Composite Model Readback" begin @@ -133,4 +142,5 @@ dh = apex(oapply(m)) composite = OpenDecapode(m) hf = composite.model.header -write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) +write_json_model(ASKEMDecapode(Header("","flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) +=# \ No newline at end of file diff --git a/test/core.jl b/test/core.jl index 55f9996..ba6f186 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,7 +1,7 @@ include("../src/SyntacticModels.jl") using .SyntacticModels -using .SyntacticModels.ASKEMDecapodes +# using .SyntacticModels.ASKEMDecapodes using Test using JSON3 @@ -9,13 +9,12 @@ using JSON3 jsondir = joinpath(@__DIR__, "json") write_json_model(m, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp - JSON3.pretty(fp, Dict(m)) -end - -readback(m, prefix=joinpath(@__DIR__, "json")) = open(joinpath(jsondir, "$(m.header.name).json"), "r") do fp - JSON3.read(fp, typeof(m)) + JSON3.pretty(fp, jsonwrite(m)) end +readback(m, T, prefix=joinpath(@__DIR__, "json")) = jsonread(joinpath(jsondir, "$(m.header.name).json"),T) + +#= write_json_model(m::ASKEMDecapodes.ASKEMDecapode, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp d = Dict("header"=>m.header, "model"=>generate_json_acset(m.model), "_type"=> "ASKEMDecapode") JSON3.pretty(fp, d) @@ -24,6 +23,7 @@ end sm_write_json_acset(X, fname, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(fname).json"), "w") do fp JSON3.pretty(fp, generate_json_acset(X)) end +=# try mkdir(joinpath(@__DIR__, "json")) @@ -35,4 +35,4 @@ include("amr_examples.jl") include("decapodes_examples.jl") include("uwd_examples.jl") include("composite_models_examples.jl") -include("serialization_mwe.jl") \ No newline at end of file +# include("serialization_mwe.jl") \ No newline at end of file diff --git a/test/decapodes_examples.jl b/test/decapodes_examples.jl index 56f0495..d7e012d 100644 --- a/test/decapodes_examples.jl +++ b/test/decapodes_examples.jl @@ -1,27 +1,30 @@ # module ASKEMDecapodesExamples -using ..SyntacticModels using ..SyntacticModels.ASKEMDecapodes using ..SyntacticModels.AMR -using MLStyle -using JSON -using Catlab using ACSets -using ACSets.JSONACSets -using Decapodes +using ACSets.InterTypes using Test - -# Build the heder object describing the model. - -h = AMR.Header("harmonic_oscillator", +using OrderedCollections +import JSON +import JSON3 + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface +using StructTypes + +h = amr.Header("", "harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "DecaExpr", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = Decapodes.parse_decapode(quote +dexpr = ASKEMDecapodes.parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -32,15 +35,15 @@ dexpr = Decapodes.parse_decapode(quote end ) -annot = [AMR.Annotation(:X,:Form0,AMR.Name("The X variable."))] +annot = [amr.Annotation(:X,:Form0,amr.Name("The X variable."))] # Bundle the DecaExpr with the header metadata. -mexpr = ASKEMDecaExpr(h, dexpr, annot) +mexpr = ASKEMDecapodes.decapodes.ASKEMDecaExpr(h, dexpr, annot) # Convert a the DecaExpr to a SummationDecapode which is the # combinatorial representation. The converter lives in Decapodes/src/language.jl. -d = Decapodes.SummationDecapode(mexpr.model) +d = ASKEMDecapodes.SummationDecapode(mexpr.model) # We want different metadata for this representation. # The Summation prefix just means that this decapodes have @@ -48,12 +51,12 @@ d = Decapodes.SummationDecapode(mexpr.model) # The summation operator happens in physics so often, # that you want to bake in some specialized handling to the data structure. -h = AMR.Header("harmonic_oscillator", +h = amr.Header("","harmonic_oscillator", "modelreps.io/SummationDecapode", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "SummationDecapode", "v1.0") -mpode = ASKEMDecapode(h, d, annot) +mpode = ASKEMDecapodes.decapodes.ASKEMDecapode(h, d, annot) # The syntactic representation can be serialized as JSON. @@ -63,12 +66,11 @@ write_json_model(mexpr) # We could also use the JSON serialization built into Catlab # to serialize the resulting combinatorial representation -sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") -# end +# sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") # Can we read back the models we just wrote? @testset "Decapodes Readback" begin - mexpr′ = readback(mexpr) + mexpr′ = readback(mexpr,ASKEMDecapodes.decapodes.ASKEMDeca) @test JSON3.write(mexpr) == JSON3.write(mexpr′) end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index dfb961c..6a3ff72 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,5 +3,5 @@ using Test using SyntacticModels @testset "Core" begin - include("core_it.jl") + include("core.jl") end diff --git a/test/uwd_examples.jl b/test/uwd_examples.jl index 8b63d05..34f641d 100644 --- a/test/uwd_examples.jl +++ b/test/uwd_examples.jl @@ -9,6 +9,17 @@ using Catlab.RelationalPrograms using Catlab.WiringDiagrams using Catlab.Graphics +using ACSets +using ACSets.InterTypes +using OrderedCollections + +using Reexport +@reexport using MLStyle +@reexport using ACSets +using ACSets.ADTs +using ACSets.ACSetInterface +using StructTypes + # This example follows what in current catlab would be given as #= @@ -19,32 +30,33 @@ using Catlab.Graphics end =# -v1 = Typed(:x, :X) -v2 = Typed(:y, :Y) -v3 = Typed(:z, :Z) -v4 = Untyped(:u) +v1 = ASKEMUWDs.uwd.Typed(:x, :X) +v2 = ASKEMUWDs.uwd.Typed(:y, :Y) +v3 = ASKEMUWDs.uwd.Typed(:z, :Z) +v4 = ASKEMUWDs.uwd.Untyped(:u) c = [v1, v3] -s = [Statement(:R, [v1,v2]), - Statement(:S, [v2,v3]), - Statement(:T, [v3,v2, v4])] -u = UWDExpr(c, s) +s = [ASKEMUWDs.uwd.Statement(:R, [v1,v2]), + ASKEMUWDs.uwd.Statement(:S, [v2,v3]), + ASKEMUWDs.uwd.Statement(:T, [v3,v2, v4])] +u = ASKEMUWDs.uwd.UWDExpr(c, s) @testset "UWDExpr Readback" begin - s = JSON3.write(u) - ujson = JSON3.read(s, UWDTerm) - # FIXME: can't compare u and ujson, because they aren't same object - # but they have the same JSON string - @test s == JSON3.write(ujson) + s = jsonwrite(u) + ujson = jsonread(s, ASKEMUWDs.uwd.UWDTerm) + @test s == jsonwrite(ujson) end -uwd = ASKEMUWDs.construct(RelationDiagram, u) -h = AMR.Header("rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") +uwd′ = ASKEMUWDs.construct(RelationDiagram, u) + + +h = AMR.Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") -mexpr = UWDModel(h, u) -write_json_model(mexpr) -mexpr′ = readback(mexpr) +mexpr = ASKEMUWDs.uwd.UWDModel(h, u) @testset "UWD Readback" begin + write_json_model(mexpr) + mexpr′ = readback(mexpr,ASKEMUWDs.uwd.UWDTerm) + @test mexpr.header == mexpr′.header @test mexpr.uwd.context == mexpr′.uwd.context @test mexpr.uwd.context == mexpr′.uwd.context @@ -54,11 +66,11 @@ mexpr′ = readback(mexpr) @test mexpr.uwd.statements[2].variables == mexpr′.uwd.statements[2].variables @test mexpr.uwd.statements[3].relation == mexpr′.uwd.statements[3].relation @test mexpr.uwd.statements[3].variables == mexpr′.uwd.statements[3].variables - # TODO: overload == for statements - # @test all(mexpr.uwd.statements .== mexpr′.uwd.statements) - # @test mexpr == mexpr′ + + @test all(mexpr.uwd.statements .== mexpr′.uwd.statements) + @test mexpr == mexpr′ end -to_graphviz(uwd, box_labels=:name, junction_labels=:variable) +to_graphviz(uwd′, box_labels=:name, junction_labels=:variable) -display(uwd) \ No newline at end of file +display(uwd′) \ No newline at end of file From 19f98d25d26f8a8138ed6ec27fd7896575605a22 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 19:12:19 -0500 Subject: [PATCH 20/35] deleted intertype versions of amr.jl and decapodes.jl --- src/amr_it.jl | 391 -------------------------------------------- src/decapodes_it.jl | 272 ------------------------------ 2 files changed, 663 deletions(-) delete mode 100644 src/amr_it.jl delete mode 100644 src/decapodes_it.jl diff --git a/src/amr_it.jl b/src/amr_it.jl deleted file mode 100644 index 8c88717..0000000 --- a/src/amr_it.jl +++ /dev/null @@ -1,391 +0,0 @@ -module AMR - -export amr, Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, - Rate, Initial, Parameter, Time, - StandardUniform, Uniform, StandardNormal, Normal, PointMass, - Semantic, Header, ODERecord, ODEList, ASKEModel, # Typing, - distro_string, # amr_to_string, - Annotation, Note, Name, Description, Grounding, Units - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface -using StructTypes - -# using ..SyntacticModelsBase - - -@intertypes "amr.it" module amr end - -using .amr - - -function distro_string(d::amr.Distribution) - @match d begin - amr.StandardUniform(s) => "U(0,1)" - amr.Uniform(min, max) => "U($min,$max)" - amr.StandardNormal(s) => "N(0,1)" - amr.Normal(mu, var) => "N($mu,$var)" - amr.PointMass(value) => "δ($value)" - end -end - -function distro_expr(d::amr.Distribution) - return Base.Meta.parse(distro_string(d)) -end - -#= - -function note_string(n::Note) - @match n begin - Name(n) => "Name($n)" - Description(d) => "Description($d)" - Grounding(ont, ident) => "Grounding($ont,$ident)" - Units(e) => "Units($e)" - end -end - -function note_expr(n::Note) - return Base.Meta.parse(note_string(n)) -end - -padlines(ss::Vector, n) = map(ss) do s - " "^n * s -end -padlines(s::String, n=2) = join(padlines(split(s, "\n"), n), "\n") -=# -function amr_to_string(amr′) - let ! = amr_to_string - @match amr′ begin - s::String => s - amr.Math(s) => !s - amr.Presentation(s) => " $s " - u::amr.Unit => !u.expression - d::amr.Distribution => distro_string(d) - amr.Time(id, u) => "$id::Time{$(!u)}\n" - amr.Rate(t, f) => "$t::Rate = $(f.expression)" - amr.Initial(t, f) => "$t::Initial = $(f.expression)" - amr.Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" - amr.Header(id, name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" - amr.Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" - m::ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" - amr.ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" - amr.ODERecord(rts, init, para, time) => join(vcat(["ODE_Record = begin\n"], !rts , !init, !para, [!time, "end"]), "\n") - vs::Vector{amr.Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") - vs::Vector{amr.Semantic} => join(map(!, vs), "\n\n") - xs::Vector => map(!, xs) - # amr.Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" - # amr.ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" - amr.Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_string(n))" - end - end -end -#= -block(exprs) = begin - q = :(begin - - end) - append!(q.args, exprs) - return q -end - -extract_acsetspec(s::String) = join(split(s, " ")[2:end], " ") |> Meta.parse - - -function amr_to_expr(amr) - let ! = amr_to_expr - @match amr begin - s::String => s - Math(s) => :(Math($(!s))) - Presentation(s) => :(Presentation($(!s))) - u::Unit => u.expression - d::Distribution => distro_expr(d) - Time(id, u) => :($id::Time{$(!u)}) - Rate(t, f) => :($t::Rate = $(f.expression)) - Initial(t, f) => :($t::Initial = $(f.expression)) - Observable(id, n, states, f) => begin "$n"; :(@doc $x $id::Observable = $(f.expression)($states)) end - Header(name, s, d, sn, mv) => begin x = "ASKE Model Representation: $name$mv :: $sn \n $s\n\n$d"; :(@doc $x) end - Parameter(t, n, d, u, v, dist) => begin x = "$n-- $d"; :(@doc $x $t::Parameter{$(!u)} = $v ~ $(!dist)) end - m::ACSetSpec => :(Model = begin $(extract_acsetspec(sprint(show, m))) end) - ODEList(l) => :(ODE_Equations = $(block(map(!, l)))) - ODERecord(rts, init, para, time) => :(ODE_Record = (rates=$(!rts), initials=$(!init), parameters=$(!para), time=!time)) - vs::Vector{Pair} => begin ys = map(vs) do v; :($(v[1]) => $(v[2])) end; block(ys) end - vs::Vector{Semantic} => begin ys = map(!, vs); block(ys) end - xs::Vector => begin ys = map(!, xs); block(ys) end - Typing(system, map) => :(Typing = $(!system); TypeMap = $(block(map))) - ASKEModel(h, m, s) => :($(!h);$(!m);$(!s)) - Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_expr(n))" - end - end -end - -optload(d, path, default=nothing) = begin - let ! = optload - @match path begin - s::Symbol => !(d, string(s), default) - s::String => get(d, s, default) - [addr] => !(d, addr, default) - [head, args...] => !(get(d, head, Dict()), args, default) - _=> error("Bad recursion in optload($d, $path)") - end - end -end - -function petrispec(dict::AbstractDict) - findkwarg(kwarg::Symbol, d::AbstractDict, path, default=nothing) = Kwarg(kwarg, Value(optload(d, path, default))) - loadstate(s) = begin - Statement(:S, [findkwarg(k, s, p, :nothing) for (k,p) in [(:id, "id"), (:name, "name"), (:units, ["units", "expression"])]]) - end - states = [loadstate(s) for s in dict["states"]] - transi = [ - Statement(:T, - [Kwarg(:id, Value(Symbol(t["id"]))), Kwarg(:name, Value(t["properties"]["name"])), Kwarg(:desc, Value(t["properties"]["description"])) ] - ) for t in dict["transitions"]] - - inputs = [[ - Statement(:I, - [Kwarg(:is, Value(i)), Kwarg(:it, Value(t["id"]))]) for i in t["input"]] for t in dict["transitions"] - ] |> Base.Flatten |> collect - outputs = [[ - Statement(:O, - [Kwarg(:os, Value(i)), Kwarg(:ot, Value(t["id"]))]) for i in t["output"]] for t in dict["transitions"] - ] |> Base.Flatten |> collect - ACSetSpec(:AMRPetriNet, vcat(states, transi, inputs, outputs)) -end - -function load(::Type{Unit}, d::AbstractDict) - ud = get(d, "units", Dict("expression"=>"", "expression_mathml"=>"")) - u = Unit(ud["expression"], Presentation(ud["expression_mathml"])) -end - -function load(::Type{Time}, t::AbstractDict) - Time(Symbol(t["id"]), load(Unit, t)) -end - -function load(::Type{Rate}, r::AbstractDict) - f = ExpressionFormula(r["expression"], Presentation(r["expression_mathml"])) - Rate(Symbol(r["target"]), f) -end - -function load(::Type{Initial}, d::AbstractDict) - f = ExpressionFormula(d["expression"], Presentation(d["expression_mathml"])) - Initial(Symbol(d["target"]), f) -end - -function load(::Type{Distribution}, d::AbstractDict) - @match d begin - Dict("type"=>"StandardUniform1") => StandardUniform - Dict("type"=>"StandardNormal") => StandardNormal - Dict("type"=>"Uniform", "parameters"=>p) => Uniform(p["minimum"], p["maximum"]) - Dict("type"=>"Uniform1", "parameters"=>p) => Uniform(p["minimum"], p["maximum"]) - Dict("type"=>"Normal", "parameters"=>p) => Normal(p["mu"], p["var"]) - Dict("type"=>"PointMass", "parameters"=>p) => PointMass(p["value"]) - end -end - -load(::Type{Distribution}, ::Nothing) = PointMass(missing) - -function load(::Type{Note}, d::AbstractDict) - @match d begin - Dict("type"=>"Name", "parameters"=>p) => Name(p["str"]) - Dict("type"=>"Description", "parameters"=>p) => Description(p["str"]) - Dict("type"=>"Grounding", "parameters"=>p) => Grounding(p["ontology"], p["identifier"]) - Dict("type"=>"Units", "parameters"=>p) => Units(p["expression"]) - end -end -function load(::Type{Annotation}, d::AbstractDict) - Annotation(d["entity"], d["type"], load(Note,d["note"])) -end - -function load(::Type{Parameter}, d::AbstractDict) - u = load(Unit, d) - Parameter( - Symbol(d["id"]), - d["name"], - d["description"], - u, - d["value"], - load(Distribution, get(d,"distribution", nothing)) - ) -end - -function load(::Type{ODERecord}, d::AbstractDict) - time = load(Time, d["time"]) - rate(x) = load(Rate, x) - initial(x) = load(Initial, x) - parameter(x) = load(Parameter, x) - rates = rate.(d["rates"]) - initials = initial.(d["initials"]) - parameters = parameter.(d["parameters"]) - - ODERecord(rates, initials, parameters, time) -end - -function load(::Type{Header}, d::AbstractDict) - @match d begin - Dict("name"=>n, "schema"=>s, "description"=>d, "schema_name"=>sn, "model_version"=>mv) => Header(n,s,d,sn,mv) - _ => error("Information for Header was not found in $d") - end -end - -function load(::Type{Typing}, d::AbstractDict) - @match d begin - Dict("type_system"=>s, "type_map"=>m) => begin @show m; Typing(petrispec(s), [x[1]=> x[2] for x in m]) end - _ => error("Typing judgement was not properly encoded in $d") - end -end - -function load(::Type{ASKEModel}, d::AbstractDict) - hdr = load(Header, d) - hdr.schema_name == "petrinet" || error("only petrinet models are supported") - mdl = petrispec(d["model"]) - sem = [] - if haskey(d["semantics"], "ode") - push!(sem, load(ODERecord, d["semantics"]["ode"])) - end - if haskey(d["semantics"], "typing") - push!(sem, load(Typing, d["semantics"]["typing"])) - end - ASKEModel(hdr, mdl, sem) -end - -using MLStyle.Modules.AST - -function load(::Type{Time}, ex::Expr) - @matchast ex quote - $a::Time{} => Time(a, Unit("", Math(""))) - $a::Time{$b} => Time(a, load(Unit, b)) - _ => error("Time was not properly encoded as Expr $ex") - end -end - -function load(::Type{Unit}, ex::Union{Symbol, Expr}) - Unit(string(ex), Math("")) -end - -function load(::Type{ExpressionFormula}, ex::Expr) - ExpressionFormula(string(ex), Math(string(ex))) -end - -function load(::Type{Rate}, ex::Expr) - @matchast ex quote - ($a::Rate = $ex) => Rate(a, load(ExpressionFormula, ex)) - ($a::Rate{$u} = $ex) => Rate(a, load(ExpressionFormula, ex)) - _ => error("Rate was not properly encoded as Expr $ex") - end -end - -function load(::Type{Initial}, ex::Expr) - @matchast ex quote - ($a::Initial = $ex) => Rate(a, load(ExpressionFormula, ex)) - ($a::Initial{$u} = $ex) => Rate(a, load(ExpressionFormula, ex)) - _ => error("Rate was not properly encoded as Expr $ex") - end -end - -function docval(exp::Expr) - s, ex = @match exp begin - Expr(:macrocall, var"@doc", _, s, ex) => (s,ex) - _ => error("Could not match documented value in $exp") - end - name, desc = split(s, "--") - return strip(name), strip(desc), ex -end - -function load(d::Type{Distribution}, ex::Expr) - @matchast ex quote - U(0,1) => StandardUniform - U($min,$max) => Uniform(min, max) - N(0,1) => StandardNormal - N($mu,$var) => Normal(mu, var) - δ($value) => PointMass(value) - _ => error("Failed to find distribution in $ex") - end -end - -function load(::Type{Parameter}, ex::Expr) - name, desc, ex = docval(ex) - id, u, val, dist = @matchast ex quote - ($id::Parameter{} = ($val ~ $d)) => (id, nounit, val, load(Distribution, d)) - ($id::Parameter{$u} = ($val ~ $d)) => (id, load(Unit, u), val, load(Distribution, d)) - ($id::Parameter{} = $val) => (id, nounit, val, PointMass(missing)) - ($id::Parameter{$u} = $val) => (id, load(Unit, u), val, PointMass(missing)) - end - Parameter(id, name, desc, u, val, dist) -end - -function load(::Type{ODEList}, ex::Expr) - map(ex.args[2].args) do arg - try - return load(Rate, arg) - catch ErrorException - try - return load(Initial, arg) - catch ErrorException - try - return load(Parameter, arg) - catch ErrorException - try - return load(Time, arg) - catch - return nothing - end - end - end - end - end |> x->filter(!isnothing, x) |> ODEList -end - -function load(::Type{Header}, ex::String) - hdr, schema, _, desc, _ = split(ex, "\n") - hdr, schema_name = split(hdr, "::") - _, hdr = split(hdr, ":") - name, version = split(hdr, "@") - Header(strip(name), strip(schema), strip(desc), strip(schema_name), strip(version)) -end - -function load(::Type{ACSetSpec}, ex::Expr) - let ! = x->load(ACSetSpec, x) - @match ex begin - Expr(:(=), name, body) => @match body.args[2] begin - Expr(:(=), type, body) => acsetspec(type, body) - end - Expr(:block, lnn, body) => !(body) - _ => ex - end - end -end - -function load(::Type{Typing}, ex::Expr) - let !(x) = load(Typing, x) - @match ex begin - Expr(:(=), :Model, body) => load(ACSetSpec, ex) - Expr(:(=), :TypeMap, list) => !list - Expr(:(=), :Typing, body) => Typing(!(body.args[2]), !(body.args[4])) - Expr(:vect, args...) => map(args) do arg - @match arg begin - Expr(:call, :(=>), a, b) => Pair(a,b) - _ => error("The type map is expected to be pairs defined with a => fa. Got $arg") - end - end - _ => error("Could not processing Typing assignment from $ex") - end - end -end - -function load(::Type{ASKEModel}, ex::Expr) - elts = map(ex.args) do arg - @match arg begin - Expr(:macrocall, var"@doc", _, s, ex) => (load(Header, s), load(ACSetSpec, ex)) - Expr(:(=), :ODE_Record, body) => load(ODEList, arg) - Expr(:(=), :ODE_Equations, body) => load(ODEList, arg) - Expr(:(=), :Typing, body) => load(Typing, arg) - _ => arg - end - end - ASKEModel(elts[2][1], elts[2][2], [elts[4], elts[6]]) -end -=# -end # module end \ No newline at end of file diff --git a/src/decapodes_it.jl b/src/decapodes_it.jl deleted file mode 100644 index f757620..0000000 --- a/src/decapodes_it.jl +++ /dev/null @@ -1,272 +0,0 @@ -module ASKEMDecapodes - -export ASKEMDecaExpr, ASKEMDecapode, ASKEMDeca, SummationDecapode, parse_decapode - -# using ..SyntacticModelsBase -using ..AMR - -using StructTypes -# using Decapodes -using MLStyle - -using ACSets -using ACSets.InterTypes - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface - -import Unicode - -using ..AMR.amr - -@intertypes "decapodes.it" module decapodes - import ..amr -end - -using .decapodes - - -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') - -term(s::Symbol) = decapodes.Var(normalize_unicode(s)) -term(s::Number) = decapodes.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) => decapodes.Tan(decapodes.Var(b)) - Expr(:call, :dt, b) => decapodes.Tan(decapodes.Var(b)) - - Expr(:call, Expr(:call, :∘, a...), b) => decapodes.AppCirc1(a, term(b)) - Expr(:call, a, b) => decapodes.App1(a, term(b)) - - Expr(:call, :+, xs...) => decapodes.Plus(term.(xs)) - Expr(:call, f, x, y) => decapodes.App2(f, term(x), term(y)) - - # TODO: Will later be converted to Op2's or schema has to be changed to include multiplication - Expr(:call, :*, xs...) => decapodes.Mult(term.(xs)) - - x => error("Cannot construct term from $x") - end -end - -function parse_decapode(expr::Expr) - stmts = map(expr.args) do line - @match line begin - ::LineNumberNode => missing - # TODO: If user doesn't provide space, this gives a temp space so we can continue to construction - # For now spaces don't matter so this is fine but if they do, this will need to change - Expr(:(::), a::Symbol, b::Symbol) => decapodes.Judgement(decapodes.Var(a).name, b, :I) - Expr(:(::), a::Expr, b::Symbol) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b, :I), a.args) - - Expr(:(::), a::Symbol, b) => decapodes.Judgement(decapodes.Var(a).name, b.args[1], b.args[2]) - Expr(:(::), a::Expr, b) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b.args[1], b.args[2]), a.args) - - Expr(:call, :(==), lhs, rhs) => decapodes.Eq(term(lhs), term(rhs)) - _ => error("The line $line is malformed") - end - end |> skipmissing |> collect - judges = [] - eqns = [] - foreach(stmts) do s - @match s begin - ::decapodes.Judgement => push!(judges, s) - ::Vector{decapodes.Judgement} => append!(judges, s) - ::decapodes.Eq => push!(eqns, s) - _ => error("Statement containing $s of type $(typeof(s)) was not added.") - end - end - decapodes.DecaExpr(judges, eqns) -end - -### - -# NOTE: the var in a Judgement in decapodes.it is just a Symbol. It does not have a name field, so that was removed -# to_decapode helper functions -reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}) = - let ! = reduce_term! - @match t begin - decapodes.Var(x) => begin - if haskey(syms, x) - syms[x] - else - res_var = add_part!(d, :Var, name = x, type=:infer) - syms[x] = res_var - end - end - decapodes.Lit(x) => begin - if haskey(syms, x) - syms[x] - else - res_var = add_part!(d, :Var, name = x, type=:Literal) - syms[x] = res_var - end - end - decapodes.App1(f, t) || decapodes.AppCirc1(f, t) => begin - res_var = add_part!(d, :Var, type=:infer) - add_part!(d, :Op1, src=!(t,d,syms), tgt=res_var, op1=f) - return res_var - end - decapodes.App2(f, t1, t2) => begin - res_var = add_part!(d, :Var, type=:infer) - add_part!(d, :Op2, proj1=!(t1,d,syms), proj2=!(t2,d,syms), res=res_var, op2=f) - return res_var - end - decapodes.Plus(ts) => begin - summands = [!(t,d,syms) for t in ts] - res_var = add_part!(d, :Var, type=:infer, name=:sum) - n = add_part!(d, :Σ, sum=res_var) - map(summands) do s - add_part!(d, :Summand, summand=s, summation=n) - end - return res_var - end - # TODO: Just for now assuming we have 2 or more terms - decapodes.Mult(ts) => begin - multiplicands = [!(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 - decapodes.Tan(t) => begin - # TODO: this is creating a spurious variable with the same name - txv = add_part!(d, :Var, type=:infer) - tx = add_part!(d, :TVar, incl=txv) - tanop = add_part!(d, :Op1, src=!(t,d,syms), tgt=txv, op1=DerivOp) - return txv #syms[x[1]] - end - _ => throw("Inline type judgements not yet supported!") - end - end - -function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) - @match eq begin - decapodes.Eq(t1, t2) => begin - lhs_ref = reduce_term!(t1,d,syms) - rhs_ref = reduce_term!(t2,d,syms) - - # Always let the a named variable take precedence - # TODO: If we have variable to variable equality, we want - # some kind of way to check track of this equality - ref_pair = (t1, t2) - @match ref_pair begin - (decapodes.Var(a), decapodes.Var(b)) => return d - (t1, decapodes.Var(b)) => begin - lhs_ref, rhs_ref = rhs_ref, lhs_ref - end - _ => nothing - end - - # Make rhs_ref equal to lhs_ref and adjust all its incidents - - # Case rhs_ref is a Tan - # WARNING: Don't push to deletion here because all TanVars should have a - # corresponding Op1. Pushing here would create a duplicate which breaks rem_parts! - for rhs in incident(d, rhs_ref, :incl) - d[rhs, :incl] = lhs_ref - end - # Case rhs_ref is a Op1 - for rhs in incident(d, rhs_ref, :tgt) - d[rhs, :tgt] = lhs_ref - push!(deletions, rhs_ref) - end - # Case rhs_ref is a Op2 - for rhs in incident(d, rhs_ref, :res) - d[rhs, :res] = lhs_ref - push!(deletions, rhs_ref) - end - # Case rhs_ref is a Plus - # FIXME: this typeguard is a subsitute for refactoring into multiple dispatch - if isa(d, decapodes.SummationDecapode) - for rhs in incident(d, rhs_ref, :sum) - d[rhs, :sum] = lhs_ref - push!(deletions, rhs_ref) - end - end - # TODO: delete unused vars. The only thing stopping me from doing - # this is I don't know if CSet deletion preserves incident relations - #rem_parts!(d, :Var, sort(deletions)) - end - end - return d -end - -function recognize_types(d::decapodes.AbstractNamedDecapode) - unrecognized_types = setdiff(d[:type], [:Form0, :Form1, :Form2, :DualForm0, - :DualForm1, :DualForm2, :Literal, :Parameter, - :Constant, :infer]) - isempty(unrecognized_types) || - error("Types $unrecognized_types are not recognized.") -end - -function fill_names!(d::decapodes.AbstractNamedDecapode) - bulletcount = 1 - for i in parts(d, :Var) - if !isassigned(d[:,:name],i) || isnothing(d[i, :name]) - d[i,:name] = Symbol("•$bulletcount") - bulletcount += 1 - end - end - for e in incident(d, :∂ₜ, :op1) - s = d[e,:src] - t = d[e, :tgt] - String(d[t,:name])[1] != '•' && continue - d[t, :name] = append_dot(d[s,:name]) - end - d -end - -function make_sum_mult_unique!(d::decapodes.AbstractNamedDecapode) - snum = 1 - mnum = 1 - for (i, name) in enumerate(d[:name]) - if(name == :sum) - d[i, :name] = Symbol("sum_$(snum)") - snum += 1 - elseif(name == :mult) - d[i, :name] = Symbol("mult_$(mnum)") - mnum += 1 - end - end -end - -function SummationDecapode(e::decapodes.DecaExpr) - # d = SummationDecapode{Any, Any, Symbol}() - d = decapodes.SummationDecapode{Symbol, Symbol, Symbol}() - symbol_table = Dict{Symbol, Int}() - - for judgement in e.context - var_id = add_part!(d, :Var, name=judgement.var, type=judgement.dim) - symbol_table[judgement.var] = var_id - end - - deletions = Vector{Int}() - for eq in e.equations - eval_eq!(eq, d, symbol_table, deletions) - end - rem_parts!(d, :Var, sort(deletions)) - - recognize_types(d) - - fill_names!(d) - d[:name] = normalize_unicode.(d[:name]) - make_sum_mult_unique!(d) - return d -end - - - -end \ No newline at end of file From 233896f544e940d1cf7c12cd626f1161c75a4117 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Wed, 6 Dec 2023 19:16:43 -0500 Subject: [PATCH 21/35] Removed remaining intertype versions of src and test files for easier diff view. --- src/composite_models_it.jl | 122 ------- src/uwd_it.jl | 191 ----------- test/amr_it_ex.jl | 575 --------------------------------- test/composite_models_it_ex.jl | 148 --------- test/core_it.jl | 38 --- test/decapodes_it_ex.jl | 103 ------ test/uwd_it_ex.jl | 94 ------ 7 files changed, 1271 deletions(-) delete mode 100644 src/composite_models_it.jl delete mode 100644 src/uwd_it.jl delete mode 100644 test/amr_it_ex.jl delete mode 100644 test/composite_models_it_ex.jl delete mode 100644 test/core_it.jl delete mode 100644 test/decapodes_it_ex.jl delete mode 100644 test/uwd_it_ex.jl diff --git a/src/composite_models_it.jl b/src/composite_models_it.jl deleted file mode 100644 index 11ffdb2..0000000 --- a/src/composite_models_it.jl +++ /dev/null @@ -1,122 +0,0 @@ -module Composites - -export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode, oapply, Open - -using MLStyle -using Catlab -using Decapodes # : SummationDecapode -using StructTypes - -# using ..SyntacticModelsBase -using ..AMR -using ..ASKEMDecapodes -using ..ASKEMUWDs - -using ACSets -using ACSets.InterTypes - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface - - -using ..AMR.amr -using ..ASKEMDecapodes.decapodes -using ..ASKEMUWDs.uwd - -#= -@intertypes "../src/amr.it" module amr end -using .amr -@intertypes "../src/decapodes.it" module decapodes - import ..amr -end -using .decapodes -@intertypes "../src/uwd.it" module uwd - import ..amr -end -using .uwd -@intertypes "../src/composite_models.it" module composites - import ..amr - import ..decapodes - import ..uwd -end -using .composites -=# - -@intertypes "composite_models.it" module composites - import ..amr - import ..decapodes - import ..uwd -end - -using .composites - -""" interface(m::CompositeModel) - -Extract the interface of a composite model. If the model is open, then it is the feet of the cospan. If it is a Composite, then it is the context of the uwd. -""" -interface(m::composites.CompositeModel) = @match m begin - composites.OpenModel(M, I) => I - composites.CompositeModelExpr(h, uwd′, components) => map(ASKEMUWDs.varname, context(uwd′)) -end - - - -OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(Decapodes.SummationDecapode, :Var) - -# function Decapodes.Open(d::ASKEMDecapodes.decapodes.SummationDecapode, names::Vector{Symbol}) -function Decapodes.Open(d::decapodes.SummationDecapode, names::Vector{Symbol}) - legs = map(names) do name - FinFunction(incident(d, name, :name), nparts(d, :Var)) - end - OpenSummationDecapode(d, legs...) -end - -#= -apex(decapode::OpenSummationDecapode) = apex(decapode.cospan) -legs(decapode::OpenSummationDecapode) = legs(decapode.cospan) -feet(decapode::OpenSummationDecapode) = decapode.feet -=# - -# Extract an open decapode from the decapode expression and the interface -open_decapode(d, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) -open_decapode(d::ASKEMDecaExpr, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) -open_decapode(d::ASKEMDecapode, interface) = Open(d.model, interface) - -""" Catlab.oapply(m::CompositeModel) - -CompositeModels can be flattened into a single level of model with the oapply function. - -!!! warning - Because the oapply algorithm operates on the compute graph representation of the equations, it does not produce syntactic equations. - Calls to oapply produce instances of OpenDecapode and not DecaExpr. - Software that expects to consume decapodes should plan to interact with both forms. -""" -function Catlab.oapply(m::CompositeModel) - let ! = oapply - @match m begin - # For a primitive model we just attach the interface - OpenModel(M, I) => open_decapode(M,I) - OpenDecapode(M, I) => open_decapode(M,I) - # For a composite model, we have to recurse - CompositeModelExpr(h, pattern, components) => begin - uwd = ASKEMUWDs.construct(RelationDiagram, pattern) - Ms = map(m.components) do mᵢ; - !(mᵢ) # oapply all the component models recursively - end - # OpenDecapode(ASKEMDecapode(h, apex(!(uwd, Ms))), interface(m)) # Then we call the oapply from Decapodes. - Open(apex(!(uwd, Ms)), uwd[[:outer_junction, :variable]]) # Then we call the oapply from Decapodes. - end - end - end -end - -function OpenDecapode(m::CompositeModel) - composite = oapply(m) - feet = map(l->only(dom(l)[:name]), legs(composite)) - OpenDecapode(ASKEMDecapode(m.header,apex(composite)), feet) -end - -end \ No newline at end of file diff --git a/src/uwd_it.jl b/src/uwd_it.jl deleted file mode 100644 index 85066f9..0000000 --- a/src/uwd_it.jl +++ /dev/null @@ -1,191 +0,0 @@ -module ASKEMUWDs - -# include("amr.jl") -export Var, Typed, Untyped, Statement, UWDExpr, UWDModel, UWDTerm, context - -# using ..SyntacticModelsBase -using ..AMR - -using MLStyle -using StructTypes -using Catlab -using Catlab.RelationalPrograms -using Catlab.WiringDiagrams -# import Base: show - -using ACSets -using ACSets.InterTypes - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface - - -#= -@intertypes "amr.it" module AMR end - -using .AMR -=# - -using ..AMR.amr - -@intertypes "uwd.it" module uwd - import ..amr -end - -using .uwd - - -@doc """ Var - -Variables of a UWD. Types are the domain types, ScalarField, VectorField, Dual1Form, Primal2Form NOT Float64,Complex128 - -Subtypes include: - -1. Untyped(var::Symbol) -1. Typed(var::Symbol, type::Symbol) - -which are used for representing typed or untyped variables. -""" - -@doc """ UWDTerm - -Term specifying UWD. - -Subtypes -======== - -1. UWDModel: A header and UWD Expr -1. UWDExpr: A Context of variables and a list of statements defining a UWD -1. Statement: R(x,y,z) a relation that acts on its arguments (which are Vars) - -Example -======= - -To specify the following relation macro: -```julia -@relation (x:X, z:Z) where y:Y begin - R(x,y) - S(y,z) - T(z,y,u) -end -``` - -Use the following SyntacticModels UWDTerm: - -```julia -v1 = Typed(:x, :X) -v2 = Typed(:y, :Y) -v3 = Typed(:z, :Z) -v4 = Untyped(:u) -c = [v1, v3] -s = [Statement(:R, [v1,v2]), - Statement(:S, [v2,v3]), - Statement(:T, [v3,v2, v4])] -u = UWDExpr(c, s) -``` -""" - -varname(v::uwd.Var) = @match v begin - uwd.Untyped(v) => v - uwd.Typed(v, t) => v -end - -vartype(v::uwd.Var) = @match v begin - uwd.Typed(v, t) => t - uwd.Untyped(v) => :untyped -end - -context(t::uwd.UWDTerm) = @match t begin - uwd.Statement(R, xs) => xs - uwd.UWDExpr(context, statements) => context - uwd.UWDModel(h, uwd) => context(uwd) -end - -""" show(io::IO, s::UWDTerm) - -generates a human readable string of the `UWDTerm` (or any sub-term). -""" -#= -function show(io::IO, s::uwd.UWDTerm) - let ! = show - @match s begin - uwd.Statement(r, v) => begin print(io, "$r("); show(io, v, wrap=false); print(io, ")") end - uwd.UWDExpr(c, body) => begin - map(enumerate(body)) do (i,s) - if i == 1 - print(io, "{ ") - show(io, s) - print(io, "\n") - elseif i == length(body) - print(io, " ") - show(io, s) - print(io, " }") - else - print(io, " ") - show(io, s) - print(io, "\n") - end - end - print(io, " where ") - show(io, c) - end - uwd.UWDModel(h, uwd′) => begin println(io, amr_to_string(h)); println(io, "UWD:"); !(io, uwd′); end - end - end -end - -function show(io::IO, c::Vector{uwd.Var}; wrap=true) - if wrap - print(io, "{") - end - map(enumerate(c)) do (i,s) - @match s begin - uwd.Untyped(v) => print(io, v) - uwd.Typed(v, T) => print(io, "$v:$T") - end - if i != length(c) - print(io, ", ") - end - end - if wrap - print(io, "}") - end -end -=# - -""" construct(::Type{RelationDiagram}, ex::UWDExpr) - -Builds a RelationDiagram from a UWDExpr like the `@relation` macro does for Julia Exprs. -""" -function construct(::Type{RelationDiagram}, ex::uwd.UWDExpr) - # If you want to understand this code, look at the schema for Relation Diagrams - # to_graphviz(RelationalPrograms.SchRelationDiagram) - uwd = RelationDiagram(map(varname, ex.context)) - junctions = Dict() - # first we add in all the outer ports and make junctions for them. - for (i,j) in enumerate(ex.context) - k = add_part!(uwd, :Junction, variable=varname(j), junction_type=vartype(j)) - junctions[varname(j)] = k - set_subpart!(uwd, i, :outer_junction, k) - end - - # then for each statement we add a box, and its ports - for s in ex.statements - b = add_part!(uwd, :Box, name=s.relation) - for a in s.variables - # if a junction is missing, we have to add it. This is for nonexported variables - if !(varname(a) ∈ keys(junctions)) - k = add_part!(uwd, :Junction, variable=varname(a), junction_type=vartype(a)) - junctions[varname(a)] = k - end - # every port connects to the junction with the same variable name - add_part!(uwd, :Port, box=b, port_type=vartype(a), junction=junctions[varname(a)]) - end - end - return uwd -end - -end \ No newline at end of file diff --git a/test/amr_it_ex.jl b/test/amr_it_ex.jl deleted file mode 100644 index a68a509..0000000 --- a/test/amr_it_ex.jl +++ /dev/null @@ -1,575 +0,0 @@ -module AMRExamples -using ..SyntacticModels.AMR -using Test -using ACSets -using ACSets.ADTs - -using ACSets.InterTypes -using Test -using OrderedCollections -import JSON -import JSON3 - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using StructTypes - - -#= -@intertypes "../src/amr.it" module AMR end - -using .AMR -=# - - -nomath = AMR.Math("") -header = AMR.Header("","SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") -model = acsetspec(:(LabelledPetriNet{Symbol}), quote - S(label=:S) - S(label=:I) - S(label=:R) - - T(label=:inf) - T(label=:rec) - - I(is=:S, it=:inf) - I(is=:I, it=:inf) - I(is=:I, it=:rec) - - O(os=:I, it=:inf) - O(os=:I, it=:inf) - O(os=:R, it=:rec) -end) - -ode = AMR.ODERecord([AMR.Rate(:inf, AMR.ExpressionFormula( "S*I*β", nomath)), - AMR.Rate(:rec, AMR.ExpressionFormula("I*γ", nomath))], - - [AMR.Initial(:S, AMR.ExpressionFormula("S₀", nomath)), - AMR.Initial(:I, AMR.ExpressionFormula("I₀", nomath)), - AMR.Initial(:R, AMR.ExpressionFormula("R₀", nomath)),], - - [AMR.Parameter(:β, "β", "the beta parameter", AMR.Unit("1/(persons^2*day)", nomath), 1e-2, AMR.Uniform(1e-3, 2e-2)), - AMR.Parameter(:γ, "γ", "the gama parameter", AMR.Unit("1/(persons*day)", nomath), 3, AMR.Uniform(1, 2e+2)), - - AMR.Parameter(:S₀, "S₀", "the initial susceptible population", AMR.Unit("persons", nomath), 300000000.0, AMR.Uniform(1e6, 4e6)), - AMR.Parameter(:I₀, "I₀", "the initial infected population", AMR.Unit("persons", nomath), 1.0, AMR.Uniform(1, 1)), - AMR.Parameter(:R₀, "R₀", "the initial recovered population", AMR.Unit("persons", nomath), 0.0, AMR.Uniform(0, 4)), - ], - AMR.Time(:t, AMR.Unit("day", nomath))) - -odelist = AMR.ODEList([ - AMR.Time(:t, AMR.Unit("day", nomath)), - AMR.Parameter(:β, "β", "the beta parameter", AMR.Unit("1/(persons^2*day)", nomath), 1e-2, AMR.Uniform(1e-3, 2e-2)), - AMR.Rate(:inf, AMR.ExpressionFormula("S*I*β", nomath)), - - AMR.Parameter(:γ, "γ", "the gama parameter", AMR.Unit("1/(persons*day)", nomath), 3, AMR.Uniform(1, 2e+2)), - AMR.Rate(:rec, AMR.ExpressionFormula("I*γ", nomath)), - - AMR.Parameter(:S₀, "S₀", "the initial susceptible population", AMR.Unit("persons", nomath), 300000000.0, AMR.Uniform(1e6, 4e6)), - AMR.Initial(:S₀, AMR.ExpressionFormula("S₀", nomath)), - - AMR.Parameter(:I₀, "I₀", "the initial infected population", AMR.Unit("persons", nomath), 1.0, AMR.Uniform(1, 1)), - AMR.Initial(:I₀, AMR.ExpressionFormula("I₀", nomath)), - - AMR.Parameter(:R₀, "R₀", "the initial recovered population", AMR.Unit("persons", nomath), 0.0, AMR.Uniform(0, 4)), - AMR.Initial(:R₀, AMR.ExpressionFormula("R₀", nomath)), - - ]) - -#= -amr₁ = AMR.ASKEModel(header, - model, - [ode] -) - - -typesystem = acsetspec(:(LabelledPetriNet{Symbol}), quote - S(label=:Pop) - - T(label=:inf) - T(label=:disease) - T(label=:strata) - - I(is=:Pop, it=:inf) - I(is=:Pop, it=:inf) - I(is=:Pop, it=:disease) - I(is=:Pop, it=:strata) - - O(os=:Pop, it=:inf) - O(os=:Pop, it=:inf) - O(os=:Pop, it=:disease) - O(os=:Pop, it=:strata) -end) - - -typing = Typing( - typesystem, - [ - (:S=>:Pop), - (:I=>:Pop), - (:R=>:Pop), - (:inf=>:inf), - (:rec=>:disease) - ] -) - -amr₂ = ASKEModel(header, - model, - [ - odelist, - typing - ] -) - -println() -println(amr_to_string(amr₁)) -println() -println(amr_to_string(amr₂)) - -AMR.amr_to_expr(amr₁) |> println -AMR.amr_to_expr(amr₂.header) |> println -AMR.amr_to_expr(amr₂.model) |> println -map(AMR.amr_to_expr(amr₂.semantics[1]).args[2].args) do s; println(s) end -AMR.amr_to_expr(amr₂.semantics[1]).args[2] -AMR.amr_to_expr(amr₂.semantics[1]) -AMR.amr_to_expr(amr₂) |> println - -h = AMR.load(Header, Dict("name" => "SIR Model", -"schema" => "https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json", -"description" => "SIR model", -"schema_name" => "petrinet", -"model_version" => "0.1")) - -@test AMR.amr_to_string(h) == "\"\"\"\nASKE Model Representation: SIR Model0.1 :: petrinet \n https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json\n\nSIR model\n\"\"\"" - -mjson = raw"""{ - "states": [ - { - "id": "S", - "name": "Susceptible", - "description": "Number of individuals that are 'susceptible' to a disease infection", - "grounding": { - "identifiers": { - "ido": "0000514" - } - }, - "units": { - "expression": "person", - "expression_mathml": "person" - } - }, - { - "id": "I", - "name": "Infected", - "description": "Number of individuals that are 'infected' by a disease", - "grounding": { - "identifiers": { - "ido": "0000511" - } - }, - "units": { - "expression": "person", - "expression_mathml": "person" - } - }, - { - "id": "R", - "name": "Recovered", - "description": "Number of individuals that have 'recovered' from a disease infection", - "grounding": { - "identifiers": { - "ido": "0000592" - } - }, - "units": { - "expression": "person", - "expression_mathml": "person" - } - } - ], - "transitions": [ - { - "id": "inf", - "input": [ - "S", - "I" - ], - "output": [ - "I", - "I" - ], - "properties": { - "name": "Infection", - "description": "Infective process between individuals" - } - }, - { - "id": "rec", - "input": [ - "I" - ], - "output": [ - "R" - ], - "properties": { - "name": "Recovery", - "description": "Recovery process of a infected individual" - } - } - ] - } - """ -using JSON -modeldict = JSON.parse(mjson) -using ACSets.ADTs - -println(sprint(show, AMR.petrispec(modeldict))) - -semantics_str = raw"""{ - "ode": { - "rates": [ - { - "target": "inf", - "expression": "S*I*beta", - "expression_mathml": "SIbeta" - }, - { - "target": "rec", - "expression": "I*gamma", - "expression_mathml": "Igamma" - } - ], - "initials": [ - { - "target": "S", - "expression": "S0", - "expression_mathml": "S0" - }, - { - "target": "I", - "expression": "I0", - "expression_mathml": "I0" - }, - { - "target": "R", - "expression": "R0", - "expression_mathml": "R0" - } - ], - "parameters": [ - { - "id": "beta", - "name": "β", - "description": "infection rate", - "units": { - "expression": "1/(person*day)", - "expression_mathml": "1personday" - }, - "value": 2.7e-7, - "distribution": { - "type": "StandardUniform1", - "parameters": { - "minimum": 2.6e-7, - "maximum": 2.8e-7 - } - } - }, - { - "id": "gamma", - "name": "γ", - "description": "recovery rate", - "grounding": { - "identifiers": { - "askemo": "0000013" - } - }, - "units": { - "expression": "1/day", - "expression_mathml": "1day" - }, - "value": 0.14, - "distribution": { - "type": "StandardUniform1", - "parameters": { - "minimum": 0.1, - "maximum": 0.18 - } - } - }, - { - "id": "S0", - "name": "S₀", - "description": "Total susceptible population at timestep 0", - "value": 1000 - }, - { - "id": "I0", - "name": "I₀", - "description": "Total infected population at timestep 0", - "value": 1 - }, - { - "id": "R0", - "name": "R₀", - "description": "Total recovered population at timestep 0", - "value": 0 - } - ], - "observables": [ - { - "id": "noninf", - "name": "Non-infectious", - "states": [ - "S", - "R" - ], - "expression": "S+R", - "expression_mathml": "SR" - } - ], - "time": { - "id": "t", - "units": { - "expression": "day", - "expression_mathml": "day" - } - } - } -} -""" -semantics_dict = JSON.parse(semantics_str) -@show semantics_dict - -AMR.load(ODERecord, semantics_dict["ode"]) |> AMR.amr_to_string |> println - -sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir.json"])) -AMR.optload(AMRExamples.sirmodel_dict, ["model", :states], nothing) |> show -sirmodel = AMR.load(ASKEModel, sirmodel_dict) -sirmodel |> AMR.amr_to_string |> println - -sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir_typed.json"])) -semtyp = sirmodel_dict["semantics"]["typing"] - -sirmodel = AMR.load(Typing, semtyp) |> AMR.amr_to_string |> println - -sirmodel = AMR.load(ASKEModel, sirmodel_dict) -sirmodel |> AMR.amr_to_string |> println - -# Deserializing from Human readable strings - - -@testset "Loading Time" begin - @test AMR.load(AMR.Time, Base.Meta.parse("t::Time{}")) == AMR.Time(:t, AMR.Unit("", nomath)) - @test AMR.load(AMR.Time, Base.Meta.parse("t::Time{day}")) == AMR.Time(:t, AMR.Unit("day", nomath)) - @test AMR.load(AMR.Time, Base.Meta.parse("t::Time{day^2}")) == AMR.Time(:t, AMR.Unit("day ^ 2", nomath)) - @test_throws ErrorException AMR.load(AMR.Time, Base.Meta.parse("t::time{}")) - @test_throws ErrorException AMR.load(AMR.Time, Base.Meta.parse("t::time")) -end - -@testset "Loading Rates" begin - infspec = Meta.parse("inf::Rate = S*I*beta") - @test AMR.load(AMR.Rate, infspec).target == :inf - @test AMR.load(AMR.Rate, infspec).f.expression == "S * I * beta" - infspec = Meta.parse("inf::Rate{persons/time} = S*I*beta") - @test AMR.load(AMR.Rate, infspec).target == :inf - @test AMR.load(AMR.Rate, infspec).f.expression == "S * I * beta" -end -@testset "Loading Initials" begin - infspec = Meta.parse("S0::Initial = S*I*beta") - @test AMR.load(AMR.Initial, infspec).target == :S0 - @test AMR.load(AMR.Initial, infspec).f.expression == "S * I * beta" - infspec = Meta.parse("S0::Initial{persons/time} = S*I*beta") - @test AMR.load(AMR.Initial, infspec).target == :S0 - @test AMR.load(AMR.Initial, infspec).f.expression == "S * I * beta" -end - -@testset "Loading Parameters" begin - @testset "No Units" begin - paramstr = raw""" - \"\"\" - R₀ -- Total recovered population at timestep 0 - \"\"\" - R0::Parameter{} = 0.0 ~ δ(missing) - """ - paramexp = Meta.parse(paramstr) - param = AMR.load(Parameter, paramexp) - @test param.units == AMR.nounit - @test param.id == :R0 - end - @testset "Units" begin - - paramstr = raw""" - \"\"\" - R₀ -- Total recovered population at timestep 0 - \"\"\" - R0::Parameter{persons/day} = 0.0 ~ δ(missing) - """ - paramexp = Meta.parse(paramstr) - param = AMR.load(Parameter, paramexp) - @test param.id == :R0 - @test param.units == Unit("persons / day", AMR.nomath) - @test param.distribution == PointMass(:missing) - - paramstr = raw""" - \"\"\" - R₀ -- Total recovered population at timestep 0 - \"\"\" - R0::Parameter{persons/day} = 0.0 ~ δ(0.0) - """ - paramexp = Meta.parse(paramstr) - param = AMR.load(Parameter, paramexp) - @test param.id == :R0 - @test param.units == Unit("persons / day", AMR.nomath) - @test param.distribution == PointMass(0.0) - end -end - -odelist_expr = Meta.parse(raw""" -ODE_Record = begin - -inf::Rate = S*I*beta -rec::Rate = I*gamma -S::Initial = S0 -I::Initial = I0 -R::Initial = R0 - -\"\"\" β -- infection rate \"\"\" -beta::Parameter{} = 0.027 ~ U(0,1) - - -\"\"\" γ -- recovery rate \"\"\" -gamma::Parameter{} = 0.14 ~ U(0,1) - - -\"\"\" S₀ -- Total susceptible population at timestep 0 \"\"\" -S0::Parameter{} = 1000.0 ~ δ(missing) - - -\"\"\" I₀ -- Total infected population at timestep 0\"\"\" -I0::Parameter{} = 1.0 ~ δ(missing) - - -\"\"\" R₀ -- Total recovered population at timestep 0\"\"\" -R0::Parameter{} = 0.0 ~ δ(missing) - -t::Time{day} -end -""") - -ol = AMR.load(ODEList, odelist_expr) -AMR.amr_to_string(ol) |> println - - -header_expr = Meta.parse(raw""" -\"\"\" -ASKE Model Representation: SIR Model@v0.1 :: petrinet - https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json - -Typed SIR model created by Nelson, derived from the one by Ben, Micah, Brandon -\"\"\" -""") - -AMR.load(Header, header_expr) - -modelrep = raw""" -begin -\"\"\" -ASKE Model Representation: SIR Model@0.1 :: petrinet - https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json - -Typed SIR model created by Nelson, derived from the one by Ben, Micah, Brandon -\"\"\" -Model = begin - AMRPetriNet = begin - S(id=S,name=Susceptible,units=nothing) - S(id=I,name=Infected,units=nothing) - S(id=R,name=Recovered,units=nothing) - T(id=inf,name=Infection,desc="Infective process between individuals") - T(id=rec,name=Recovery,desc="Recovery process of a infected individual") - I(is=S,it=inf) - I(is=I,it=inf) - I(is=I,it=rec) - O(os=I,ot=inf) - O(os=I,ot=inf) - O(os=R,ot=rec) - end -end - -ODE_Record = begin - -inf::Rate = S*I*beta -rec::Rate = I*gamma -S::Initial = S0 -I::Initial = I0 -R::Initial = R0 - -\"\"\" β -- infection rate \"\"\" -beta::Parameter{} = 0.027 ~ U(0,1) - - -\"\"\" γ -- recovery rate \"\"\" -gamma::Parameter{} = 0.14 ~ U(0,1) - - -\"\"\" S₀ -- Total susceptible population at timestep 0 \"\"\" -S0::Parameter{} = 1000.0 ~ δ(missing) - - -\"\"\" I₀ -- Total infected population at timestep 0\"\"\" -I0::Parameter{} = 1.0 ~ δ(missing) - - -\"\"\" R₀ -- Total recovered population at timestep 0\"\"\" -R0::Parameter{} = 0.0 ~ δ(missing) - -t::Time{day} -end - -Typing = begin - Model = begin - AMRPetriNet = begin - S(id=Pop,name=Pop,units=nothing) - S(id=Vaccine,name=Vaccine,units=nothing) - T(id=Infect,name=Infect,desc="2-to-2 process that represents infectious contact between two human individuals.") - T(id=Disease,name=Disease,desc="1-to-1 process that represents a change in th edisease status of a human individual.") - T(id=Strata,name=Strata,desc="1-to-1 process that represents a change in the demographic division of a human individual.") - T(id=Vaccinate,name=Vaccinate,desc="2-to-1 process that represents an human individual receiving a vaccine dose.") - T(id=Produce_Vaccine,name="Produce Vaccine",desc="0-to-1 process that represents the production of a single vaccine dose.") - I(is=Pop,it=Infect) - I(is=Pop,it=Infect) - I(is=Pop,it=Disease) - I(is=Pop,it=Strata) - I(is=Pop,it=Vaccinate) - I(is=Vaccine,it=Vaccinate) - O(os=Pop,ot=Infect) - O(os=Pop,ot=Infect) - O(os=Pop,ot=Disease) - O(os=Pop,ot=Strata) - O(os=Pop,ot=Vaccinate) - O(os=Vaccine,ot=Produce_Vaccine) - end - end - TypeMap = [ - S => Pop, - I => Pop, - R => Pop, - inf => Infect, - rec => Disease,] -end -end -""" -println(modelrep) -model_expr = Base.Meta.parse(modelrep) -@test AMR.load(ASKEModel, model_expr).header isa Header -@test AMR.load(ASKEModel, model_expr).model isa ACSetSpec -@test length(AMR.load(ODEList, model_expr.args[4]).statements) == 8 -@test length(AMR.load(ASKEModel, model_expr).semantics[1].statements) == 8 -@test AMR.load(Typing, model_expr.args[6]).system isa ACSetSpec -@test AMR.load(Typing, model_expr.args[6]).map isa Vector{Pair} -println(AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) -@test_skip (AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) == modelrep - -=# - -end # module \ No newline at end of file diff --git a/test/composite_models_it_ex.jl b/test/composite_models_it_ex.jl deleted file mode 100644 index ab9969f..0000000 --- a/test/composite_models_it_ex.jl +++ /dev/null @@ -1,148 +0,0 @@ -using ..SyntacticModels.AMR -using ..SyntacticModels.ASKEMDecapodes -using ..SyntacticModels.ASKEMUWDs -using ..SyntacticModels.Composites - -using MLStyle -using JSON -# using Decapodes -using Catlab -using Catlab.RelationalPrograms -using Catlab.WiringDiagrams -using Test - - - -using ACSets -using ACSets.InterTypes -using Test -using OrderedCollections -import JSON -import JSON3 - - -x = Typed(:X, :Form0) -v = Typed(:V, :Form0) -Q = Typed(:Q, :Form0) - -c = [x, Q] -s = [ASKEMUWDs.uwd.Statement(:oscillator, [x,v]), - ASKEMUWDs.uwd.Statement(:heating, [v,Q])] -u = UWDExpr(c, s) - - - -h = Header("","harmonic_oscillator", - "modelreps.io/DecaExpr", - "A Simple Harmonic Oscillator as a Diagrammatic Equation", - "DecaExpr", - "v1.0") - -# The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = ASKEMDecapodes.parse_decapode(quote - X::Form0{Point} - V::Form0{Point} - - k::Constant{Point} - - ∂ₜ(X) == V - ∂ₜ(V) == -1*k*(X) -end -) - -# That gave us the first model -d1 = ASKEMDecaExpr(h, dexpr, []) - -# The second model is: -d2 = ASKEMDecaExpr( - Header("","fricative_heating", - "modelreps.io/SummationDecapode", - "Velocity makes it get hot, but you dissipate heat away from Q₀", - "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote - V::Form0{Point} - Q::Form0{Point} - κ::Constant{Point} - λ::Constant{Point} - Q₀::Parameter{Point} - - ∂ₜ(Q) == κ*V + λ(Q - Q₀) - end), - [] -) - -# Now we can assemble this bad boi: -h = Header("","composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") -m = CompositeModelExpr(h, u, [OpenModel(d1, [:X, :V]), OpenModel(d2, [:V, :Q])]) -interface(m) == [:X, :Q] -#= TODO: FIXME -write_json_model(m) # you can see from this little model (two coupled odes even) that the jsons will not be human editable. - -# now we can interpret this big data structure to execute a composition! -composite = oapply(m) -display(apex(composite)) -to_graphviz(apex(composite)) -sm_write_json_acset(apex(composite),"$(m.header.name)-acset") -=# - -# TESTING NESTED COMPOSITION - -Q₊ = Untyped(:Q₊) -Q₋ = Untyped(:Q₋) -Q̇ = Untyped(:Q̇) - -uwdʰ = UWDExpr([v, Q], [ASKEMUWDs.uwd.Statement(:drag, [v, Q₊]), ASKEMUWDs.uwd.Statement(:cooling, [Q₋, Q]), ASKEMUWDs.uwd.Statement(:superposition, [Q₊, Q₋, Q̇])]) - -drag = ASKEMDecaExpr( - Header("","DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote - V::Form0{Point} - Q₊::Form0{Point} - κ::Constant{Point} - - Q₊ == κ*V - end), [] -) - -cooling = ASKEMDecaExpr( - Header("","NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote - Q₋::Form0{Point} - Q₀::Parameter{Point} - Q::Form0{Point} - λ::Constant{Point} - - Q₋ == λ(Q-Q₀) - end), [] -) - -superposition = ASKEMDecaExpr( - Header("","LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote - X::Form0{Point} - Y::Form0{Point} - T::Form0{Point} - - T == X + Y - end), [] -) - -h = Header("","hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") -m = CompositeModelExpr(h,u, [OpenModel(d1, [:X, :V]), - CompositeModelExpr(Header("","heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), - uwdʰ, [OpenModel(drag, [:V, :Q₊]), OpenModel(cooling, [:Q₋, :Q]), OpenModel(superposition, [:X, :Y, :T])]) -]) -#= TODO: FIXME -write_json_model(m) - -@testset "Composite Model Readback" begin - m′ = readback(m) - @test JSON3.write(m) == JSON3.write(m′) -end - -dh = apex(oapply(m)) - -composite = OpenDecapode(m) -hf = composite.model.header -write_json_model(ASKEMDecapode(Header("","flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) -=# \ No newline at end of file diff --git a/test/core_it.jl b/test/core_it.jl deleted file mode 100644 index 1e3ed37..0000000 --- a/test/core_it.jl +++ /dev/null @@ -1,38 +0,0 @@ -include("../src/SyntacticModels.jl") - -using .SyntacticModels -# using .SyntacticModels.ASKEMDecapodes - -using Test -using JSON3 - -jsondir = joinpath(@__DIR__, "json") - -write_json_model(m, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp - JSON3.pretty(fp, jsonwrite(m)) -end - -readback(m, T, prefix=joinpath(@__DIR__, "json")) = jsonread(joinpath(jsondir, "$(m.header.name).json"),T) - -#= -write_json_model(m::ASKEMDecapodes.ASKEMDecapode, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp - d = Dict("header"=>m.header, "model"=>generate_json_acset(m.model), "_type"=> "ASKEMDecapode") - JSON3.pretty(fp, d) -end - -sm_write_json_acset(X, fname, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(fname).json"), "w") do fp - JSON3.pretty(fp, generate_json_acset(X)) -end -=# - -try - mkdir(joinpath(@__DIR__, "json")) -catch - @info "JSON DIR already exists, you might want to rm -r it to clean up" -end - -include("amr_it_ex.jl") -include("decapodes_it_ex.jl") -include("uwd_it_ex.jl") -include("composite_models_it_ex.jl") -# include("serialization_it_mwe.jl") \ No newline at end of file diff --git a/test/decapodes_it_ex.jl b/test/decapodes_it_ex.jl deleted file mode 100644 index f832828..0000000 --- a/test/decapodes_it_ex.jl +++ /dev/null @@ -1,103 +0,0 @@ - -# using SyntacticModels - -using ACSets -using ACSets.InterTypes -using Test -using OrderedCollections -import JSON -import JSON3 -# import JSONSchema - -# include("src/SyntacticModels.jl") - -# using .SyntacticModels -# # using .SyntacticModels.ASKEMDecapodes - -# using Decapodes -# import Decapodes: recognize_types, make_sum_mult_unique! - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface -using StructTypes - -# using ..SyntacticModelsBase - -#= -@intertypes "../src/amr.it" module AMR end - -using .AMR - - - -@intertypes "../src/decapodes.it" module decapodes - import ..AMR -end - -using .decapodes -=# -using ..SyntacticModels.AMR -using ..SyntacticModels.ASKEMDecapodes - -h = amr.Header("", "harmonic_oscillator", - "modelreps.io/DecaExpr", - "A Simple Harmonic Oscillator as a Diagrammatic Equation", - "DecaExpr", - "v1.0") - -# The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = ASKEMDecapodes.parse_decapode(quote - X::Form0{Point} - V::Form0{Point} - - k::Constant{Point} - - ∂ₜ(X) == V - ∂ₜ(V) == -1*k*(X) -end -) - -annot = [amr.Annotation(:X,:Form0,amr.Name("The X variable."))] - -# Bundle the DecaExpr with the header metadata. -mexpr = ASKEMDecapodes.decapodes.ASKEMDecaExpr(h, dexpr, annot) - - - -# Convert a the DecaExpr to a SummationDecapode which is the -# combinatorial representation. The converter lives in Decapodes/src/language.jl. - -d = ASKEMDecapodes.SummationDecapode(mexpr.model) - -# We want different metadata for this representation. -# The Summation prefix just means that this decapodes have -# specialized support for the handling of summation. -# The summation operator happens in physics so often, -# that you want to bake in some specialized handling to the data structure. - -h = amr.Header("","harmonic_oscillator", - "modelreps.io/SummationDecapode", - "A Simple Harmonic Oscillator as a Diagrammatic Equation", - "SummationDecapode", - "v1.0") -mpode = ASKEMDecapodes.decapodes.ASKEMDecapode(h, d, annot) - - -# The syntactic representation can be serialized as JSON. -# The resulting structure is like a parse tree of the syntactic -# representation of the DecaExpr -write_json_model(mexpr) - -# We could also use the JSON serialization built into Catlab -# to serialize the resulting combinatorial representation -# sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") - - - # Can we read back the models we just wrote? -@testset "Decapodes Readback" begin - mexpr′ = readback(mexpr,ASKEMDecapodes.decapodes.ASKEMDeca) - @test JSON3.write(mexpr) == JSON3.write(mexpr′) -end diff --git a/test/uwd_it_ex.jl b/test/uwd_it_ex.jl deleted file mode 100644 index 3f381a2..0000000 --- a/test/uwd_it_ex.jl +++ /dev/null @@ -1,94 +0,0 @@ -using Test -using JSON3 -using Catlab.RelationalPrograms -using Catlab.WiringDiagrams -using Catlab.Graphics - -using ACSets -using ACSets.InterTypes -using Test -using OrderedCollections -import JSON - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface -using StructTypes - -# using ..SyntacticModelsBase - -#= -@intertypes "../src/amr.it" module amr end - -using .amr - - - -@intertypes "../src/uwd.it" module uwd - import ..amr -end - -using .uwd -=# -using ..SyntacticModels.AMR -using ..SyntacticModels.ASKEMUWDs - -# This example follows what in current catlab would be given as - -#= -@relation (x:X, z:Z) where y:Y begin - R(x,y) - S(y,z) - T(z,y,u) -end -=# - -v1 = ASKEMUWDs.uwd.Typed(:x, :X) -v2 = ASKEMUWDs.uwd.Typed(:y, :Y) -v3 = ASKEMUWDs.uwd.Typed(:z, :Z) -v4 = ASKEMUWDs.uwd.Untyped(:u) -c = [v1, v3] -s = [ASKEMUWDs.uwd.Statement(:R, [v1,v2]), - ASKEMUWDs.uwd.Statement(:S, [v2,v3]), - ASKEMUWDs.uwd.Statement(:T, [v3,v2, v4])] -u = ASKEMUWDs.uwd.UWDExpr(c, s) - -@testset "UWDExpr Readback" begin - s = jsonwrite(u) - ujson = jsonread(s, ASKEMUWDs.uwd.UWDTerm) - @test s == jsonwrite(ujson) -end - - -uwd′ = ASKEMUWDs.construct(RelationDiagram, u) - - -h = AMR.Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") - -mexpr = ASKEMUWDs.uwd.UWDModel(h, u) -@testset "UWD Readback" begin - write_json_model(mexpr) - # NOTE: because the intertype parsing does not recognize the subtypes of a sum type, readback won't currently work - # That is the reason for directly writing the UWDTerm type below. - mexpr′ = readback(mexpr,ASKEMUWDs.uwd.UWDTerm) - # mexpr′ = jsonread(joinpath(joinpath(@__DIR__, "json"), "$(mexpr.header.name).json"), ASKEMUWDs.UWDTerm) - - @test mexpr.header == mexpr′.header - @test mexpr.uwd.context == mexpr′.uwd.context - @test mexpr.uwd.context == mexpr′.uwd.context - @test mexpr.uwd.statements[1].relation == mexpr′.uwd.statements[1].relation - @test mexpr.uwd.statements[1].variables == mexpr′.uwd.statements[1].variables - @test mexpr.uwd.statements[2].relation == mexpr′.uwd.statements[2].relation - @test mexpr.uwd.statements[2].variables == mexpr′.uwd.statements[2].variables - @test mexpr.uwd.statements[3].relation == mexpr′.uwd.statements[3].relation - @test mexpr.uwd.statements[3].variables == mexpr′.uwd.statements[3].variables - - @test all(mexpr.uwd.statements .== mexpr′.uwd.statements) - @test mexpr == mexpr′ -end - -to_graphviz(uwd′, box_labels=:name, junction_labels=:variable) - -display(uwd′) \ No newline at end of file From c2cc82bbc9f9b4512491b20350d641c088a9e8a1 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 7 Dec 2023 10:48:07 -0500 Subject: [PATCH 22/35] Removed unnecessary imports from test examples. --- test/amr_examples.jl | 11 ----------- test/composite_models_examples.jl | 8 -------- test/decapodes_examples.jl | 11 ----------- test/uwd_examples.jl | 12 ------------ 4 files changed, 42 deletions(-) diff --git a/test/amr_examples.jl b/test/amr_examples.jl index b2e40fd..64c3334 100644 --- a/test/amr_examples.jl +++ b/test/amr_examples.jl @@ -4,17 +4,6 @@ using Test using ACSets using ACSets.ADTs -using ACSets.InterTypes -using Test -using OrderedCollections -import JSON -import JSON3 - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using StructTypes - nomath = Math("") header = Header("","SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") model = acsetspec(:(LabelledPetriNet{Symbol}), quote diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index 907644a..72d081e 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -3,19 +3,11 @@ using ..SyntacticModels.ASKEMDecapodes using ..SyntacticModels.ASKEMUWDs using ..SyntacticModels.Composites -using MLStyle -using JSON # using Decapodes using Catlab using Catlab.RelationalPrograms using Catlab.WiringDiagrams using Test - -using ACSets -using ACSets.InterTypes -using Test -using OrderedCollections -import JSON import JSON3 diff --git a/test/decapodes_examples.jl b/test/decapodes_examples.jl index d7e012d..95daca6 100644 --- a/test/decapodes_examples.jl +++ b/test/decapodes_examples.jl @@ -3,20 +3,9 @@ using ..SyntacticModels.ASKEMDecapodes using ..SyntacticModels.AMR -using ACSets -using ACSets.InterTypes using Test -using OrderedCollections -import JSON import JSON3 -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface -using StructTypes - h = amr.Header("", "harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", diff --git a/test/uwd_examples.jl b/test/uwd_examples.jl index 34f641d..d7ff6b4 100644 --- a/test/uwd_examples.jl +++ b/test/uwd_examples.jl @@ -1,5 +1,4 @@ using ..SyntacticModels -using ..SyntacticModels.SyntacticModelsBase using ..SyntacticModels.AMR using ..SyntacticModels.ASKEMUWDs @@ -9,17 +8,6 @@ using Catlab.RelationalPrograms using Catlab.WiringDiagrams using Catlab.Graphics -using ACSets -using ACSets.InterTypes -using OrderedCollections - -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface -using StructTypes - # This example follows what in current catlab would be given as #= From bac39db4260c60b0d41c0200ad7eeb8b97c09a21 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 7 Dec 2023 11:59:34 -0500 Subject: [PATCH 23/35] Updated Project.toml for docs. --- docs/Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Project.toml b/docs/Project.toml index 399ccd8..2cd9c2d 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -6,5 +6,6 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" SyntacticModels = "22bb929c-8bcf-4852-b455-eb3e1675e09c" From 0274d67683d1dbccf4e8cfd503056686b82aa534 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 7 Dec 2023 14:07:22 -0500 Subject: [PATCH 24/35] Updated decapodes example doc. --- docs/literate/decapodes_examples.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/literate/decapodes_examples.jl b/docs/literate/decapodes_examples.jl index 02285fa..0d219db 100644 --- a/docs/literate/decapodes_examples.jl +++ b/docs/literate/decapodes_examples.jl @@ -8,6 +8,7 @@ We only look at ordinary differential equations here, but you can think of these using ..SyntacticModels using ..SyntacticModels.ASKEMDecapodes +using ..SyntacticModels.ASKEMDecapodes: parse_decapode, SummationDecapode using ..SyntacticModels.AMR using MLStyle @@ -15,7 +16,7 @@ using JSON3 using Catlab using ACSets using ACSets.JSONACSets -using Decapodes +# using Decapodes using Test # Build the heder object describing the model. @@ -27,7 +28,7 @@ h = AMR.Header("harmonic_oscillator", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = Decapodes.parse_decapode(quote +dexpr = parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -44,7 +45,7 @@ mexpr = ASKEMDecaExpr(h, dexpr) # Convert a the DecaExpr to a SummationDecapode which is the # combinatorial representation. The converter lives in Decapodes/src/language.jl. -d = Decapodes.SummationDecapode(mexpr.model) +d = SummationDecapode(mexpr.model) # To visualize the Decapode as a compute graph, you can use Graphviz From 37e6398306cd30dee3166665173d0a059806af5d Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 7 Dec 2023 16:47:01 -0500 Subject: [PATCH 25/35] Cleaned up imports and qualification of type names in src files. Removed references to Decapodes.jl in decapodes.jl. --- src/amr.jl | 54 ++++++++++++++--------------- src/composite_models.jl | 21 +++-------- src/decapodes.jl | 77 ++++++++++++++++++----------------------- src/uwd.jl | 43 +++++++++-------------- 4 files changed, 82 insertions(+), 113 deletions(-) diff --git a/src/amr.jl b/src/amr.jl index 35156e9..e3cf827 100644 --- a/src/amr.jl +++ b/src/amr.jl @@ -20,22 +20,20 @@ using StructTypes using .amr -function distro_string(d::amr.Distribution) +function distro_string(d::Distribution) @match d begin - amr.StandardUniform(s) => "U(0,1)" - amr.Uniform(min, max) => "U($min,$max)" - amr.StandardNormal(s) => "N(0,1)" - amr.Normal(mu, var) => "N($mu,$var)" - amr.PointMass(value) => "δ($value)" + StandardUniform(s) => "U(0,1)" + Uniform(min, max) => "U($min,$max)" + StandardNormal(s) => "N(0,1)" + Normal(mu, var) => "N($mu,$var)" + PointMass(value) => "δ($value)" end end -function distro_expr(d::amr.Distribution) +function distro_expr(d::Distribution) return Base.Meta.parse(distro_string(d)) end -#= - function note_string(n::Note) @match n begin Name(n) => "Name($n)" @@ -53,30 +51,30 @@ padlines(ss::Vector, n) = map(ss) do s " "^n * s end padlines(s::String, n=2) = join(padlines(split(s, "\n"), n), "\n") -=# + function amr_to_string(amr′) let ! = amr_to_string @match amr′ begin s::String => s - amr.Math(s) => !s - amr.Presentation(s) => " $s " - u::amr.Unit => !u.expression - d::amr.Distribution => distro_string(d) - amr.Time(id, u) => "$id::Time{$(!u)}\n" - amr.Rate(t, f) => "$t::Rate = $(f.expression)" - amr.Initial(t, f) => "$t::Initial = $(f.expression)" - amr.Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" - amr.Header(id, name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" - amr.Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" - m::ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" - amr.ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" - amr.ODERecord(rts, init, para, time) => join(vcat(["ODE_Record = begin\n"], !rts , !init, !para, [!time, "end"]), "\n") - vs::Vector{amr.Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") - vs::Vector{amr.Semantic} => join(map(!, vs), "\n\n") + Math(s) => !s + Presentation(s) => " $s " + u::Unit => !u.expression + d::Distribution => distro_string(d) + Time(id, u) => "$id::Time{$(!u)}\n" + Rate(t, f) => "$t::Rate = $(f.expression)" + Initial(t, f) => "$t::Initial = $(f.expression)" + Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" + Header(id, name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" + Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" + m::amr.ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" + ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" + ODERecord(rts, init, para, time) => join(vcat(["ODE_Record = begin\n"], !rts , !init, !para, [!time, "end"]), "\n") + vs::Vector{Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") + vs::Vector{Semantic} => join(map(!, vs), "\n\n") xs::Vector => map(!, xs) - # amr.Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" - # amr.ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" - amr.Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_string(n))" + Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" + ASKEModel(h, m, s) => "$(!h)\n$(!m)\n\n$(!s)" + Annotation(e,t,n) => "Annotation = $(String(e)),$(String(t)): $(note_string(n))" end end end diff --git a/src/composite_models.jl b/src/composite_models.jl index efa65c9..e801b10 100644 --- a/src/composite_models.jl +++ b/src/composite_models.jl @@ -2,10 +2,7 @@ module Composites export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode, oapply, Open -using MLStyle using Catlab -using Decapodes # : SummationDecapode -using StructTypes using ..AMR using ..ASKEMDecapodes @@ -14,13 +11,6 @@ using ..ASKEMUWDs using ACSets using ACSets.InterTypes -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface - - using ..AMR.amr using ..ASKEMDecapodes.decapodes using ..ASKEMUWDs.uwd @@ -37,15 +27,14 @@ using .composites Extract the interface of a composite model. If the model is open, then it is the feet of the cospan. If it is a Composite, then it is the context of the uwd. """ -interface(m::composites.CompositeModel) = @match m begin - composites.OpenModel(M, I) => I - composites.CompositeModelExpr(h, uwd′, components) => map(ASKEMUWDs.varname, context(uwd′)) +interface(m::CompositeModel) = @match m begin + OpenModel(M, I) => I + CompositeModelExpr(h, uwd′, components) => map(ASKEMUWDs.varname, context(uwd′)) end -OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(Decapodes.SummationDecapode, :Var) +OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(decapodes.SummationDecapode, :Var) -# function Decapodes.Open(d::ASKEMDecapodes.decapodes.SummationDecapode, names::Vector{Symbol}) -function Decapodes.Open(d::decapodes.SummationDecapode, names::Vector{Symbol}) +function Open(d::decapodes.SummationDecapode, names::Vector{Symbol}) legs = map(names) do name FinFunction(incident(d, name, :name), nparts(d, :Var)) end diff --git a/src/decapodes.jl b/src/decapodes.jl index 5629672..fc91669 100644 --- a/src/decapodes.jl +++ b/src/decapodes.jl @@ -4,18 +4,9 @@ export ASKEMDecaExpr, ASKEMDecapode, ASKEMDeca, SummationDecapode, parse_decapod using ..AMR -using StructTypes -using MLStyle - using ACSets using ACSets.InterTypes -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface - import Unicode using ..AMR.amr @@ -32,23 +23,23 @@ normalize_unicode(s::Symbol) = Symbol(normalize_unicode(String(s))) DerivOp = Symbol("∂ₜ") append_dot(s::Symbol) = Symbol(string(s)*'\U0307') -term(s::Symbol) = decapodes.Var(normalize_unicode(s)) -term(s::Number) = decapodes.Lit(Symbol(s)) +term(s::Symbol) = Var(normalize_unicode(s)) +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) => decapodes.Tan(decapodes.Var(b)) - Expr(:call, :dt, b) => decapodes.Tan(decapodes.Var(b)) + Expr(:call, :∂ₜ, b) => Tan(Var(b)) + Expr(:call, :dt, b) => Tan(Var(b)) - Expr(:call, Expr(:call, :∘, a...), b) => decapodes.AppCirc1(a, term(b)) - Expr(:call, a, b) => decapodes.App1(a, term(b)) + Expr(:call, Expr(:call, :∘, a...), b) => AppCirc1(a, term(b)) + Expr(:call, a, b) => App1(a, term(b)) - Expr(:call, :+, xs...) => decapodes.Plus(term.(xs)) - Expr(:call, f, x, y) => decapodes.App2(f, term(x), term(y)) + Expr(:call, :+, xs...) => Plus(term.(xs)) + Expr(:call, f, x, y) => App2(f, term(x), term(y)) # TODO: Will later be converted to Op2's or schema has to be changed to include multiplication - Expr(:call, :*, xs...) => decapodes.Mult(term.(xs)) + Expr(:call, :*, xs...) => Mult(term.(xs)) x => error("Cannot construct term from $x") end @@ -60,13 +51,13 @@ function parse_decapode(expr::Expr) ::LineNumberNode => missing # TODO: If user doesn't provide space, this gives a temp space so we can continue to construction # For now spaces don't matter so this is fine but if they do, this will need to change - Expr(:(::), a::Symbol, b::Symbol) => decapodes.Judgement(decapodes.Var(a).name, b, :I) - Expr(:(::), a::Expr, b::Symbol) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b, :I), a.args) + Expr(:(::), a::Symbol, b::Symbol) => Judgement(Var(a).name, b, :I) + Expr(:(::), a::Expr, b::Symbol) => map(sym -> Judgement(Var(sym).name, b, :I), a.args) - Expr(:(::), a::Symbol, b) => decapodes.Judgement(decapodes.Var(a).name, b.args[1], b.args[2]) - Expr(:(::), a::Expr, b) => map(sym -> decapodes.Judgement(decapodes.Var(sym).name, b.args[1], b.args[2]), a.args) + Expr(:(::), a::Symbol, b) => Judgement(Var(a).name, b.args[1], b.args[2]) + Expr(:(::), a::Expr, b) => map(sym -> Judgement(Var(sym).name, b.args[1], b.args[2]), a.args) - Expr(:call, :(==), lhs, rhs) => decapodes.Eq(term(lhs), term(rhs)) + Expr(:call, :(==), lhs, rhs) => Eq(term(lhs), term(rhs)) _ => error("The line $line is malformed") end end |> skipmissing |> collect @@ -74,23 +65,23 @@ function parse_decapode(expr::Expr) eqns = [] foreach(stmts) do s @match s begin - ::decapodes.Judgement => push!(judges, s) - ::Vector{decapodes.Judgement} => append!(judges, s) - ::decapodes.Eq => push!(eqns, s) + ::Judgement => push!(judges, s) + ::Vector{Judgement} => append!(judges, s) + ::Eq => push!(eqns, s) _ => error("Statement containing $s of type $(typeof(s)) was not added.") end end - decapodes.DecaExpr(judges, eqns) + DecaExpr(judges, eqns) end ### # NOTE: the var in a Judgement in decapodes.it is just a Symbol. It does not have a name field, so that was removed # to_decapode helper functions -reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}) = +reduce_term!(t::Term, d::AbstractDecapode, syms::Dict{Symbol, Int}) = let ! = reduce_term! @match t begin - decapodes.Var(x) => begin + Var(x) => begin if haskey(syms, x) syms[x] else @@ -98,7 +89,7 @@ reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol syms[x] = res_var end end - decapodes.Lit(x) => begin + Lit(x) => begin if haskey(syms, x) syms[x] else @@ -106,17 +97,17 @@ reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol syms[x] = res_var end end - decapodes.App1(f, t) || decapodes.AppCirc1(f, t) => begin + App1(f, t) || AppCirc1(f, t) => begin res_var = add_part!(d, :Var, type=:infer) add_part!(d, :Op1, src=!(t,d,syms), tgt=res_var, op1=f) return res_var end - decapodes.App2(f, t1, t2) => begin + App2(f, t1, t2) => begin res_var = add_part!(d, :Var, type=:infer) add_part!(d, :Op2, proj1=!(t1,d,syms), proj2=!(t2,d,syms), res=res_var, op2=f) return res_var end - decapodes.Plus(ts) => begin + Plus(ts) => begin summands = [!(t,d,syms) for t in ts] res_var = add_part!(d, :Var, type=:infer, name=:sum) n = add_part!(d, :Σ, sum=res_var) @@ -126,7 +117,7 @@ reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol return res_var end # TODO: Just for now assuming we have 2 or more terms - decapodes.Mult(ts) => begin + Mult(ts) => begin multiplicands = [!(t,d,syms) for t in ts] res_var = add_part!(d, :Var, type=:infer, name=:mult) m1,m2 = multiplicands[1:2] @@ -139,7 +130,7 @@ reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol end return res_var end - decapodes.Tan(t) => begin + Tan(t) => begin # TODO: this is creating a spurious variable with the same name txv = add_part!(d, :Var, type=:infer) tx = add_part!(d, :TVar, incl=txv) @@ -150,9 +141,9 @@ reduce_term!(t::decapodes.Term, d::decapodes.AbstractDecapode, syms::Dict{Symbol end end -function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) +function eval_eq!(eq::Eq, d::AbstractDecapode, syms::Dict{Symbol, Int}, deletions::Vector{Int}) @match eq begin - decapodes.Eq(t1, t2) => begin + Eq(t1, t2) => begin lhs_ref = reduce_term!(t1,d,syms) rhs_ref = reduce_term!(t2,d,syms) @@ -161,8 +152,8 @@ function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Sy # some kind of way to check track of this equality ref_pair = (t1, t2) @match ref_pair begin - (decapodes.Var(a), decapodes.Var(b)) => return d - (t1, decapodes.Var(b)) => begin + (Var(a), Var(b)) => return d + (t1, Var(b)) => begin lhs_ref, rhs_ref = rhs_ref, lhs_ref end _ => nothing @@ -202,7 +193,7 @@ function eval_eq!(eq::decapodes.Eq, d::decapodes.AbstractDecapode, syms::Dict{Sy return d end -function recognize_types(d::decapodes.AbstractNamedDecapode) +function recognize_types(d::AbstractNamedDecapode) unrecognized_types = setdiff(d[:type], [:Form0, :Form1, :Form2, :DualForm0, :DualForm1, :DualForm2, :Literal, :Parameter, :Constant, :infer]) @@ -210,7 +201,7 @@ function recognize_types(d::decapodes.AbstractNamedDecapode) error("Types $unrecognized_types are not recognized.") end -function fill_names!(d::decapodes.AbstractNamedDecapode) +function fill_names!(d::AbstractNamedDecapode) bulletcount = 1 for i in parts(d, :Var) if !isassigned(d[:,:name],i) || isnothing(d[i, :name]) @@ -227,7 +218,7 @@ function fill_names!(d::decapodes.AbstractNamedDecapode) d end -function make_sum_mult_unique!(d::decapodes.AbstractNamedDecapode) +function make_sum_mult_unique!(d::AbstractNamedDecapode) snum = 1 mnum = 1 for (i, name) in enumerate(d[:name]) @@ -241,7 +232,7 @@ function make_sum_mult_unique!(d::decapodes.AbstractNamedDecapode) end end -function SummationDecapode(e::decapodes.DecaExpr) +function SummationDecapode(e::DecaExpr) # d = SummationDecapode{Any, Any, Symbol}() d = decapodes.SummationDecapode{Symbol, Symbol, Symbol}() symbol_table = Dict{Symbol, Int}() diff --git a/src/uwd.jl b/src/uwd.jl index 7b7e405..a9bf4be 100644 --- a/src/uwd.jl +++ b/src/uwd.jl @@ -1,12 +1,9 @@ module ASKEMUWDs -# include("amr.jl") -export Var, Typed, Untyped, Statement, UWDExpr, UWDModel, UWDTerm, context +export Var, Typed, Untyped, Statement, UWDExpr, UWDModel, UWDTerm, vartype, varname, context, construct using ..AMR -using MLStyle -using StructTypes using Catlab using Catlab.RelationalPrograms using Catlab.WiringDiagrams @@ -15,12 +12,6 @@ using Catlab.WiringDiagrams using ACSets using ACSets.InterTypes -using Reexport -@reexport using MLStyle -@reexport using ACSets -using ACSets.ADTs -using ACSets.ACSetInterface - using ..AMR.amr @intertypes "uwd.it" module uwd @@ -80,20 +71,20 @@ u = UWDExpr(c, s) ``` """ -varname(v::uwd.Var) = @match v begin - uwd.Untyped(v) => v - uwd.Typed(v, t) => v +varname(v::Var) = @match v begin + Untyped(v) => v + Typed(v, t) => v end -vartype(v::uwd.Var) = @match v begin - uwd.Typed(v, t) => t - uwd.Untyped(v) => :untyped +vartype(v::Var) = @match v begin + Typed(v, t) => t + Untyped(v) => :untyped end -context(t::uwd.UWDTerm) = @match t begin +context(t::UWDTerm) = @match t begin uwd.Statement(R, xs) => xs - uwd.UWDExpr(context, statements) => context - uwd.UWDModel(h, uwd) => context(uwd) + UWDExpr(context, statements) => context + UWDModel(h, uwd) => context(uwd) end """ show(io::IO, s::UWDTerm) @@ -101,11 +92,11 @@ end generates a human readable string of the `UWDTerm` (or any sub-term). """ #= -function show(io::IO, s::uwd.UWDTerm) +function show(io::IO, s::UWDTerm) let ! = show @match s begin uwd.Statement(r, v) => begin print(io, "$r("); show(io, v, wrap=false); print(io, ")") end - uwd.UWDExpr(c, body) => begin + UWDExpr(c, body) => begin map(enumerate(body)) do (i,s) if i == 1 print(io, "{ ") @@ -124,19 +115,19 @@ function show(io::IO, s::uwd.UWDTerm) print(io, " where ") show(io, c) end - uwd.UWDModel(h, uwd′) => begin println(io, amr_to_string(h)); println(io, "UWD:"); !(io, uwd′); end + UWDModel(h, uwd′) => begin println(io, amr_to_string(h)); println(io, "UWD:"); !(io, uwd′); end end end end -function show(io::IO, c::Vector{uwd.Var}; wrap=true) +function show(io::IO, c::Vector{Var}; wrap=true) if wrap print(io, "{") end map(enumerate(c)) do (i,s) @match s begin - uwd.Untyped(v) => print(io, v) - uwd.Typed(v, T) => print(io, "$v:$T") + Untyped(v) => print(io, v) + Typed(v, T) => print(io, "$v:$T") end if i != length(c) print(io, ", ") @@ -152,7 +143,7 @@ end Builds a RelationDiagram from a UWDExpr like the `@relation` macro does for Julia Exprs. """ -function construct(::Type{RelationDiagram}, ex::uwd.UWDExpr) +function construct(::Type{RelationDiagram}, ex::UWDExpr) # If you want to understand this code, look at the schema for Relation Diagrams # to_graphviz(RelationalPrograms.SchRelationDiagram) uwd = RelationDiagram(map(varname, ex.context)) From bbcb53d96ff78e70b041d584d73a173530cf72d3 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Fri, 8 Dec 2023 01:32:57 -0500 Subject: [PATCH 26/35] Updated name qualifications in test examples. --- test/composite_models_examples.jl | 10 +++++----- test/decapodes_examples.jl | 16 ++++++++-------- test/uwd_examples.jl | 20 ++++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index 72d081e..edf9da5 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -29,7 +29,7 @@ h = Header("","harmonic_oscillator", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = ASKEMDecapodes.parse_decapode(quote +dexpr = parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -49,7 +49,7 @@ d2 = ASKEMDecaExpr( "modelreps.io/SummationDecapode", "Velocity makes it get hot, but you dissipate heat away from Q₀", "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote + parse_decapode(quote V::Form0{Point} Q::Form0{Point} κ::Constant{Point} @@ -85,7 +85,7 @@ uwdʰ = UWDExpr([v, Q], [ASKEMUWDs.uwd.Statement(:drag, [v, Q₊]), ASKEMUWDs.uw drag = ASKEMDecaExpr( Header("","DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote + parse_decapode(quote V::Form0{Point} Q₊::Form0{Point} κ::Constant{Point} @@ -96,7 +96,7 @@ drag = ASKEMDecaExpr( cooling = ASKEMDecaExpr( Header("","NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote + parse_decapode(quote Q₋::Form0{Point} Q₀::Parameter{Point} Q::Form0{Point} @@ -108,7 +108,7 @@ cooling = ASKEMDecaExpr( superposition = ASKEMDecaExpr( Header("","LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), - ASKEMDecapodes.parse_decapode(quote + parse_decapode(quote X::Form0{Point} Y::Form0{Point} T::Form0{Point} diff --git a/test/decapodes_examples.jl b/test/decapodes_examples.jl index 95daca6..5f4f042 100644 --- a/test/decapodes_examples.jl +++ b/test/decapodes_examples.jl @@ -6,14 +6,14 @@ using ..SyntacticModels.AMR using Test import JSON3 -h = amr.Header("", "harmonic_oscillator", +h = Header("", "harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "DecaExpr", "v1.0") # The easiest way to write down a DecaExpr is in our DSL and calling the parser. -dexpr = ASKEMDecapodes.parse_decapode(quote +dexpr = parse_decapode(quote X::Form0{Point} V::Form0{Point} @@ -24,15 +24,15 @@ dexpr = ASKEMDecapodes.parse_decapode(quote end ) -annot = [amr.Annotation(:X,:Form0,amr.Name("The X variable."))] +annot = [Annotation(:X,:Form0,Name("The X variable."))] # Bundle the DecaExpr with the header metadata. -mexpr = ASKEMDecapodes.decapodes.ASKEMDecaExpr(h, dexpr, annot) +mexpr = ASKEMDecaExpr(h, dexpr, annot) # Convert a the DecaExpr to a SummationDecapode which is the # combinatorial representation. The converter lives in Decapodes/src/language.jl. -d = ASKEMDecapodes.SummationDecapode(mexpr.model) +d = SummationDecapode(mexpr.model) # We want different metadata for this representation. # The Summation prefix just means that this decapodes have @@ -40,12 +40,12 @@ d = ASKEMDecapodes.SummationDecapode(mexpr.model) # The summation operator happens in physics so often, # that you want to bake in some specialized handling to the data structure. -h = amr.Header("","harmonic_oscillator", +h = Header("","harmonic_oscillator", "modelreps.io/SummationDecapode", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "SummationDecapode", "v1.0") -mpode = ASKEMDecapodes.decapodes.ASKEMDecapode(h, d, annot) +mpode = ASKEMDecapode(h, d, annot) # The syntactic representation can be serialized as JSON. @@ -60,6 +60,6 @@ write_json_model(mexpr) # Can we read back the models we just wrote? @testset "Decapodes Readback" begin - mexpr′ = readback(mexpr,ASKEMDecapodes.decapodes.ASKEMDeca) + mexpr′ = readback(mexpr,ASKEMDeca) @test JSON3.write(mexpr) == JSON3.write(mexpr′) end \ No newline at end of file diff --git a/test/uwd_examples.jl b/test/uwd_examples.jl index d7ff6b4..0c12456 100644 --- a/test/uwd_examples.jl +++ b/test/uwd_examples.jl @@ -18,32 +18,32 @@ using Catlab.Graphics end =# -v1 = ASKEMUWDs.uwd.Typed(:x, :X) -v2 = ASKEMUWDs.uwd.Typed(:y, :Y) -v3 = ASKEMUWDs.uwd.Typed(:z, :Z) -v4 = ASKEMUWDs.uwd.Untyped(:u) +v1 = Typed(:x, :X) +v2 = Typed(:y, :Y) +v3 = Typed(:z, :Z) +v4 = Untyped(:u) c = [v1, v3] s = [ASKEMUWDs.uwd.Statement(:R, [v1,v2]), ASKEMUWDs.uwd.Statement(:S, [v2,v3]), ASKEMUWDs.uwd.Statement(:T, [v3,v2, v4])] -u = ASKEMUWDs.uwd.UWDExpr(c, s) +u = UWDExpr(c, s) @testset "UWDExpr Readback" begin s = jsonwrite(u) - ujson = jsonread(s, ASKEMUWDs.uwd.UWDTerm) + ujson = jsonread(s, UWDTerm) @test s == jsonwrite(ujson) end -uwd′ = ASKEMUWDs.construct(RelationDiagram, u) +uwd′ = construct(RelationDiagram, u) -h = AMR.Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") +h = Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") -mexpr = ASKEMUWDs.uwd.UWDModel(h, u) +mexpr = UWDModel(h, u) @testset "UWD Readback" begin write_json_model(mexpr) - mexpr′ = readback(mexpr,ASKEMUWDs.uwd.UWDTerm) + mexpr′ = readback(mexpr,UWDTerm) @test mexpr.header == mexpr′.header @test mexpr.uwd.context == mexpr′.uwd.context From 5a9f95be4603834ab209cec6e083be6f89910ab0 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Fri, 8 Dec 2023 12:24:06 -0500 Subject: [PATCH 27/35] Removed new id field from Header. Now same as before the PR. Will make new PR to add later if needed. --- src/amr.it | 1 - src/amr.jl | 2 +- test/amr_examples.jl | 2 +- test/composite_models_examples.jl | 18 +++++++++--------- test/decapodes_examples.jl | 4 ++-- test/uwd_examples.jl | 2 +- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/amr.it b/src/amr.it index 592544a..bfdc1e6 100644 --- a/src/amr.it +++ b/src/amr.it @@ -86,7 +86,6 @@ struct Annotation end struct Header - id::String name::String schema::String description::String diff --git a/src/amr.jl b/src/amr.jl index e3cf827..9edf4eb 100644 --- a/src/amr.jl +++ b/src/amr.jl @@ -64,7 +64,7 @@ function amr_to_string(amr′) Rate(t, f) => "$t::Rate = $(f.expression)" Initial(t, f) => "$t::Initial = $(f.expression)" Observable(id, n, states, f) => "# $n\n$id::Observable = $(f.expression)($states)\n" - Header(id, name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" + Header(name, s, d, sn, mv) => "\"\"\"\nASKE Model Representation: $name$mv :: $sn \n $s\n\n$d\n\"\"\"" Parameter(t, n, d, u, v, dist) => "\n# $n-- $d\n$t::Parameter{$(!u)} = $v ~ $(!dist)\n" m::amr.ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" diff --git a/test/amr_examples.jl b/test/amr_examples.jl index 64c3334..1669c39 100644 --- a/test/amr_examples.jl +++ b/test/amr_examples.jl @@ -5,7 +5,7 @@ using ACSets using ACSets.ADTs nomath = Math("") -header = Header("","SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") +header = Header("SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") model = acsetspec(:(LabelledPetriNet{Symbol}), quote S(label=:S) S(label=:I) diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index edf9da5..e7ddfc3 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -22,7 +22,7 @@ u = UWDExpr(c, s) -h = Header("","harmonic_oscillator", +h = Header("harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "DecaExpr", @@ -45,7 +45,7 @@ d1 = ASKEMDecaExpr(h, dexpr, []) # The second model is: d2 = ASKEMDecaExpr( - Header("","fricative_heating", + Header("fricative_heating", "modelreps.io/SummationDecapode", "Velocity makes it get hot, but you dissipate heat away from Q₀", "SummationDecapode", "v1.0"), @@ -62,7 +62,7 @@ d2 = ASKEMDecaExpr( ) # Now we can assemble this bad boi: -h = Header("","composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") +h = Header("composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") m = CompositeModelExpr(h, u, [OpenModel(d1, [:X, :V]), OpenModel(d2, [:V, :Q])]) interface(m) == [:X, :Q] #= TODO: FIXME @@ -84,7 +84,7 @@ Q̇ = Untyped(:Q̇) uwdʰ = UWDExpr([v, Q], [ASKEMUWDs.uwd.Statement(:drag, [v, Q₊]), ASKEMUWDs.uwd.Statement(:cooling, [Q₋, Q]), ASKEMUWDs.uwd.Statement(:superposition, [Q₊, Q₋, Q̇])]) drag = ASKEMDecaExpr( - Header("","DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), + Header("DragHeat", "modelreps.io/SummationDecapode", "velocity makes it get hot", "SummationDecapode", "v1.0"), parse_decapode(quote V::Form0{Point} Q₊::Form0{Point} @@ -95,7 +95,7 @@ drag = ASKEMDecaExpr( ) cooling = ASKEMDecaExpr( - Header("","NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), + Header("NetwonCooling", "modelreps.io/SummationDecapode", "heat dissipates to the enviornment", "SummationDecapode", "v1.0"), parse_decapode(quote Q₋::Form0{Point} Q₀::Parameter{Point} @@ -107,7 +107,7 @@ cooling = ASKEMDecaExpr( ) superposition = ASKEMDecaExpr( - Header("","LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), + Header("LinearSuperpositon", "modelreps.io/SummationDecapode", "variables be addin", "SummationDecapode", "v1.0"), parse_decapode(quote X::Form0{Point} Y::Form0{Point} @@ -117,9 +117,9 @@ superposition = ASKEMDecaExpr( end), [] ) -h = Header("","hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") +h = Header("hierarchical_composite", "modelreps.io/Composite", "A hierarchical composite model of frictional heating", "CompositeModelExpr", "v0.1") m = CompositeModelExpr(h,u, [OpenModel(d1, [:X, :V]), - CompositeModelExpr(Header("","heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), + CompositeModelExpr(Header("heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), uwdʰ, [OpenModel(drag, [:V, :Q₊]), OpenModel(cooling, [:Q₋, :Q]), OpenModel(superposition, [:X, :Y, :T])]) ]) #= TODO: FIXME @@ -134,5 +134,5 @@ dh = apex(oapply(m)) composite = OpenDecapode(m) hf = composite.model.header -write_json_model(ASKEMDecapode(Header("","flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) +write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) =# \ No newline at end of file diff --git a/test/decapodes_examples.jl b/test/decapodes_examples.jl index 5f4f042..9f6c7fd 100644 --- a/test/decapodes_examples.jl +++ b/test/decapodes_examples.jl @@ -6,7 +6,7 @@ using ..SyntacticModels.AMR using Test import JSON3 -h = Header("", "harmonic_oscillator", +h = Header( "harmonic_oscillator", "modelreps.io/DecaExpr", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "DecaExpr", @@ -40,7 +40,7 @@ d = SummationDecapode(mexpr.model) # The summation operator happens in physics so often, # that you want to bake in some specialized handling to the data structure. -h = Header("","harmonic_oscillator", +h = Header("harmonic_oscillator", "modelreps.io/SummationDecapode", "A Simple Harmonic Oscillator as a Diagrammatic Equation", "SummationDecapode", diff --git a/test/uwd_examples.jl b/test/uwd_examples.jl index 0c12456..8c1e9ed 100644 --- a/test/uwd_examples.jl +++ b/test/uwd_examples.jl @@ -38,7 +38,7 @@ end uwd′ = construct(RelationDiagram, u) -h = Header("","rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") +h = Header("rst_relation", "modelreps.io/UWD", "A demo UWD showing generic relation composition", "UWDExpr", "v0.1") mexpr = UWDModel(h, u) @testset "UWD Readback" begin From 7785c961e2525acbcd5698d97e2fb920237034c4 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Mon, 11 Dec 2023 14:46:41 -0500 Subject: [PATCH 28/35] Made oapply workaround -- it_to_orig converts an intertype summation decapode to a Decapodes one, then Decapodes.Open can be used to compose. Still need to add reverse conversion. --- src/composite_models.jl | 24 ++++++++++++++++++------ test/composite_models_examples.jl | 5 ++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/composite_models.jl b/src/composite_models.jl index e801b10..e2e7f65 100644 --- a/src/composite_models.jl +++ b/src/composite_models.jl @@ -15,7 +15,7 @@ using ..AMR.amr using ..ASKEMDecapodes.decapodes using ..ASKEMUWDs.uwd -@intertypes "composite_models.it" module composites +@intertypes "../src/composite_models.it" module composites import ..amr import ..decapodes import ..uwd @@ -32,14 +32,24 @@ interface(m::CompositeModel) = @match m begin CompositeModelExpr(h, uwd′, components) => map(ASKEMUWDs.varname, context(uwd′)) end -OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(decapodes.SummationDecapode, :Var) +using Decapodes +function it_to_orig(test::decapodes.SymSummationDecapode) + d = Decapodes.SummationDecapode{Any, Any, Symbol}() + copy_parts!(d,test, NamedTuple(Dict(k=>parts(test,k) for k in types(decapodes.SchSummationDecapode)))) + return d +end + +# OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(Decapodes.SummationDecapode, :Var) + +#= function Open(d::decapodes.SummationDecapode, names::Vector{Symbol}) legs = map(names) do name FinFunction(incident(d, name, :name), nparts(d, :Var)) end OpenSummationDecapode(d, legs...) end +=# #= apex(decapode::OpenSummationDecapode) = apex(decapode.cospan) @@ -48,9 +58,9 @@ feet(decapode::OpenSummationDecapode) = decapode.feet =# # Extract an open decapode from the decapode expression and the interface -open_decapode(d, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) -open_decapode(d::ASKEMDecaExpr, interface) = Open(ASKEMDecapodes.SummationDecapode(d.model), interface) -open_decapode(d::ASKEMDecapode, interface) = Open(d.model, interface) +open_decapode(d, interface) = Decapodes.Open(it_to_orig(ASKEMDecapodes.SummationDecapode(d.model)), interface) +open_decapode(d::ASKEMDecaExpr, interface) = Decapodes.Open(it_to_orig(ASKEMDecapodes.SummationDecapode(d.model)), interface) +open_decapode(d::ASKEMDecapode, interface) = Decapodes.Open(it_to_orig(d.model), interface) """ Catlab.oapply(m::CompositeModel) @@ -74,16 +84,18 @@ function Catlab.oapply(m::CompositeModel) !(mᵢ) # oapply all the component models recursively end # OpenDecapode(ASKEMDecapode(h, apex(!(uwd, Ms))), interface(m)) # Then we call the oapply from Decapodes. - Open(apex(!(uwd, Ms)), uwd[[:outer_junction, :variable]]) # Then we call the oapply from Decapodes. + Decapodes.Open(apex(!(uwd, Ms)), uwd[[:outer_junction, :variable]]) # Then we call the oapply from Decapodes. end end end end +#= function OpenDecapode(m::CompositeModel) composite = oapply(m) feet = map(l->only(dom(l)[:name]), legs(composite)) OpenDecapode(ASKEMDecapode(m.header,apex(composite)), feet) end +=# end \ No newline at end of file diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index e7ddfc3..d6ef3d4 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -67,11 +67,13 @@ m = CompositeModelExpr(h, u, [OpenModel(d1, [:X, :V]), OpenModel(d2, [:V, :Q])]) interface(m) == [:X, :Q] #= TODO: FIXME write_json_model(m) # you can see from this little model (two coupled odes even) that the jsons will not be human editable. +=# # now we can interpret this big data structure to execute a composition! composite = oapply(m) display(apex(composite)) to_graphviz(apex(composite)) +#= TODO: FIXME sm_write_json_acset(apex(composite),"$(m.header.name)-acset") =# @@ -129,9 +131,10 @@ write_json_model(m) m′ = readback(m) @test JSON3.write(m) == JSON3.write(m′) end - +=# dh = apex(oapply(m)) +#= TODO: FIXME composite = OpenDecapode(m) hf = composite.model.header write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) From 6feef74b4e2517622112fda39f79a23246ecd113 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Mon, 11 Dec 2023 15:27:32 -0500 Subject: [PATCH 29/35] Made coversion from Decapodes.SummationDecapode to intertype version. Updated remaining OpenDecapode function to oapply composite model and convert back to intertype. --- src/composite_models.jl | 31 ++++++++++--------------------- test/composite_models_examples.jl | 2 +- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/composite_models.jl b/src/composite_models.jl index e2e7f65..3b8278e 100644 --- a/src/composite_models.jl +++ b/src/composite_models.jl @@ -1,6 +1,6 @@ module Composites -export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode, oapply, Open +export CompositeModelExpr, OpenModel, OpenDecapode, CompositeModel, interface, open_decapode, oapply using Catlab @@ -34,28 +34,17 @@ end using Decapodes -function it_to_orig(test::decapodes.SymSummationDecapode) +function it_to_orig(x::decapodes.SymSummationDecapode) d = Decapodes.SummationDecapode{Any, Any, Symbol}() - copy_parts!(d,test, NamedTuple(Dict(k=>parts(test,k) for k in types(decapodes.SchSummationDecapode)))) + copy_parts!(d,x, NamedTuple(Dict(k=>parts(x,k) for k in types(decapodes.SchSummationDecapode)))) return d end -# OpenSummationDecapodeOb, OpenSummationDecapode = OpenACSetTypes(Decapodes.SummationDecapode, :Var) - -#= -function Open(d::decapodes.SummationDecapode, names::Vector{Symbol}) - legs = map(names) do name - FinFunction(incident(d, name, :name), nparts(d, :Var)) - end - OpenSummationDecapode(d, legs...) +function orig_to_it(y::Decapodes.SummationDecapode) + d = decapodes.SummationDecapode{Symbol, Symbol, Symbol}() + copy_parts!(d,y, NamedTuple(Dict(k=>parts(y,k) for k in types(Schema(Decapodes.SchSummationDecapode))))) + return d end -=# - -#= -apex(decapode::OpenSummationDecapode) = apex(decapode.cospan) -legs(decapode::OpenSummationDecapode) = legs(decapode.cospan) -feet(decapode::OpenSummationDecapode) = decapode.feet -=# # Extract an open decapode from the decapode expression and the interface open_decapode(d, interface) = Decapodes.Open(it_to_orig(ASKEMDecapodes.SummationDecapode(d.model)), interface) @@ -90,12 +79,12 @@ function Catlab.oapply(m::CompositeModel) end end -#= + function OpenDecapode(m::CompositeModel) composite = oapply(m) feet = map(l->only(dom(l)[:name]), legs(composite)) - OpenDecapode(ASKEMDecapode(m.header,apex(composite)), feet) + apx = orig_to_it(apex(composite)) + OpenDecapode(ASKEMDecapode(m.header,apx,[]), feet) end -=# end \ No newline at end of file diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index d6ef3d4..74b3cd9 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -134,8 +134,8 @@ end =# dh = apex(oapply(m)) -#= TODO: FIXME composite = OpenDecapode(m) hf = composite.model.header +#= TODO: FIXME write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) =# \ No newline at end of file From ab9a25828cbce661e2eb4dee6f0364e519c372c8 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Tue, 12 Dec 2023 09:53:26 -0500 Subject: [PATCH 30/35] Uncommented sm_write_json_model, 2nd write_json_model method and most uses in examples. jsonwrite in first method still not working for empty annotations, so those examples are still commented. --- test/composite_models_examples.jl | 6 +----- test/core.jl | 5 ++--- test/decapodes_examples.jl | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index 74b3cd9..30eaabd 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -73,9 +73,7 @@ write_json_model(m) # you can see from this little model (two coupled odes even) composite = oapply(m) display(apex(composite)) to_graphviz(apex(composite)) -#= TODO: FIXME sm_write_json_acset(apex(composite),"$(m.header.name)-acset") -=# # TESTING NESTED COMPOSITION @@ -136,6 +134,4 @@ dh = apex(oapply(m)) composite = OpenDecapode(m) hf = composite.model.header -#= TODO: FIXME -write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model)) -=# \ No newline at end of file +write_json_model(ASKEMDecapode(Header("flattened_composite", hf.schema, "A flattened version of the composite_physics model.", hf.schema_name, hf.model_version), composite.model.model, [])) diff --git a/test/core.jl b/test/core.jl index ba6f186..ce76fcc 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,7 +1,7 @@ include("../src/SyntacticModels.jl") using .SyntacticModels -# using .SyntacticModels.ASKEMDecapodes +using .SyntacticModels.ASKEMDecapodes using Test using JSON3 @@ -14,7 +14,6 @@ end readback(m, T, prefix=joinpath(@__DIR__, "json")) = jsonread(joinpath(jsondir, "$(m.header.name).json"),T) -#= write_json_model(m::ASKEMDecapodes.ASKEMDecapode, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(m.header.name).json"), "w") do fp d = Dict("header"=>m.header, "model"=>generate_json_acset(m.model), "_type"=> "ASKEMDecapode") JSON3.pretty(fp, d) @@ -23,7 +22,7 @@ end sm_write_json_acset(X, fname, prefix=joinpath(@__DIR__, "json")) = open(joinpath(prefix, "$(fname).json"), "w") do fp JSON3.pretty(fp, generate_json_acset(X)) end -=# + try mkdir(joinpath(@__DIR__, "json")) diff --git a/test/decapodes_examples.jl b/test/decapodes_examples.jl index 9f6c7fd..04cac1b 100644 --- a/test/decapodes_examples.jl +++ b/test/decapodes_examples.jl @@ -55,7 +55,7 @@ write_json_model(mexpr) # We could also use the JSON serialization built into Catlab # to serialize the resulting combinatorial representation -# sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") +sm_write_json_acset(mpode.model, "$(mpode.header.name)-acset") # Can we read back the models we just wrote? From 6af697758c3f3eecc4f8af3d36e38a73ff36e161 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Tue, 12 Dec 2023 11:58:53 -0500 Subject: [PATCH 31/35] Added clarifying comments in composite_models_examples.jl about fixes needed to make write_json_model and readback work. --- test/composite_models_examples.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/composite_models_examples.jl b/test/composite_models_examples.jl index 30eaabd..e042680 100644 --- a/test/composite_models_examples.jl +++ b/test/composite_models_examples.jl @@ -65,7 +65,7 @@ d2 = ASKEMDecaExpr( h = Header("composite_physics", "modelreps.io/Composite", "A composite model", "CompositeModelExpr", "v0.0") m = CompositeModelExpr(h, u, [OpenModel(d1, [:X, :V]), OpenModel(d2, [:V, :Q])]) interface(m) == [:X, :Q] -#= TODO: FIXME +#= TODO: write_json_model will work with the upstream fix to jsonwrite for vectors to handle empty case write_json_model(m) # you can see from this little model (two coupled odes even) that the jsons will not be human editable. =# @@ -122,11 +122,12 @@ m = CompositeModelExpr(h,u, [OpenModel(d1, [:X, :V]), CompositeModelExpr(Header("heating_dynamics", "modelreps.io/Composite", "A formula for heating - cooling", "CompositeModelExpr", "v0.1"), uwdʰ, [OpenModel(drag, [:V, :Q₊]), OpenModel(cooling, [:Q₋, :Q]), OpenModel(superposition, [:X, :Y, :T])]) ]) -#= TODO: FIXME +#= TODO: write_json_model will work with the upstream fix to jsonwrite for vectors to handle empty case + TODO: readback errors when reading in UWDExpr of CompositeModel. It requires an upstream fix to jsonread to recognize variant types and not just sum types. write_json_model(m) @testset "Composite Model Readback" begin - m′ = readback(m) + m′ = readback(m,CompositeModel) @test JSON3.write(m) == JSON3.write(m′) end =# From cf7722013dc97d5ba0177e864656bba726bd5789 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 14 Dec 2023 10:11:59 -0500 Subject: [PATCH 32/35] Added acsetspec converter from ADTs version to intertype. Updated types in .it as needed. Load to AMR from expression examples now work. Need to update amr_to_string (and check other functions) to match current intertypes. --- src/amr.it | 10 ++++- src/amr.jl | 105 +++++++++++++++++++++++++++++++------------ test/amr_examples.jl | 55 ++++++++++++----------- 3 files changed, 113 insertions(+), 57 deletions(-) diff --git a/src/amr.it b/src/amr.it index bfdc1e6..d89e5ee 100644 --- a/src/amr.it +++ b/src/amr.it @@ -1,7 +1,12 @@ +@sum Val begin + ValSymbol(_1::Symbol) + ValString(_1::String) +end + @sum Args begin - Value(_1::Symbol) - Kwarg(_1::Symbol, _2::Args) + Value(_1::Val) + Kwarg(_1::Symbol, _2::Val) end struct Statement @@ -44,6 +49,7 @@ end StandardNormal(str::String) Normal(mean::Float64, variance::Float64) PointMass(value::Float64) + Undefined(str::String) end struct Observable diff --git a/src/amr.jl b/src/amr.jl index 9edf4eb..8e6aa03 100644 --- a/src/amr.jl +++ b/src/amr.jl @@ -2,10 +2,10 @@ module AMR export amr, Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, Rate, Initial, Parameter, Time, - StandardUniform, Uniform, StandardNormal, Normal, PointMass, + StandardUniform, Uniform, StandardNormal, Normal, PointMass, Undefined, Semantic, Header, ODERecord, ODEList, ASKEModel, # Typing, distro_string, # amr_to_string, - Annotation, Note, Name, Description, Grounding, Units + Annotation, Note, Name, Description, Grounding, Units, nomath, nounit using Reexport @reexport using MLStyle @@ -15,10 +15,12 @@ using ACSets.ACSetInterface using StructTypes -@intertypes "amr.it" module amr end +@intertypes "../src/amr.it" module amr end using .amr +nomath = Math("") +nounit = Unit("", nomath) function distro_string(d::Distribution) @match d begin @@ -69,7 +71,7 @@ function amr_to_string(amr′) m::amr.ACSetSpec => "Model = begin\n$(padlines(sprint(show, m),2))\nend" ODEList(l) => "ODE_Equations = begin\n" * padlines(join(map(!, l), "\n")) * "\nend" ODERecord(rts, init, para, time) => join(vcat(["ODE_Record = begin\n"], !rts , !init, !para, [!time, "end"]), "\n") - vs::Vector{Pair} => map(vs) do v; "$(v[1]) => $(v[2])," end |> x-> join(x, "\n") + vs::Vector{amr.Pair} => map(vs) do v; "$(v.first) => $(v.second)," end |> x-> join(x, "\n") vs::Vector{Semantic} => join(map(!, vs), "\n\n") xs::Vector => map(!, xs) Typing(system, map) => "Typing = begin\n$(padlines(!system, 2))\nTypeMap = [\n$(padlines(!map, 2))]\nend" @@ -78,7 +80,7 @@ function amr_to_string(amr′) end end end -#= + block(exprs) = begin q = :(begin @@ -130,25 +132,25 @@ optload(d, path, default=nothing) = begin end function petrispec(dict::AbstractDict) - findkwarg(kwarg::Symbol, d::AbstractDict, path, default=nothing) = Kwarg(kwarg, Value(optload(d, path, default))) + findkwarg(kwarg::Symbol, d::AbstractDict, path, default=nothing) = ADTs.Kwarg(kwarg, ADTs.Value(optload(d, path, default))) loadstate(s) = begin - Statement(:S, [findkwarg(k, s, p, :nothing) for (k,p) in [(:id, "id"), (:name, "name"), (:units, ["units", "expression"])]]) + ADTs.Statement(:S, [findkwarg(k, s, p, :nothing) for (k,p) in [(:id, "id"), (:name, "name"), (:units, ["units", "expression"])]]) end states = [loadstate(s) for s in dict["states"]] transi = [ - Statement(:T, - [Kwarg(:id, Value(Symbol(t["id"]))), Kwarg(:name, Value(t["properties"]["name"])), Kwarg(:desc, Value(t["properties"]["description"])) ] + ADTs.Statement(:T, + [ADTs.Kwarg(:id, ADTs.Value(Symbol(t["id"]))), ADTs.Kwarg(:name, ADTs.Value(t["properties"]["name"])), ADTs.Kwarg(:desc, ADTs.Value(t["properties"]["description"])) ] ) for t in dict["transitions"]] inputs = [[ - Statement(:I, - [Kwarg(:is, Value(i)), Kwarg(:it, Value(t["id"]))]) for i in t["input"]] for t in dict["transitions"] + ADTs.Statement(:I, + [ADTs.Kwarg(:is, ADTs.Value(i)), ADTs.Kwarg(:it, ADTs.Value(t["id"]))]) for i in t["input"]] for t in dict["transitions"] ] |> Base.Flatten |> collect outputs = [[ - Statement(:O, - [Kwarg(:os, Value(i)), Kwarg(:ot, Value(t["id"]))]) for i in t["output"]] for t in dict["transitions"] + ADTs.Statement(:O, + [ADTs.Kwarg(:os, ADTs.Value(i)), ADTs.Kwarg(:ot, ADTs.Value(t["id"]))]) for i in t["output"]] for t in dict["transitions"] ] |> Base.Flatten |> collect - ACSetSpec(:AMRPetriNet, vcat(states, transi, inputs, outputs)) + ADTs.ACSetSpec(:AMRPetriNet, vcat(states, transi, inputs, outputs)) end function load(::Type{Unit}, d::AbstractDict) @@ -172,8 +174,8 @@ end function load(::Type{Distribution}, d::AbstractDict) @match d begin - Dict("type"=>"StandardUniform1") => StandardUniform - Dict("type"=>"StandardNormal") => StandardNormal + Dict("type"=>"StandardUniform1") => StandardUniform("") + Dict("type"=>"StandardNormal") => StandardNormal("") Dict("type"=>"Uniform", "parameters"=>p) => Uniform(p["minimum"], p["maximum"]) Dict("type"=>"Uniform1", "parameters"=>p) => Uniform(p["minimum"], p["maximum"]) Dict("type"=>"Normal", "parameters"=>p) => Normal(p["mu"], p["var"]) @@ -181,7 +183,9 @@ function load(::Type{Distribution}, d::AbstractDict) end end -load(::Type{Distribution}, ::Nothing) = PointMass(missing) +# TODO: determine best way to handle non-specified distributions +# load(::Type{Distribution}, ::Nothing) = PointMass(missing) +load(::Type{Distribution}, ::Nothing) = Undefined("") function load(::Type{Note}, d::AbstractDict) @match d begin @@ -197,13 +201,19 @@ end function load(::Type{Parameter}, d::AbstractDict) u = load(Unit, d) + # TODO: determine best way to handle non-specified distributions + if isnothing(get(d,"distribution", nothing)) + tmp_dist = !isnothing(get(d,"value", nothing)) ? PointMass(d["value"]) : Undefined("") + else + tmp_dist = load(Distribution, d["distribution"]) + end Parameter( Symbol(d["id"]), d["name"], d["description"], u, d["value"], - load(Distribution, get(d,"distribution", nothing)) + tmp_dist ) end @@ -291,12 +301,14 @@ function docval(exp::Expr) end function load(d::Type{Distribution}, ex::Expr) + # println(ex) @matchast ex quote - U(0,1) => StandardUniform + U(0,1) => StandardUniform("") U($min,$max) => Uniform(min, max) - N(0,1) => StandardNormal + N(0,1) => StandardNormal("") N($mu,$var) => Normal(mu, var) δ($value) => PointMass(value) + Undefined() => Undefined("") _ => error("Failed to find distribution in $ex") end end @@ -306,8 +318,9 @@ function load(::Type{Parameter}, ex::Expr) id, u, val, dist = @matchast ex quote ($id::Parameter{} = ($val ~ $d)) => (id, nounit, val, load(Distribution, d)) ($id::Parameter{$u} = ($val ~ $d)) => (id, load(Unit, u), val, load(Distribution, d)) - ($id::Parameter{} = $val) => (id, nounit, val, PointMass(missing)) - ($id::Parameter{$u} = $val) => (id, load(Unit, u), val, PointMass(missing)) + # TODO: determine best way to handle non-specified distributions + ($id::Parameter{} = $val) => (id, nounit, val, Undefined("")) # PointMass(missing), PointMass(NaN) + ($id::Parameter{$u} = $val) => (id, load(Unit, u), val, Undefined("")) end Parameter(id, name, desc, u, val, dist) end @@ -342,8 +355,8 @@ function load(::Type{Header}, ex::String) Header(strip(name), strip(schema), strip(desc), strip(schema_name), strip(version)) end -function load(::Type{ACSetSpec}, ex::Expr) - let ! = x->load(ACSetSpec, x) +function load(::Type{ADTs.ACSetSpec}, ex::Expr) + let ! = x->load(ADTs.ACSetSpec, x) @match ex begin Expr(:(=), name, body) => @match body.args[2] begin Expr(:(=), type, body) => acsetspec(type, body) @@ -354,15 +367,51 @@ function load(::Type{ACSetSpec}, ex::Expr) end end +function convert_val_to_it(v) + if typeof(v)==Symbol + it = amr.ValSymbol(v) + elseif typeof(v)==String + it = amr.ValString(v) + else + error("Value type not found: $v") + end + it +end + +function convert_acsetspec_to_it(x::ADTs.ACSetSpec) + y = amr.ACSetSpec(x.acstype,[]) + for (ii, stmt) in enumerate(x.body) + # println(ii," ",stmt) + push!(y.body,amr.Statement(stmt.table,[])) + for (jj, arg) in enumerate(stmt.element) + # println(jj," ",arg) + if length(propertynames(arg))==2 + # println(amr.Kwarg(arg._1,amr.Value(arg._2._1))) + push!(y.body[ii].element,amr.Kwarg(arg._1,convert_val_to_it(arg._2._1))) + elseif length(propertynames(arg))==1 + # println(amr.Value(arg._1)) + push!(y.body[ii].element,convert_val_to_it(arg._1)) + else + error("Bad Arg in ACSetSpec, stmt $ii, arg $jj: $arg") + end + end + end + y +end + +function load(::Type{amr.ACSetSpec}, ex::Expr) + convert_acsetspec_to_it(load(ADTs.ACSetSpec, ex)) +end + function load(::Type{Typing}, ex::Expr) let !(x) = load(Typing, x) @match ex begin - Expr(:(=), :Model, body) => load(ACSetSpec, ex) + Expr(:(=), :Model, body) => load(amr.ACSetSpec, ex) Expr(:(=), :TypeMap, list) => !list Expr(:(=), :Typing, body) => Typing(!(body.args[2]), !(body.args[4])) Expr(:vect, args...) => map(args) do arg @match arg begin - Expr(:call, :(=>), a, b) => Pair(a,b) + Expr(:call, :(=>), a, b) => amr.Pair(a,b) _ => error("The type map is expected to be pairs defined with a => fa. Got $arg") end end @@ -374,7 +423,7 @@ end function load(::Type{ASKEModel}, ex::Expr) elts = map(ex.args) do arg @match arg begin - Expr(:macrocall, var"@doc", _, s, ex) => (load(Header, s), load(ACSetSpec, ex)) + Expr(:macrocall, var"@doc", _, s, ex) => (load(Header, s), load(amr.ACSetSpec, ex)) Expr(:(=), :ODE_Record, body) => load(ODEList, arg) Expr(:(=), :ODE_Equations, body) => load(ODEList, arg) Expr(:(=), :Typing, body) => load(Typing, arg) @@ -383,5 +432,5 @@ function load(::Type{ASKEModel}, ex::Expr) end ASKEModel(elts[2][1], elts[2][2], [elts[4], elts[6]]) end -=# + end # module end \ No newline at end of file diff --git a/test/amr_examples.jl b/test/amr_examples.jl index 1669c39..d3ee84b 100644 --- a/test/amr_examples.jl +++ b/test/amr_examples.jl @@ -4,7 +4,6 @@ using Test using ACSets using ACSets.ADTs -nomath = Math("") header = Header("SIR", "amr-schemas:petri_schema.json", "The SIR Model of disease", "petrinet", "0.2") model = acsetspec(:(LabelledPetriNet{Symbol}), quote S(label=:S) @@ -58,11 +57,12 @@ odelist = ODEList([ ]) -#= +#= TODO: amr₁ = AMR.ASKEModel(header, model, [ode] ) +=# typesystem = acsetspec(:(LabelledPetriNet{Symbol}), quote S(label=:Pop) @@ -82,7 +82,7 @@ typesystem = acsetspec(:(LabelledPetriNet{Symbol}), quote O(os=:Pop, it=:strata) end) - +#= TODO: typing = Typing( typesystem, [ @@ -114,6 +114,7 @@ map(AMR.amr_to_expr(amr₂.semantics[1]).args[2].args) do s; println(s) end AMR.amr_to_expr(amr₂.semantics[1]).args[2] AMR.amr_to_expr(amr₂.semantics[1]) AMR.amr_to_expr(amr₂) |> println +=# h = AMR.load(Header, Dict("name" => "SIR Model", "schema" => "https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json", @@ -324,17 +325,17 @@ semantics_dict = JSON.parse(semantics_str) AMR.load(ODERecord, semantics_dict["ode"]) |> AMR.amr_to_string |> println sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir.json"])) -AMR.optload(AMRExamples.sirmodel_dict, ["model", :states], nothing) |> show -sirmodel = AMR.load(ASKEModel, sirmodel_dict) -sirmodel |> AMR.amr_to_string |> println +AMR.optload(sirmodel_dict, ["model", :states], nothing) |> show +# TODO: sirmodel = AMR.load(ASKEModel, sirmodel_dict) +# TODO: sirmodel |> AMR.amr_to_string |> println sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir_typed.json"])) semtyp = sirmodel_dict["semantics"]["typing"] -sirmodel = AMR.load(Typing, semtyp) |> AMR.amr_to_string |> println +# TODO: sirmodel = AMR.load(Typing, semtyp) |> AMR.amr_to_string |> println -sirmodel = AMR.load(ASKEModel, sirmodel_dict) -sirmodel |> AMR.amr_to_string |> println +# TODO: sirmodel = AMR.load(ASKEModel, sirmodel_dict) +# TODO: sirmodel |> AMR.amr_to_string |> println # Deserializing from Human readable strings @@ -370,7 +371,7 @@ end \"\"\" R₀ -- Total recovered population at timestep 0 \"\"\" - R0::Parameter{} = 0.0 ~ δ(missing) + R0::Parameter{} = 0.0 ~ Undefined() """ paramexp = Meta.parse(paramstr) param = AMR.load(Parameter, paramexp) @@ -383,13 +384,13 @@ end \"\"\" R₀ -- Total recovered population at timestep 0 \"\"\" - R0::Parameter{persons/day} = 0.0 ~ δ(missing) + R0::Parameter{persons/day} = 0.0 ~ Undefined() """ paramexp = Meta.parse(paramstr) param = AMR.load(Parameter, paramexp) @test param.id == :R0 @test param.units == Unit("persons / day", AMR.nomath) - @test param.distribution == PointMass(:missing) + @test param.distribution == Undefined("") # PointMass(:missing) paramstr = raw""" \"\"\" @@ -423,22 +424,22 @@ gamma::Parameter{} = 0.14 ~ U(0,1) \"\"\" S₀ -- Total susceptible population at timestep 0 \"\"\" -S0::Parameter{} = 1000.0 ~ δ(missing) +S0::Parameter{} = 1000.0 ~ Undefined() \"\"\" I₀ -- Total infected population at timestep 0\"\"\" -I0::Parameter{} = 1.0 ~ δ(missing) +I0::Parameter{} = 1.0 ~ Undefined() \"\"\" R₀ -- Total recovered population at timestep 0\"\"\" -R0::Parameter{} = 0.0 ~ δ(missing) +R0::Parameter{} = 0.0 ~ Undefined() t::Time{day} end """) ol = AMR.load(ODEList, odelist_expr) -AMR.amr_to_string(ol) |> println +# TODO: AMR.amr_to_string(ol) |> println header_expr = Meta.parse(raw""" @@ -493,15 +494,15 @@ gamma::Parameter{} = 0.14 ~ U(0,1) \"\"\" S₀ -- Total susceptible population at timestep 0 \"\"\" -S0::Parameter{} = 1000.0 ~ δ(missing) +S0::Parameter{} = 1000.0 ~ Undefined() \"\"\" I₀ -- Total infected population at timestep 0\"\"\" -I0::Parameter{} = 1.0 ~ δ(missing) +I0::Parameter{} = 1.0 ~ Undefined() \"\"\" R₀ -- Total recovered population at timestep 0\"\"\" -R0::Parameter{} = 0.0 ~ δ(missing) +R0::Parameter{} = 0.0 ~ Undefined() t::Time{day} end @@ -542,14 +543,14 @@ end println(modelrep) model_expr = Base.Meta.parse(modelrep) @test AMR.load(ASKEModel, model_expr).header isa Header -@test AMR.load(ASKEModel, model_expr).model isa ACSetSpec -@test length(AMR.load(ODEList, model_expr.args[4]).statements) == 8 -@test length(AMR.load(ASKEModel, model_expr).semantics[1].statements) == 8 -@test AMR.load(Typing, model_expr.args[6]).system isa ACSetSpec -@test AMR.load(Typing, model_expr.args[6]).map isa Vector{Pair} -println(AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) -@test_skip (AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) == modelrep +@test AMR.load(ASKEModel, model_expr).model isa AMR.amr.ACSetSpec +@test length(AMR.load(ODEList, model_expr.args[4]).statements) == 8 # Changed δ(missing) to Undefined() in odelist_expr +@test length(AMR.load(ASKEModel, model_expr).semantics[1].statements) == 8 # Changed δ(missing) to Undefined() in modelrep +@test AMR.load(AMR.Typing, model_expr.args[6]).system isa AMR.amr.ACSetSpec +@test AMR.load(AMR.Typing, model_expr.args[6]).map isa Vector{AMR.amr.Pair} +# TODO: println(AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) +# TODO: @test_skip (AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) == modelrep + -=# end # module From 403b57332e91d836e61b093014417127da01b9d2 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 14 Dec 2023 11:52:07 -0500 Subject: [PATCH 33/35] Fixed convert_pair_to_it and changed convert_val_to_it to handle QuoteNode. Need to update load from dict for ASKEModel and Typing and amr_to_string. --- src/amr.jl | 9 ++++++--- test/amr_examples.jl | 16 +++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/amr.jl b/src/amr.jl index 8e6aa03..36dfae6 100644 --- a/src/amr.jl +++ b/src/amr.jl @@ -3,8 +3,8 @@ module AMR export amr, Math, MathML, ExpressionFormula, Unit, Distribution, Observable, Expression, Rate, Initial, Parameter, Time, StandardUniform, Uniform, StandardNormal, Normal, PointMass, Undefined, - Semantic, Header, ODERecord, ODEList, ASKEModel, # Typing, - distro_string, # amr_to_string, + Semantic, Header, ODERecord, ODEList, ASKEModel, Typing, + distro_string, amr_to_string, Annotation, Note, Name, Description, Grounding, Units, nomath, nounit using Reexport @@ -368,6 +368,7 @@ function load(::Type{ADTs.ACSetSpec}, ex::Expr) end function convert_val_to_it(v) + if typeof(v)==QuoteNode v=v.value end if typeof(v)==Symbol it = amr.ValSymbol(v) elseif typeof(v)==String @@ -378,8 +379,10 @@ function convert_val_to_it(v) it end +convert_pair_to_it(p) = amr.Pair(p[1],p[2]) + function convert_acsetspec_to_it(x::ADTs.ACSetSpec) - y = amr.ACSetSpec(x.acstype,[]) + y = amr.ACSetSpec(Symbol(x.acstype),[]) for (ii, stmt) in enumerate(x.body) # println(ii," ",stmt) push!(y.body,amr.Statement(stmt.table,[])) diff --git a/test/amr_examples.jl b/test/amr_examples.jl index d3ee84b..86f575b 100644 --- a/test/amr_examples.jl +++ b/test/amr_examples.jl @@ -57,12 +57,10 @@ odelist = ODEList([ ]) -#= TODO: amr₁ = AMR.ASKEModel(header, - model, + AMR.convert_acsetspec_to_it(model), [ode] ) -=# typesystem = acsetspec(:(LabelledPetriNet{Symbol}), quote S(label=:Pop) @@ -82,26 +80,26 @@ typesystem = acsetspec(:(LabelledPetriNet{Symbol}), quote O(os=:Pop, it=:strata) end) -#= TODO: -typing = Typing( - typesystem, - [ +typing = amr.Typing( + AMR.convert_acsetspec_to_it(typesystem), + AMR.convert_pair_to_it.([ (:S=>:Pop), (:I=>:Pop), (:R=>:Pop), (:inf=>:inf), (:rec=>:disease) - ] + ]) ) amr₂ = ASKEModel(header, - model, + AMR.convert_acsetspec_to_it(model), [ odelist, typing ] ) +#= TODO: println() println(amr_to_string(amr₁)) println() From dc77e6836011ba5baa81da29950ad11fc28765d5 Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 14 Dec 2023 12:09:52 -0500 Subject: [PATCH 34/35] Edited load Typing and ASKEModel from dict. Typing part requires Pairs of Strings for some examples. Also still need to fix amr_to_string and amr_to_expr. --- src/amr.jl | 4 ++-- test/amr_examples.jl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/amr.jl b/src/amr.jl index 36dfae6..dc90594 100644 --- a/src/amr.jl +++ b/src/amr.jl @@ -238,7 +238,7 @@ end function load(::Type{Typing}, d::AbstractDict) @match d begin - Dict("type_system"=>s, "type_map"=>m) => begin @show m; Typing(petrispec(s), [x[1]=> x[2] for x in m]) end + Dict("type_system"=>s, "type_map"=>m) => begin @show m; Typing(convert_acsetspec_to_it(petrispec(s)), convert_pair_to_it.([x[1]=> x[2] for x in m])) end _ => error("Typing judgement was not properly encoded in $d") end end @@ -246,7 +246,7 @@ end function load(::Type{ASKEModel}, d::AbstractDict) hdr = load(Header, d) hdr.schema_name == "petrinet" || error("only petrinet models are supported") - mdl = petrispec(d["model"]) + mdl = convert_acsetspec_to_it(petrispec(d["model"])) sem = [] if haskey(d["semantics"], "ode") push!(sem, load(ODERecord, d["semantics"]["ode"])) diff --git a/test/amr_examples.jl b/test/amr_examples.jl index 86f575b..f3fbf73 100644 --- a/test/amr_examples.jl +++ b/test/amr_examples.jl @@ -99,12 +99,12 @@ amr₂ = ASKEModel(header, ] ) -#= TODO: println() println(amr_to_string(amr₁)) println() println(amr_to_string(amr₂)) +#= TODO: AMR.amr_to_expr(amr₁) |> println AMR.amr_to_expr(amr₂.header) |> println AMR.amr_to_expr(amr₂.model) |> println @@ -324,8 +324,8 @@ AMR.load(ODERecord, semantics_dict["ode"]) |> AMR.amr_to_string |> println sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir.json"])) AMR.optload(sirmodel_dict, ["model", :states], nothing) |> show -# TODO: sirmodel = AMR.load(ASKEModel, sirmodel_dict) -# TODO: sirmodel |> AMR.amr_to_string |> println +sirmodel = AMR.load(ASKEModel, sirmodel_dict) +sirmodel |> AMR.amr_to_string |> println sirmodel_dict = JSON.parsefile(joinpath([@__DIR__, "inputs", "sir_typed.json"])) semtyp = sirmodel_dict["semantics"]["typing"] From e07fb297f08fbde11c973f98d35ab5866d906f0b Mon Sep 17 00:00:00 2001 From: p-stokes Date: Thu, 14 Dec 2023 13:02:44 -0500 Subject: [PATCH 35/35] Corrected amr var name in amr_to_expr. Now, problem with amr_to_expr relates to intertype version of ACSetSpec is different format from ADTs. May want to convert back for expr or string. amr_to_string runs but doesn't look good. --- src/amr.jl | 9 +++++---- test/amr_examples.jl | 14 ++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/amr.jl b/src/amr.jl index dc90594..b765cd5 100644 --- a/src/amr.jl +++ b/src/amr.jl @@ -29,6 +29,7 @@ function distro_string(d::Distribution) StandardNormal(s) => "N(0,1)" Normal(mu, var) => "N($mu,$var)" PointMass(value) => "δ($value)" + Undefined(s) => "Undefined()" end end @@ -92,9 +93,9 @@ end extract_acsetspec(s::String) = join(split(s, " ")[2:end], " ") |> Meta.parse -function amr_to_expr(amr) +function amr_to_expr(amr′) let ! = amr_to_expr - @match amr begin + @match amr′ begin s::String => s Math(s) => :(Math($(!s))) Presentation(s) => :(Presentation($(!s))) @@ -106,10 +107,10 @@ function amr_to_expr(amr) Observable(id, n, states, f) => begin "$n"; :(@doc $x $id::Observable = $(f.expression)($states)) end Header(name, s, d, sn, mv) => begin x = "ASKE Model Representation: $name$mv :: $sn \n $s\n\n$d"; :(@doc $x) end Parameter(t, n, d, u, v, dist) => begin x = "$n-- $d"; :(@doc $x $t::Parameter{$(!u)} = $v ~ $(!dist)) end - m::ACSetSpec => :(Model = begin $(extract_acsetspec(sprint(show, m))) end) + m::amr.ACSetSpec => :(Model = begin $(extract_acsetspec(sprint(show, m))) end) ODEList(l) => :(ODE_Equations = $(block(map(!, l)))) ODERecord(rts, init, para, time) => :(ODE_Record = (rates=$(!rts), initials=$(!init), parameters=$(!para), time=!time)) - vs::Vector{Pair} => begin ys = map(vs) do v; :($(v[1]) => $(v[2])) end; block(ys) end + vs::Vector{amr.Pair} => begin ys = map(vs) do v; :($(v[1]) => $(v[2])) end; block(ys) end vs::Vector{Semantic} => begin ys = map(!, vs); block(ys) end xs::Vector => begin ys = map(!, xs); block(ys) end Typing(system, map) => :(Typing = $(!system); TypeMap = $(block(map))) diff --git a/test/amr_examples.jl b/test/amr_examples.jl index f3fbf73..7b7abc7 100644 --- a/test/amr_examples.jl +++ b/test/amr_examples.jl @@ -104,15 +104,13 @@ println(amr_to_string(amr₁)) println() println(amr_to_string(amr₂)) -#= TODO: -AMR.amr_to_expr(amr₁) |> println +# TODO: AMR.amr_to_expr(amr₁) |> println AMR.amr_to_expr(amr₂.header) |> println -AMR.amr_to_expr(amr₂.model) |> println +# TODO: AMR.amr_to_expr(amr₂.model) |> println map(AMR.amr_to_expr(amr₂.semantics[1]).args[2].args) do s; println(s) end AMR.amr_to_expr(amr₂.semantics[1]).args[2] AMR.amr_to_expr(amr₂.semantics[1]) -AMR.amr_to_expr(amr₂) |> println -=# +# TODO: AMR.amr_to_expr(amr₂) |> println h = AMR.load(Header, Dict("name" => "SIR Model", "schema" => "https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.5/petrinet/petrinet_schema.json", @@ -437,7 +435,7 @@ end """) ol = AMR.load(ODEList, odelist_expr) -# TODO: AMR.amr_to_string(ol) |> println +AMR.amr_to_string(ol) |> println header_expr = Meta.parse(raw""" @@ -546,8 +544,8 @@ model_expr = Base.Meta.parse(modelrep) @test length(AMR.load(ASKEModel, model_expr).semantics[1].statements) == 8 # Changed δ(missing) to Undefined() in modelrep @test AMR.load(AMR.Typing, model_expr.args[6]).system isa AMR.amr.ACSetSpec @test AMR.load(AMR.Typing, model_expr.args[6]).map isa Vector{AMR.amr.Pair} -# TODO: println(AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) -# TODO: @test_skip (AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) == modelrep +println(AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) +@test_skip (AMR.amr_to_string(AMR.load(ASKEModel, model_expr))) == modelrep