Skip to content

Commit 3b4ce9b

Browse files
quffaroKris Brown
authored andcommitted
basic tabular view functionality
CLEANUP: changing tests CLEANUP: reorganized code into parse and execute folders refactor algjuliainterop CORS and async issues resolved basic tabular view functionality edit remove decapodes material
1 parent 93e3389 commit 3b4ce9b

43 files changed

Lines changed: 848 additions & 3233 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,6 @@ result
105105

106106
# vm image
107107
catcolab-vm.qcow2
108+
109+
# VSCode
110+
.vscode/*
Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,28 @@
11
name = "CatColabInterop"
22
uuid = "9ecda8fb-39ab-46a2-a496-7285fa6368c1"
33
license = "MIT"
4-
authors = ["CatColab team"]
54
version = "0.1.1"
5+
authors = ["CatColab team"]
66

77
[deps]
8-
ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8"
9-
Catlab = "134e5e36-593f-5add-ad60-77f754baafbe"
10-
CombinatorialSpaces = "b1c52339-7909-45ad-8b6a-6e388f7c67f2"
11-
ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66"
12-
CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb"
13-
Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391"
14-
DiagrammaticEquations = "6f00c28b-6bed-4403-80fa-30e0dc12f317"
15-
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
16-
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
17-
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
18-
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
19-
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
8+
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
209
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
21-
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
22-
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
23-
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
10+
Oxygen = "df9a0d86-3283-4920-82dc-4555fc0d1d8b"
2411
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
25-
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
12+
StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
2613

2714
[weakdeps]
28-
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
15+
ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8"
16+
Catlab = "134e5e36-593f-5add-ad60-77f754baafbe"
2917

3018
[extensions]
31-
SysImageExt = "PackageCompiler"
19+
CatlabExt = ["Catlab", "ACSets"]
3220

3321
[compat]
34-
ACSets = "0.2.21"
35-
Catlab = "0.16.20"
36-
CombinatorialSpaces = "0.7.4"
37-
ComponentArrays = "0.15"
38-
CoordRefSystems = "0.18.9"
39-
Decapodes = "0.6"
40-
DiagrammaticEquations = "0.2"
41-
Distributions = "0.25"
42-
GeometryBasics = "0.5.7"
43-
IJulia = "1.26.0"
44-
JSON3 = "1"
45-
LinearAlgebra = "1"
46-
MLStyle = "0.4"
47-
OrdinaryDiffEq = "6.101.0"
48-
PackageCompiler = "2.2.1"
49-
Preferences = "1.5.0"
50-
REPL = "1.11.0"
22+
Catlab = "0.17.2"
23+
HTTP = "1.10.19"
24+
MLStyle = "0.4.17"
25+
Oxygen = "1.7.5"
5126
Reexport = "1.2.2"
52-
StaticArrays = "1"
27+
StructTypes = "1.11.0"
5328
julia = "1.11"
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
module CatlabExt
2+
3+
using ACSets
4+
using Catlab: Presentation, FreeSchema, Left
5+
import Catlab: id, dom
6+
using Catlab.CategoricalAlgebra.Pointwise.FunctorialDataMigrations.Yoneda:
7+
yoneda, colimit_representables, DiagramData
8+
using CatColabInterop, Oxygen, HTTP
9+
import CatColabInterop: endpoint
10+
11+
"""
12+
Take a parsed CatColab model of ThSchema and make a Catlab schema. Also
13+
collect the mapping from UUIDs to human-readable names.
14+
"""
15+
function model_to_schema(m::Model)::Tuple{Schema, Dict{String,Symbol}}
16+
obs, homs, attrtypes, attrs = Symbol[],[],[],[]
17+
names = Dict{String, Symbol}()
18+
19+
for stmt in m.obGenerators
20+
names[stmt.id] = Symbol(only(stmt.label))
21+
if stmt.obType.content == "Entity"
22+
push!(obs, names[stmt.id])
23+
elseif stmt.obType.content == "AttrType"
24+
push!(attrtypes, names[stmt.id])
25+
else
26+
error(stmt.obType)
27+
end
28+
end
29+
30+
for stmt in m.morGenerators
31+
h = (Symbol(only(stmt.label)), names[stmt.dom.content],
32+
names[stmt.cod.content])
33+
names[stmt.id] = h[1]
34+
if stmt.morType.content == "Attr"
35+
push!(attrs, h)
36+
else
37+
push!(homs, h)
38+
end
39+
end
40+
(Schema(Presentation(BasicSchema{Symbol}(obs, homs, attrtypes, attrs, []))),
41+
names)
42+
end
43+
44+
"""
45+
Take a CatColab diagram in a model of ThSchema and construct the input data
46+
that gets parsed normally from `@acset_colim`. Mutate an existing mapping of
47+
UUIDs to schema-level names to include UUID mappings for instance-level names.
48+
"""
49+
function diagram_to_data(d::Types.Diagram, names::Dict{String,Symbol}
50+
)::DiagramData
51+
data = DiagramData()
52+
for o in d.obGenerators
53+
names[o.id] = Symbol(only(o.label))
54+
push!(data.reprs[names[o.over.content]], names[o.id])
55+
end
56+
for m in d.morGenerators
57+
p1 = names[m.cod.content] => Symbol[]
58+
p2 = names[m.dom.content] => [names[m.over.content]]
59+
push!(data.eqs, p1 => p2)
60+
end
61+
data
62+
end
63+
64+
"""
65+
Receiver of the data already knows the schema, so the JSON payload to CatColab
66+
just includes the columns of data. Every part is named, so we use the names
67+
(including for primary key columns) rather than numeric indices.
68+
"""
69+
function acset_to_json(X::ACSet, S::Schema, names::Dict{Symbol, Vector{String}}
70+
)::AbstractDict
71+
Dict{Symbol, Vector{String}}(
72+
[t => names[t] for t in types(S)]
73+
[f => names[c][X[f]] for (f,_,c) in homs(S)]
74+
[f => names[c][getvalue.(X[f])] for (f,_,c) in attrs(S)] )
75+
end
76+
77+
"""
78+
Pick a human-readable name for all parts of the ACSet, given explicit names for
79+
some of the parts. There is some ambiguity here (the vertex of a generic
80+
reflexive edge `e` could be either `src(e)` or `tgt(e)`), but an arbitrary name
81+
is chosen after minimizing length (`src(e)` preferred over `src(refl(src(e)))`).
82+
"""
83+
function make_names(res::ACSet, names::NamedTuple
84+
)::Dict{Symbol, Vector{String}}
85+
S = acset_schema(res)
86+
function get_name(o::Symbol, i, curr=[])::Vector{Vector{Symbol}}
87+
V(x) = o in attrtypes(S) ? AttrVar(x) : x # embellish attrvars
88+
L(x) = o in attrtypes(S) ? Left(x) : x # embellish attrvars
89+
found = findfirst(==((o, L(i))), names) # if (o,i) is in names
90+
isnothing(found) || return [[found; curr]] # then just give the name
91+
inc = [(d, new_i, f) for (f, d, _) in arrows(S, to=o)
92+
for new_i in incident(res, V(i), f)]
93+
return vcat([get_name(d, new_i, [f; curr]) for (d, new_i, f) in inc]...)
94+
end
95+
return Dict{Symbol, Vector{String}}(map(types(acset_schema(res))) do o
96+
o => map(parts(res, o)) do i
97+
possible_names = sort(get_name(o, i); by=length)
98+
foldl((x,y)->"$y($x)", string.(first(possible_names)))
99+
end
100+
end)
101+
end
102+
103+
"""
104+
Top level function called by CatColab. Computes an ACSet colimit of a
105+
diagrammatic instance. Return a JSON tabular representation.
106+
"""
107+
function endpoint(::Val{:ACSetColim})
108+
@post "/acsetcolim" function(req::HTTP.Request)
109+
payload = json(req, ModelDiagram)
110+
schema, names = model_to_schema(payload.model)
111+
data = diagram_to_data(payload.diagram, names)
112+
acset_type = AnonACSet(
113+
schema; type_assignment=Dict(a=>Nothing for a in schema.attrtypes))
114+
y = yoneda(constructor(acset_type))
115+
names, res = colimit_representables(data, y)
116+
acset_to_json(res, schema, make_names(res, names))
117+
end
118+
end
119+
120+
end # module

packages/algjulia-interop/ext/SysImageExt.jl

Lines changed: 0 additions & 24 deletions
This file was deleted.

packages/algjulia-interop/make_sysimage.jl

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
# Example usage:
3+
4+
# julia --project=my_alg_julia_env --threads 4 endpoint.jl Catlab AlgebraicPetri
5+
6+
# Where my_alg_julia_env is a Julia environment with CatColabInterop, Oxygen,
7+
# HTTP, and any AlgJulia dependencies.
8+
9+
using CatColabInterop
10+
using Oxygen
11+
using HTTP
12+
13+
const CORS_HEADERS = [
14+
"Access-Control-Allow-Origin" => "*",
15+
"Access-Control-Allow-Headers" => "*",
16+
"Access-Control-Allow-Methods" => "POST, GET, OPTIONS"
17+
]
18+
19+
function CorsHandler(handle)
20+
return function (req::HTTP.Request)
21+
# return headers on OPTIONS request
22+
if HTTP.method(req) == "OPTIONS"
23+
return HTTP.Response(200, CORS_HEADERS)
24+
else
25+
r = handle(req)
26+
append!(r.headers, ["Access-Control-Allow-Origin" => "*"])
27+
r
28+
29+
end
30+
end
31+
end
32+
33+
defaults = [:Catlab,:ACSets] # all extensions to date
34+
35+
# Dynamically load packages in command lin eargs
36+
for pkg in (isempty(ARGS) ? defaults : ARGS )
37+
@info "using $pkg"
38+
@eval using $pkg
39+
end
40+
41+
for m in methods(CatColabInterop.endpoint)
42+
sig = m.sig.parameters
43+
(length(sig)==2 && sig[2].instance isa Val) || error("Unexpected signature $sig")
44+
name = only(sig[2].parameters)
45+
@info "Loading endpoint $name"
46+
name isa Symbol || error("Unexpected endpoint name $name")
47+
fntype, argtypes... = m.sig.types
48+
invoke(fntype.instance, Tuple{argtypes...}, Val(name))
49+
end
50+
51+
serve(middleware=[CorsHandler])
Lines changed: 7 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,15 @@
11
module CatColabInterop
22

3-
using MLStyle
4-
using Reexport
5-
6-
# this code tracks integrations and allows for basic theory/model-building code to dispatch from it.
7-
# the intent is that this is an interface for AlgebraicJulia code to interoperate with CatColab
8-
abstract type AlgebraicJuliaIntegration end
9-
10-
# cells in the JSON are tagged. these are just objects for dispatching `to_model`
11-
@data ModelElementTag begin
12-
ObTag()
13-
HomTag()
14-
end
15-
export ObTag, HomTag
16-
17-
#=
18-
@active patterns are MLStyle-implementations of F# active patterns that forces us to work in the Maybe/Option pattern.
19-
Practically, yet while a matter of opinion, they make @match statements cleaner; a statement amounts to a helpful pattern
20-
name and the variables we intend to capture.
21-
=#
22-
@active IsObject(x) begin; x[:content][:tag] == "object" ? Some(x[:content]) : nothing end
23-
@active IsMorphism(x) begin; x[:content][:tag] == "morphism" ? Some(x[:content]) : nothing end
24-
export IsObject, IsMorphism
25-
26-
# Obs, Homs
27-
@data ModelElementValue begin
28-
ObValue()
29-
HomValue(dom,cod)
30-
end
31-
export ObValue, HomValue
32-
33-
"""
34-
Struct capturing the name of the object and its relevant information.
35-
ModelElementValue may be objects or homs, each of which has different data.
36-
"""
37-
struct ModelElement
38-
name::Union{Symbol, Nothing}
39-
val::Union{<:ModelElementValue, Nothing}
40-
function ModelElement(;name::Symbol=nothing,val::Any=nothing)
41-
new(name, val)
42-
end
43-
end
44-
export ModelElement
45-
46-
Base.nameof(t::ModelElement) = t.name
3+
export endpoint
474

48-
""" Struct wrapping a dictionary """
49-
struct Model{T<:AlgebraicJuliaIntegration}
50-
data::Dict{String, ModelElement}
51-
end
52-
export Model
53-
54-
function Model(::T) where T<:AlgebraicJuliaIntegration
55-
Model{T}(Dict{String, ModelElement}())
56-
end
57-
58-
Base.values(model::Model) = values(model.data)
5+
using Reexport
596

607
"""
61-
Functions to build a dictionary associating ids in the theory to elements in the model
8+
Extend this method with endpoint(::Val{my_analysis_name}) in extension packages.
629
"""
63-
function to_model end
64-
export to_model
65-
66-
67-
# TODO supposes bijection between theories, models, diagrams, etc.
68-
abstract type AbstractDiagram{T<:AlgebraicJuliaIntegration} end
69-
70-
abstract type AbstractAnalysis{T<:AlgebraicJuliaIntegration} end
71-
72-
struct ImplError <: Exception
73-
name::String
74-
end
75-
export ImplError
76-
77-
Base.showerror(io::IO, e::ImplError) = print(io, "$(e.name) not implemented")
78-
79-
include("result.jl")
80-
include("kernel_management.jl")
81-
include("kernel_support.jl")
82-
include("decapodes-service/DecapodesService.jl")
10+
function endpoint end
8311

84-
@reexport using .DecapodesService
12+
include("Types.jl")
13+
@reexport using .Types
8514

86-
end
15+
end # module

0 commit comments

Comments
 (0)