Browse Source

Merge branch 'master' into serialization

pull/372/head
James 1 week ago
committed by GitHub
parent
commit
8a3c84b863
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 696 additions and 526 deletions
  1. +2
    -2
      Project.toml
  2. +13
    -0
      docs/literate/graphics/graphviz_schema_visualization.jl
  3. +1
    -0
      docs/make.jl
  4. +0
    -1
      docs/src/apis/graphs.md
  5. +2
    -2
      src/categorical_algebra/ACSetViews.jl
  6. +55
    -53
      src/categorical_algebra/CSetDataStructures.jl
  7. +20
    -19
      src/categorical_algebra/CSets.jl
  8. +11
    -11
      src/categorical_algebra/StructuredCospans.jl
  9. +5
    -1
      src/core/Present.jl
  10. +50
    -4
      src/graphics/GraphvizGraphs.jl
  11. +15
    -12
      src/graphics/GraphvizWiringDiagrams.jl
  12. +5
    -4
      src/graphics/WiringDiagramLayouts.jl
  13. +3
    -3
      src/graphics/YFilesWiringDiagrams.jl
  14. +0
    -126
      src/graphs/EmbeddedGraphs.jl
  15. +0
    -1
      src/graphs/Graphs.jl
  16. +2
    -2
      src/programs/ParseJuliaPrograms.jl
  17. +12
    -23
      src/theories/Schema.jl
  18. +9
    -9
      src/wiring_diagrams/Algorithms.jl
  19. +396
    -143
      src/wiring_diagrams/Directed.jl
  20. +27
    -16
      src/wiring_diagrams/Expressions.jl
  21. +4
    -3
      src/wiring_diagrams/GraphML.jl
  22. +3
    -3
      src/wiring_diagrams/JSON.jl
  23. +24
    -17
      src/wiring_diagrams/MonoidalDirected.jl
  24. +0
    -61
      test/graphs/EmbeddedGraphs.jl
  25. +0
    -4
      test/graphs/Graphs.jl
  26. +37
    -6
      test/wiring_diagrams/Directed.jl

+ 2
- 2
Project.toml View File

@ -2,7 +2,7 @@ name = "Catlab"
uuid = "134e5e36-593f-5add-ad60-77f754baafbe"
license = "MIT"
authors = ["Evan Patterson <evan@epatters.org>"]
version = "0.10.1"
version = "0.11.1"
[deps]
AutoHashEquals = "15f4f7f2-30c1-5605-9d31-71845cf9641f"
@ -39,7 +39,7 @@ JSON = "0.20, 0.21"
LightXML = "0.8, 0.9"
MLStyle = "0.4"
Parameters = "0.11, 0.12"
PrettyTables = "0.10"
PrettyTables = "0.10, 0.11"
Reexport = "0.2, 1.0"
Requires = "^1"
StaticArrays = "0.12, 1.0"


+ 13
- 0
docs/literate/graphics/graphviz_schema_visualization.jl View File

@ -0,0 +1,13 @@
# # Visualizing Acset Schemas with Graphviz
#
#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/generated/graphics/graphviz_wiring_diagrams.ipynb)
#
# It is convenient to visualize schemas in a non-textual way; often schemas can have many objects, homs and attributes, and it is hard to keep these all in your head.
#
# For this reason, we provide a function which takes a schema and produces a visual representation of it, using the Catlab Graphviz interface.
#
# This is as simple as the following code. These figures are by no means publication-quality, but they can be useful for interactive development.
using Catlab.Present, Catlab.Graphics, Catlab.Graphs
to_graphviz(Graphs.BasicGraphs.TheoryWeightedGraph)

+ 1
- 0
docs/make.jl View File

@ -58,6 +58,7 @@ makedocs(
"generated/graphics/graphviz_wiring_diagrams.md",
"generated/graphics/tikz_wiring_diagrams.md",
"generated/graphics/layouts_vs_drawings.md",
"generated/graphics/graphviz_schema_visualization.md",
],
],
"Modules" => Any[


+ 0
- 1
docs/src/apis/graphs.md View File

@ -3,7 +3,6 @@
```@autodocs
Modules = [
Graphs.BasicGraphs,
Graphs.EmbeddedGraphs,
Graphs.PropertyGraphs,
Graphs.GraphAlgorithms,
]


+ 2
- 2
src/categorical_algebra/ACSetViews.jl View File

@ -7,7 +7,7 @@ export ACSetView, backing, @compute_prop, @select_where,
using ...CSetDataStructures
using ..CSets
using ..FinSets
using ...Theories: CatDesc, AttrDesc, dom, codom_num
using ...Theories: CatDesc, AttrDesc, dom, codom_num, attr
using MLStyle: @match
using PrettyTables: pretty_table
@ -28,7 +28,7 @@ struct ACSetView{A <: ACSet, P, Attrs <: NamedTuple} <: AbstractArray{Attrs,1}
parts :: AbstractVector{Int}
function ACSetView(backing::A, P::Symbol, parts::AbstractVector{Int}) where
{CD <: CatDesc,AD <: AttrDesc{CD},Ts <: Tuple, A <: ACSet{CD,AD,Ts}}
attr_names = filter(a -> dom(AD,a) == P, AD.attr)
attr_names = filter(a -> dom(AD,a) == P, attr(AD))
attr_types = map(a -> Ts.parameters[codom_num(AD,a)], attr_names)
row_type = NamedTuple{attr_names, Tuple{attr_types...}}
new{A,P,row_type}(backing,parts)


+ 55
- 53
src/categorical_algebra/CSetDataStructures.jl View File

@ -11,12 +11,15 @@ using Compat: isnothing, only
using MLStyle: @match
using PrettyTables: pretty_table
using StaticArrays: StaticArray
import Tables, TypedTables
using ...Meta, ...Present
using ...Syntax: GATExpr, args
using ...Theories: Schema, FreeSchema, dom, codom, codom_num, data_num, attrs_by_codom,
CatDesc, CatDescType, AttrDesc, AttrDescType, SchemaType
using ...Theories: Schema, FreeSchema, SchemaType,
CatDesc, CatDescType, ob, hom, dom, codom, codom_num,
AttrDesc, AttrDescType, data, attr, adom, acodom, data_num, attrs_by_codom
# Data types
############
@ -136,16 +139,16 @@ end
@generated function ACSetTableUnionAll(::Type{X}, ::Type{Val{ob₀}}) where
{CD<:CatDesc, AD<:AttrDesc{CD}, X<:AbstractACSet{CD,AD}, ob₀}
CD₀, AD₀ = ACSetTableDesc(CD, AD, ob₀)
:(ACSet{$CD₀,$AD₀,Tuple{$(AD.data...)},(),()} where {$(AD.data...)})
:(ACSet{$CD₀,$AD₀,Tuple{$(data(AD)...)},(),()} where {$(data(AD)...)})
end
function ACSetTableDesc(::Type{CD}, ::Type{AD}, ob₀::Symbol) where
{CD<:CatDesc, AD<:AttrDesc{CD}}
@assert ob₀ CD.ob
attrs₀ = [ i for (i,j) in enumerate(AD.adom) if CD.ob[j] == ob₀ ]
adom = Tuple(ones(Int, length(attrs₀)))
@assert ob₀ ob(CD)
attrs₀ = [ i for (i,j) in enumerate(adom(AD)) if ob(CD)[j] == ob₀ ]
adom = Tuple(ones(Int, length(attrs₀)))
CD₀ = CatDesc{(ob₀,),(),(),()}
AD₀ = AttrDesc{CD₀,AD.data,AD.attr[attrs₀],adom,AD.acodom[attrs₀]}
AD₀ = AttrDesc{CD₀,data(AD),attr(AD)[attrs₀],adom,acodom(AD)[attrs₀]}
(CD₀, AD₀)
end
@ -191,9 +194,9 @@ function make_indices(::Type{CD}, AD::Type{<:AttrDesc{CD}},
# TODO: Could be `@generated` for faster initialization of C-sets.
NamedTuple{Idxed}(Tuple(map(Idxed) do name
IndexType = name UniqueIdxed ? Int : Vector{Int}
if name CD.hom
if name hom(CD)
Vector{IndexType}()
elseif name AD.attr
elseif name attr(AD)
Dict{Ts.parameters[codom_num(AD,name)],IndexType}()
else
error("Cannot index $name: not a morphism or an attribute")
@ -204,12 +207,12 @@ end
function make_tables(Table::Type, ::Type{CD}, AD::Type{<:AttrDesc{CD}},
Ts::Type{<:Tuple}) where {CD}
# TODO: Could be `@generated` for faster initialization of C-sets.
cols = NamedTuple{CD.ob}((names=Symbol[], types=Type[]) for ob in CD.ob)
for hom in CD.hom
cols = NamedTuple{ob(CD)}((names=Symbol[], types=Type[]) for _ in ob(CD))
for hom in hom(CD)
col = cols[dom(CD,hom)]
push!(col.names, hom); push!(col.types, Int)
end
for attr in AD.attr
for attr in attr(AD)
col = cols[dom(AD,attr)]
push!(col.names, attr); push!(col.types, Ts.parameters[codom_num(AD,attr)])
end
@ -244,12 +247,12 @@ function Base.show(io::IO, acs::T) where {CD,AD,Ts,T<:AbstractACSet{CD,AD,Ts}}
print(io, T <: AbstractCSet ? "CSet" : "ACSet")
println(io, "(")
join(io, vcat(
[ " $ob = 1:$(nparts(acs,ob))" for ob in CD.ob ],
[ " $data = $(Ts.parameters[i])" for (i,data) in enumerate(AD.data) ],
[ " $ob = 1:$(nparts(acs,ob))" for ob in ob(CD) ],
[ " $data = $(Ts.parameters[i])" for (i, data) in enumerate(data(AD)) ],
[ " $hom : $(dom(CD,i)) $(codom(CD,i)) = $(subpart(acs,hom))"
for (i,hom) in enumerate(CD.hom) ],
for (i, hom) in enumerate(hom(CD)) ],
[ " $attr : $(dom(AD,i)) $(codom(AD,i)) = $(subpart(acs,attr))"
for (i,attr) in enumerate(AD.attr) ],
for (i, attr) in enumerate(attr(AD)) ],
), ",\n")
print(io, ")")
end
@ -315,7 +318,7 @@ has_part(acs::ACSet, type::Symbol) = _has_part(acs, Val{type})
@generated function _has_part(::ACSet{CD,AD}, ::Type{Val{type}}) where
{CD,AD,type}
type CD.ob || type AD.data
type ob(CD) || type data(AD)
end
has_part(acs::ACSet, type::Symbol, part::Int) = 1 <= part <= nparts(acs, type)
@ -328,7 +331,7 @@ has_subpart(acs::ACSet, name::Symbol) = _has_subpart(acs, Val{name})
@generated function _has_subpart(::ACSet{CD,AD}, ::Type{Val{name}}) where
{CD,AD,name}
name CD.hom || name AD.attr
name hom(CD) || name attr(AD)
end
""" Get subpart of part in C-set.
@ -354,7 +357,7 @@ convention differs from DataFrames but note that the alternative interpretation
of `[:src,:vattr]` as two independent columns does not even make sense, since
they have different domains (belong to different tables).
"""
@inline subpart(acs::ACSet, part, name) = view_slice(subpart(acs, name), part)
@inline subpart(acs::ACSet, part, name) = view_or_slice(subpart(acs, name), part)
@inline subpart(acs::ACSet, name::Symbol) = _subpart(acs, Val{name})
# These accessors must be inlined to ensure that constant names are propagated
# at compile time, e.g., `subpart(g, :src)` becomes `_subpart(g, Val{:src})`.
@ -381,19 +384,19 @@ subpart_names(expr::GATExpr{:compose}) =
@generated function _subpart(acs::ACSet{CD,AD,Ts}, ::Type{Val{name}}) where
{CD,AD,Ts,name}
if name CD.hom
if name hom(CD)
:(acs.tables.$(dom(CD,name)).$name)
elseif name AD.attr
elseif name attr(AD)
:(acs.tables.$(dom(AD,name)).$name)
else
throw(ArgumentError("$(repr(name)) not in $(CD.hom) or $(AD.attr)"))
throw(ArgumentError("$(repr(name)) not in $(hom(CD)) or $(attr(AD))"))
end
end
@inline Base.getindex(acs::ACSet, args...) = subpart(acs, args...)
@inline view_slice(x::AbstractVector, i) = view(x, i)
@inline view_slice(x::AbstractVector, i::Int) = x[i]
@inline view_or_slice(x::AbstractVector, i) = view(x, i)
@inline view_or_slice(x::AbstractVector, i::Union{Integer,StaticArray}) = x[i]
""" Get superparts incident to part in C-set.
@ -429,16 +432,16 @@ incident(acs::ACSet, part, expr::GATExpr; kw...) =
@generated function _incident(acs::ACSet{CD,AD,Ts,Idxed}, part,
::Type{Val{name}}; copy::Bool=false) where {CD,AD,Ts,Idxed,name}
if name CD.hom
if name hom(CD)
if name Idxed
quote
indices = view_slice(acs.indices.$name, part)
indices = view_or_slice(acs.indices.$name, part)
copy ? Base.copy.(indices) : indices
end
else
:(broadcast_findall(part, acs.tables.$(dom(CD,name)).$name))
end
elseif name AD.attr
elseif name attr(AD)
if name Idxed
quote
indices = get_data_index.(Ref(acs.indices.$name), part)
@ -448,7 +451,7 @@ incident(acs::ACSet, part, expr::GATExpr; kw...) =
:(broadcast_findall(part, acs.tables.$(dom(AD,name)).$name))
end
else
throw(ArgumentError("$(repr(name)) not in $(CD.hom) or $(AD.attr)"))
throw(ArgumentError("$(repr(name)) not in $(hom(CD)) or $(attr(AD))"))
end
end
@ -491,8 +494,8 @@ end
@generated function _add_parts!(acs::ACSet{CD,AD,Ts,Idxed}, ::Type{Val{ob}},
n::Int) where {CD,AD,Ts,Idxed,ob}
out_homs = filter(hom -> dom(CD, hom) == ob, CD.hom)
indexed_in_homs = filter(hom -> codom(CD, hom) == ob && hom Idxed, CD.hom)
out_homs = filter(hom -> dom(CD, hom) == ob, hom(CD))
indexed_in_homs = filter(hom -> codom(CD, hom) == ob && hom Idxed, hom(CD))
quote
if n == 0; return 1:0 end
nparts = Tables.rowcount(acs.tables.$ob) + n
@ -540,7 +543,7 @@ _set_subpart!(acs::ACSet, ::Type{Name}, subpart) where Name =
@generated function _set_subpart!(acs::ACSet{CD,AD,Ts,Idxed}, part::Int,
::Type{Val{name}}, subpart) where {CD,AD,Ts,Idxed,name}
if name CD.hom
if name hom(CD)
ob, codom_ob = dom(CD, name), codom(CD, name)
if name Idxed
quote
@ -560,7 +563,7 @@ _set_subpart!(acs::ACSet, ::Type{Name}, subpart) where Name =
acs.tables.$ob.$name[part] = subpart
end
end
elseif name AD.attr
elseif name attr(AD)
ob = dom(AD, name)
if name Idxed
quote
@ -575,7 +578,7 @@ _set_subpart!(acs::ACSet, ::Type{Name}, subpart) where Name =
:(acs.tables.$ob.$name[part] = subpart)
end
else
throw(ArgumentError("$(repr(name)) not in $(CD.hom) or $(AD.attr)"))
throw(ArgumentError("$(repr(name)) not in $(hom(CD)) or $(attr(AD))"))
end
end
@ -627,9 +630,9 @@ rem_part!(acs::ACSet, type::Symbol, part::Int) =
@generated function _rem_part!(acs::ACSet{CD,AD,Ts,Idxed}, ::Type{Val{ob}},
part::Int) where {CD,AD,Ts,Idxed,ob}
in_homs = filter(hom -> codom(CD, hom) == ob, CD.hom)
indexed_out_homs = filter(hom -> dom(CD, hom) == ob && hom Idxed, CD.hom)
indexed_attrs = filter(attr -> dom(AD, attr) == ob && attr Idxed, AD.attr)
in_homs = filter(hom -> codom(CD, hom) == ob, hom(CD))
indexed_out_homs = filter(hom -> dom(CD, hom) == ob && hom Idxed, hom(CD))
indexed_attrs = filter(attr -> dom(AD, attr) == ob && attr Idxed, attr(AD))
quote
last_part = Tables.rowcount(acs.tables.$ob)
@assert 1 <= part <= last_part
@ -691,7 +694,7 @@ will have undefined subparts.
"""
@generated function copy_parts!(to::ACSet{CD},
from::ACSet{CD′}; kw...) where {CD, CD′}
obs = intersect(CD.ob, CD′.ob)
obs = intersect(ob(CD), ob(CD′))
:(copy_parts!(to, from, isempty(kw) ? $(Tuple(obs)) : (; kw...)))
end
@ -702,8 +705,8 @@ copy_parts!(to::ACSet, from::ACSet, parts::NamedTuple) =
@generated function _copy_parts!(to::ACSet{CD}, from::ACSet{CD′},
parts::NamedTuple{obs}) where {CD, CD′, obs}
@assert obs intersect(CD.ob, CD′.ob)
homs = intersect(CD.hom, CD′.hom)
@assert obs intersect(ob(CD), ob(CD′))
homs = intersect(hom(CD), hom(CD′))
homs = filter(homs) do hom
c, c′, d, d′ = dom(CD,hom), dom(CD′,hom), codom(CD,hom), codom(CD′,hom)
c == c′ && d == d′ && c obs && d obs
@ -737,7 +740,7 @@ See also: [`copy_parts!`](@ref).
"""
@generated function copy_parts_only!(to::ACSet{CD},
from::ACSet{CD′}; kw...) where {CD, CD′}
obs = intersect(CD.ob, CD′.ob)
obs = intersect(ob(CD), ob(CD′))
:(copy_parts_only!(to, from, isempty(kw) ? $(Tuple(obs)) : (; kw...)))
end
@ -748,8 +751,8 @@ copy_parts_only!(to::ACSet, from::ACSet, parts::NamedTuple) =
@generated function _copy_parts_only!(to::ACSet{CD,AD}, from::ACSet{CD′,AD′},
parts::NamedTuple{obs}) where {CD, AD, CD′, AD′, obs}
@assert obs intersect(CD.ob, CD′.ob)
attrs = intersect(AD.attr, AD′.attr)
@assert obs intersect(ob(CD), ob(CD′))
attrs = intersect(attr(AD), attr(AD′))
attrs = filter(attrs) do attr
ob, ob′ = dom(AD, attr), dom(AD′, attr)
ob == ob′ && ob obs
@ -866,9 +869,9 @@ function init_acset(T::Type{<:ACSet{CD,AD,Ts}},body) where {CD <: CatDesc, AD <:
Expr(:(=), lhs, rhs) => (lhs,rhs)
_ => error("Every line of `@acset` must be an assignment")
end
if lhs in CD.ob
if lhs in ob(CD)
push!(code.args, :(add_parts!(acs, $(Expr(:quote, lhs)), $(rhs))))
elseif lhs in CD.hom || lhs in AD.attr
elseif lhs in hom(CD) || lhs in attr(AD)
push!(code.args, :(set_subpart!(acs, :, $(Expr(:quote, lhs)), $(rhs))))
end
end
@ -891,13 +894,12 @@ function sortunique!(x)
end
@generated function _map(acs::AT, fns::NamedTuple{map_over}) where
{map_over,AT<:ACSet}
CD,AD,Ts,Idxed,UniqIdxed = AT.parameters[1:5]
{map_over, CD,AD,Ts,Idxed,UniqIdxed, AT<:ACSet{CD,AD,Ts,Idxed,UniqIdxed}}
map_over = [map_over...]
attrs = filter(x -> x AD.attr, map_over)
data = filter(x -> x AD.data, map_over)
attrs = filter(x -> x attr(AD), map_over)
data_names = filter(x -> x data(AD), map_over)
abc = attrs_by_codom(AD)
data_attrs = vcat(map(d -> abc[d], data)...)
data_attrs = vcat(map(d -> abc[d], data_names)...)
all_attrs = sortunique!(Symbol[attrs; data_attrs])
affected_data = sortunique!(map(a -> codom(AD,a), all_attrs))
needed_attrs = sortunique!(vcat(map(d -> abc[d], affected_data)...))
@ -912,7 +914,7 @@ end
:($a = (fns[$(Expr(:quote,d))]).(subpart(acs, $qa)))
end
end
data_types = map(enumerate(AD.data)) do (i,d)
data_types = map(enumerate(data(AD))) do (i,d)
if d affected_data
quote
T = eltype(fn_vals[$(Expr(:quote, abc[d][1]))])
@ -929,13 +931,13 @@ end
fn_vals = $(Expr(:tuple, fn_applications...))
new_Ts = Tuple{$(data_types...)}
new_acs = ACSet{$CD,$AD,new_Ts,$Idxed,$UniqIdxed}()
$(Expr(:block, map(CD.ob) do ob
$(Expr(:block, map(ob(CD)) do ob
:(add_parts!(new_acs,$(Expr(:quote,ob)),nparts(acs,$(Expr(:quote,ob)))))
end...))
$(Expr(:block, map(CD.hom) do hom
$(Expr(:block, map(hom(CD)) do hom
:(set_subpart!(new_acs,$(Expr(:quote,hom)),subpart(acs,$(Expr(:quote,hom)))))
end...))
$(Expr(:block, map(AD.attr) do attr
$(Expr(:block, map(attr(AD)) do attr
qa = Expr(:quote, attr)
if attr all_attrs
:(set_subpart!(new_acs,$qa,fn_vals[$qa]))


+ 20
- 19
src/categorical_algebra/CSets.jl View File

@ -15,7 +15,7 @@ using StaticArrays: SVector
using ...GAT, ..FreeDiagrams, ..Limits, ..Sets, ..FinSets
import ..Limits: limit, colimit, universal
import ..FinSets: FinSet, FinFunction, FinDomFunction, force
using ...Theories: Category, CatDesc, AttrDesc
using ...Theories: Category, CatDesc, AttrDesc, ob, hom, attr, adom, acodom
import ...Theories: dom, codom, compose, , id
# FinSets interop
@ -33,18 +33,18 @@ FinFunction(X::ACSet, name::Symbol) = fin_function(X, Val{name})
@generated function fin_function(X::ACSet{CD,AD,Ts,Idxed},
::Type{Val{name}}) where {CD,AD,Ts,Idxed,name}
if name CD.ob
if name ob(CD)
quote
FinFunction(identity, FinSet(X, $(QuoteNode(name))))
end
elseif name CD.hom
elseif name hom(CD)
quote
FinFunction(subpart(X, $(QuoteNode(name))),
FinSet(X, $(QuoteNode(codom(CD, name)))),
index=$(name Idxed ? :(X.indices.$name) : false))
end
else
throw(ArgumentError("$(repr(name)) not in $(CD.ob) or $(CD.hom)"))
throw(ArgumentError("$(repr(name)) not in $(ob(CD)) or $(hom(CD))"))
end
end
@ -57,18 +57,19 @@ FinDomFunction(X::ACSet, name::Symbol) = fin_dom_function(X, Val{name})
@generated function fin_dom_function(X::ACSet{CD,AD,Ts,Idxed},
::Type{Val{name}}) where {CD,AD,Ts,Idxed,name}
if name CD.ob
if name ob(CD)
quote
n = nparts(X, $(QuoteNode(name)))
FinDomFunction(1:n, FinSet(n), TypeSet{Int}())
end
elseif name CD.hom || name AD.attr
elseif name hom(CD) || name attr(AD)
quote
FinDomFunction(subpart(X, $(QuoteNode(name))),
index=$(name Idxed ? :(X.indices.$name) : false))
end
else
throw(ArgumentError("$(repr(name)) not in $(CD.ob), $(CD.hom), or $(AD.attr)"))
throw(ArgumentError(
"$(repr(name)) not in $(ob(CD)), $(hom(CD)), or $(attr(AD))"))
end
end
@ -135,11 +136,11 @@ check the naturality equation on a generating set of morphisms.
"""
function is_natural(α::ACSetTransformation{CD,AD}) where {CD,AD}
X, Y = dom(α), codom(α)
for (f, c, d) in zip(CD.hom, CD.dom, CD.codom)
for (f, c, d) in zip(hom(CD), dom(CD), codom(CD))
Xf, Yf, α_c, α_d = subpart(X,f), subpart(Y,f), α[c], α[d]
all(Yf[α_c(i)] == α_d(Xf[i]) for i in eachindex(Xf)) || return false
end
for (f, c) in zip(AD.attr, AD.adom)
for (f, c) in zip(attr(AD), adom(AD))
Xf, Yf, α_c = subpart(X,f), subpart(Y,f), α[c]
all(Yf[α_c(i)] == Xf[i] for i in eachindex(Xf)) || return false
end
@ -199,7 +200,7 @@ function limit(diagram::AbstractFreeDiagram{ACS}) where
for (c, lim) in pairs(limits)
add_parts!(Y, c, length(ob(lim)))
end
for (f, c, d) in zip(CD.hom, CD.dom, CD.codom)
for (f, c, d) in zip(hom(CD), dom(CD), codom(CD))
Yfs = map(legs(limits[c]), Xs) do π, X
compose(π, FinFunction(subpart(X, f), nparts(X, d)))
end
@ -224,7 +225,7 @@ function colimit(diagram::AbstractFreeDiagram{ACS}) where
for (c, colim) in pairs(colimits)
add_parts!(Y, c, length(ob(colim)))
end
for (f, c, d) in zip(CD.hom, CD.dom, CD.codom)
for (f, c, d) in zip(hom(CD), dom(CD), codom(CD))
Yfs = map(legs(colimits[d]), Xs) do ι, X
compose(FinFunction(subpart(X, f), nparts(X, d)), ι)
end
@ -234,7 +235,7 @@ function colimit(diagram::AbstractFreeDiagram{ACS}) where
ιs = pack_components(map(legs, colimits), Xs, map(X -> Y, Xs))
# Set data attributes by canonical inclusion from attributes in diagram.
for (attr, c, d) in zip(AD.attr, AD.adom, AD.acodom)
for (attr, c, d) in zip(attr(AD), adom(AD), acodom(AD))
T = Ts.parameters[d]
data = Vector{Union{Some{T},Nothing}}(nothing, nparts(Y, c))
for (ι, X) in zip(ιs, Xs)
@ -329,18 +330,18 @@ When the functor is the identity, this function is equivalent to
"""
function migrate!(Y::ACSet{CD, AD}, X::ACSet,
FOb::AbstractDict, FHom::AbstractDict) where {CD, AD}
CD.ob keys(FOb) || error("Every object in $CD must be a key in $FOb")
CD.hom keys(FHom) || error("Every hom in $CD must be a key in $FHom")
AD.attr keys(FHom) || error("Every attribute in $AD must be a key in $FHom")
partsY = NamedTuple{CD.ob}(map(CD.ob) do obY
ob(CD) keys(FOb) || error("Every object in $CD must be a key in $FOb")
hom(CD) keys(FHom) || error("Every morphism in $CD must be a key in $FHom")
attr(AD) keys(FHom) || error("Every attribute in $AD must be a key in $FHom")
partsY = NamedTuple{ob(CD)}(map(ob(CD)) do obY
add_parts!(Y, obY, nparts(X, FOb[obY]))
end)
for homY in CD.hom
for homY in hom(CD)
domY, codomY = dom(CD, homY), codom(CD, homY)
set_subpart!(Y, partsY[domY], homY, partsY[codomY][subpart(X, FHom[homY])])
end
for attrY in AD.attr
for attrY in attr(AD)
domY = dom(AD, attrY)
set_subpart!(Y, partsY[domY], attrY, subpart(X, FHom[attrY]))
end


+ 11
- 11
src/categorical_algebra/StructuredCospans.jl View File

@ -15,7 +15,7 @@ using StaticArrays: StaticVector, SVector
using ...GAT, ..FreeDiagrams, ..Limits, ..FinSets, ..CSets
import ..FreeDiagrams: apex, legs, feet, left, right, bundle_legs
import ..CSets: force
using ...Theories: Category, CatDesc, AttrDesc
using ...Theories: Category, CatDesc, AttrDesc, data, attr, adom, acodom
import ...Theories: dom, codom, compose, , id, otimes, , munit, braid, σ,
mcopy, Δ, mmerge, , delete, , create, , dunit, dcounit, dagger
@ -191,7 +191,7 @@ See also: [`OpenACSetTypes`](@ref).
"""
function OpenCSetTypes(::Type{X}, ob₀::Symbol) where
{CD<:CatDesc, X<:AbstractCSet{CD}}
@assert ob₀ CD.ob
@assert ob₀ ob(CD)
L = FinSetDiscreteACSet{ob₀, X}
(StructuredCospanOb{L}, StructuredMulticospan{L})
end
@ -205,9 +205,9 @@ See also: [`OpenCSetTypes`](@ref).
"""
function OpenACSetTypes(::Type{X}, ob₀::Symbol) where
{CD<:CatDesc, AD<:AttrDesc{CD}, X<:AbstractACSet{CD,AD}}
@assert ob₀ CD.ob
type_vars = map(TypeVar, AD.data)
L = if any(CD.ob[j] == ob₀ for (i,j) in enumerate(AD.adom))
@assert ob₀ ob(CD)
type_vars = map(TypeVar, data(AD))
L = if any(ob(CD)[j] == ob₀ for (i,j) in enumerate(adom(AD)))
A = ACSetTableType(X, ob₀, union_all=true)
DiscreteACSet{A{type_vars...}, X{type_vars...}}
else
@ -271,7 +271,7 @@ end
function StructuredCospanOb{L}(set::FinSet{Int}; kw...) where
{CD, A <: AbstractACSet{CD}, L <: DiscreteACSet{A}}
a = A()
add_parts!(a, only(CD.ob), length(set); kw...)
add_parts!(a, only(ob(CD)), length(set); kw...)
StructuredCospanOb{L}(a)
end
@ -279,15 +279,15 @@ end
"""
function induced_transformation(a::A, f::FinFunction{Int,Int}) where
{CD, AD, A <: AbstractACSet{CD,AD}}
ob = only(CD.ob)
@assert nparts(a, ob) == length(codom(f))
ob = only(ob(CD))
@assert nparts(a, ob) == length(codom(f))
b = A()
add_parts!(b, ob, length(dom(f)))
add_parts!(b, ob, length(dom(f)))
f_vec = collect(f)
for attr in AD.attr
for attr in attr(AD)
set_subpart!(b, attr, subpart(a, f_vec, attr))
end
ACSetTransformation((; ob => f), b, a)
ACSetTransformation((; ob => f), b, a)
end
""" Apply left adjoint L: FinSet C-Set to object.


+ 5
- 1
src/core/Present.jl View File

@ -9,7 +9,7 @@ in applications like knowledge representation.
"""
module Present
export @present, Presentation, generator, generators, has_generator, equations,
add_generator!, add_generators!, add_definition!, add_equation!
add_generator!, add_generators!, add_definition!, add_equation!, generator_index
using Base.Meta: ParseError
using Compat
@ -117,6 +117,10 @@ function add_definition!(pres::Presentation, name::Symbol, rhs::GATExpr)
generator
end
""" Get the index of a generator
"""
generator_index(pres::Presentation, x::Symbol) = pres.generator_name_index[x].second
# Presentation macro
####################


+ 50
- 4
src/graphics/GraphvizGraphs.jl View File

@ -7,6 +7,8 @@ using Compat: isnothing
using StaticArrays: StaticVector, SVector
using ...Graphs
using ...Present
using ...Theories
import ..Graphviz
# Property graphs
@ -38,10 +40,8 @@ function parse_graphviz(doc::AbstractDict)::AbstractPropertyGraph
props = Dict{Symbol,Any}(
Symbol(k) => node[k] for k in node_keys if haskey(node, k))
props[:position] = parse_point(node["pos"])
props[:size] = 72*SVector(
parse(Float64, node["width"]),
parse(Float64, node["height"])
)
props[:size] = 72*SVector(parse(Float64, node["width"]),
parse(Float64, node["height"]))
add_vertex!(graph, props)
end
@ -278,4 +278,50 @@ function to_graphviz(g::AbstractHalfEdgeGraph;
to_graphviz(pg)
end
# Schemas
#########
""" Construct a graph representing the schema
"""
function to_graphviz_graph(pres::Presentation{Schema})
obs,homs,datas,attrs = generators.(Ref(pres), [:Ob,:Hom,:Data,:Attr])
g = PropertyGraph{Any}()
add_vertices!(g,length(obs))
for (i,ob) in enumerate(obs)
set_vprop!(g,i,:label,string(nameof(ob)))
set_vprop!(g,i,:shape,"plain")
set_vprop!(g,i,:margin,"2")
end
add_vertices!(g,length(datas))
for (i,data) in enumerate(datas)
set_vprop!(g,i+length(obs),:label,string(nameof(data)))
end
add_edges!(g,
generator_index.(Ref(pres), nameof.(dom.(homs))),
generator_index.(Ref(pres), nameof.(codom.(homs))))
for (i,hom) in enumerate(homs)
set_eprop!(g,i,:label,string(nameof(hom)))
set_eprop!(g,i,:len,"2")
end
add_edges!(g,
generator_index.(Ref(pres), nameof.(dom.(attrs))),
length(obs) .+ generator_index.(Ref(pres), nameof.(codom.(attrs))))
for (i,attr) in enumerate(attrs)
set_eprop!(g,i+length(homs),:label,string(nameof(attr)))
set_eprop!(g,i+length(homs),:len,"2")
end
set_gprop!(g,:graph,Dict(:rankdir => "LR"))
set_gprop!(g,:prog,"neato")
g
end
to_graphviz(pres::Presentation{Schema}) = to_graphviz(to_graphviz_graph(pres))
end

+ 15
- 12
src/graphics/GraphvizWiringDiagrams.jl View File

@ -270,36 +270,38 @@ function graphviz_outer_box(f::WiringDiagram;
stmts = Graphviz.Statement[]
ninputs, noutputs = length(input_ports(f)), length(output_ports(f))
if ninputs > 0
push!(stmts, graphviz_outer_ports(input_id(f), InputPort, ninputs;
push!(stmts, graphviz_outer_ports(InputPort, ninputs;
anchor=anchor, orientation=orientation))
end
if noutputs > 0
push!(stmts, graphviz_outer_ports(output_id(f), OutputPort, noutputs;
push!(stmts, graphviz_outer_ports(OutputPort, noutputs;
anchor=anchor, orientation=orientation))
end
# Input and output ports.
graphviz_port = (port::Port) -> Graphviz.NodeID(
port_node_name(port.box, port.port),
port_anchor(port.kind, orientation)
)
inputs = [ graphviz_port(Port(input_id(f), OutputPort, i)) for i in 1:ninputs ]
outputs = [ graphviz_port(Port(output_id(f), InputPort, i)) for i in 1:noutputs ]
inputs = map(1:ninputs) do i
Graphviz.NodeID(port_node_name(InputPort, i),
port_anchor(OutputPort, orientation))
end
outputs = map(1:noutputs) do i
Graphviz.NodeID(port_node_name(OutputPort, i),
port_anchor(InputPort, orientation))
end
GraphvizBox(stmts, inputs, outputs)
end
""" Create invisible nodes for the input or output ports of an outer box.
"""
function graphviz_outer_ports(v::Int, kind::PortKind, nports::Int;
function graphviz_outer_ports(kind::PortKind, nports::Int;
anchor::Bool=true, orientation::LayoutOrientation=TopToBottom)::Graphviz.Subgraph
@assert nports > 0
port_width = "$(round(24/72,digits=3))" # port width in inches
nodes = [ port_node_name(v, i) for i in 1:nports ]
nodes = [ port_node_name(kind, i) for i in 1:nports ]
stmts = Graphviz.Statement[
Graphviz.Node(nodes[i], id=port_name(kind, i)) for i in 1:nports
]
if anchor
if anchor && length(nodes) >= 2
push!(stmts, Graphviz.Edge(nodes))
end
Graphviz.Subgraph(
@ -320,7 +322,8 @@ function graphviz_outer_ports(v::Int, kind::PortKind, nports::Int;
),
)
end
port_node_name(v::Int, port::Int) = string(box_id([v]), "p", port)
port_node_name(kind::PortKind, port::Int) =
string(kind == InputPort ? "n0in" : "n0out", port)
""" Graphviz rank direction (`rankdir`) for layout orientation.


+ 5
- 4
src/graphics/WiringDiagramLayouts.jl View File

@ -472,10 +472,11 @@ end
"""
function layout_outer_ports!(diagram::WiringDiagram, opts::LayoutOptions; kw...)
original_value = x -> x isa PortLayout ? x.value : x # XXX: This is ugly.
diagram.input_ports = map(original_value, input_ports(diagram))
diagram.output_ports = map(original_value, output_ports(diagram))
diagram.input_ports, diagram.output_ports =
layout_outer_ports(diagram, opts; kw...)
set_input_ports!(diagram, map(original_value, input_ports(diagram)))
set_output_ports!(diagram, map(original_value, output_ports(diagram)))
in_ports, out_ports = layout_outer_ports(diagram, opts; kw...)
set_input_ports!(diagram, in_ports)
set_output_ports!(diagram, out_ports)
diagram
end


+ 3
- 3
src/graphics/YFilesWiringDiagrams.jl View File

@ -99,12 +99,12 @@ function parse_yfiles_diagram(BoxValue::Type, WireValue::Type, xdoc::XMLDocument
box_layout = if pop!(vprops(graph, v), :input, false)
# Special case: diagram inputs.
ports, coord_map = infer_output_ports(graph, v)
diagram.input_ports = ports
add_input_ports!(diagram, ports)
BoxLayout(input_id(diagram), Dict(), coord_map)
elseif pop!(vprops(graph, v), :output, false)
# Special case: diagram outputs.
ports, coord_map = infer_input_ports(graph, v)
diagram.output_ports = ports
add_output_ports!(diagram, ports)
BoxLayout(output_id(diagram), coord_map, Dict())
else
# Generic case: a box.
@ -116,7 +116,7 @@ function parse_yfiles_diagram(BoxValue::Type, WireValue::Type, xdoc::XMLDocument
end
push!(boxes, box_layout)
end
# Add wires to diagram.
for edge in edges(graph)
wire_data = eprops(graph, edge)


+ 0
- 126
src/graphs/EmbeddedGraphs.jl View File

@ -1,126 +0,0 @@
""" Embedded graphs, represented as C-sets.
The term *embedded graph* is used as in topological graph theory, graph drawing,
and related fields to mean a combinatorial structure representing a graph
embedded in an (oriented) surface, up to equivalence under
(orientation-preserving) homeomorphism.
"""
module EmbeddedGraphs
export σ, α, ϕ, trace_vertices, trace_edges, trace_faces,
AbstractRotationGraph, RotationGraph, add_corolla!, pair_half_edges!,
AbstractRotationSystem, RotationSystem,
AbstractCombinatorialMap, CombinatorialMap
using ...Present, ...CSetDataStructures, ..BasicGraphs
using ...Permutations: cycles
using ..BasicGraphs: TheoryHalfEdgeGraph
# General properties
####################
""" Vertex permutation of rotation system or similar structure.
"""
σ(x::AbstractACSet, args...) = subpart(x, args..., :σ)
""" Edge permutation of rotation system or similar structure.
"""
α(x::AbstractACSet, args...) = subpart(x, args..., :α)
""" Face permutation of rotation system or similar structure.
"""
ϕ(x::AbstractACSet, args...) = subpart(x, args..., :ϕ)
""" Trace vertices of rotation system or similar, returning a list of cycles.
"""
trace_vertices(x::AbstractACSet) = cycles(σ(x))
""" Trace edges of rotation system or similar, return a listing of cycles.
Usually the cycles will be pairs of half edges but in a hypermap the cycles can
be arbitrary.
"""
trace_edges(x::AbstractACSet) = cycles(α(x))
""" Trace faces of rotation system or similar, returning list of cycles.
"""
trace_faces(x::AbstractACSet) = cycles(ϕ(x))
# Rotation graphs
#################
@present TheoryRotationGraph <: TheoryHalfEdgeGraph begin
σ::Hom(H,H)
compose(σ, vertex) == vertex
end
const AbstractRotationGraph = AbstractACSetType(TheoryRotationGraph)
const RotationGraph = CSetType(TheoryRotationGraph, index=[:vertex])
α(g::AbstractRotationGraph) = inv(g)
ϕ(g::AbstractRotationGraph) = sortperm(inv(g)[σ(g)]) # == (σ ⋅ inv)⁻¹
""" Add corolla to rotation graph, rotation system, or similar structure.
A *corolla* is a vertex together with its incident half-edges, the number of
which is its *valence*. The rotation on the half-edges is the consecutive one
induced by the half-edge part numbers.
"""
function add_corolla!(g::AbstractRotationGraph, valence::Int; kw...)
v = add_vertex!(g; kw...)
n = nparts(g, :H)
add_parts!(g, :H, valence; vertex=v, σ=circshift((n+1):(n+valence), -1))
end
""" Pair together half-edges into edges.
"""
pair_half_edges!(g::AbstractRotationGraph, h, h′) =
set_subpart!(g, [h; h′], :inv, [h′; h])
# Rotation systems
##################
@present TheoryRotationSystem(FreeSchema) begin
H::Ob
σ::Hom(H,H)
α::Hom(H,H)
compose(α, α) == id(H)
end
const AbstractRotationSystem = AbstractACSetType(TheoryRotationSystem)
const RotationSystem = CSetType(TheoryRotationSystem)
# ϕ == (σ⋅α)⁻¹ == α⁻¹ ⋅ σ⁻¹
ϕ(sys::AbstractRotationSystem) = sortperm(α(sys)[σ(sys)])
function add_corolla!(sys::AbstractRotationSystem, valence::Int)
n = nparts(sys, :H)
add_parts!(sys, :H, valence; σ=circshift((n+1):(n+valence), -1))
end
pair_half_edges!(sys::AbstractRotationSystem, h, h′) =
set_subpart!(sys, [h; h′], :α, [h′; h])
# Combinatorial maps
####################
@present TheoryHypermap(FreeSchema) begin
H::Ob
σ::Hom(H,H)
α::Hom(H,H)
ϕ::Hom(H,H)
compose(σ, α, ϕ) == id(H)
end
@present TheoryCombinatorialMap <: TheoryHypermap begin
compose(α, α) == id(H)
end
const AbstractCombinatorialMap = AbstractACSetType(TheoryCombinatorialMap)
const CombinatorialMap = CSetType(TheoryCombinatorialMap)
# TODO: What kind of interface should we have for maps and hypermaps?
end

+ 0
- 1
src/graphs/Graphs.jl View File

@ -4,7 +4,6 @@ using Reexport
include("BasicGraphs.jl")
include("BipartiteGraphs.jl")
include("EmbeddedGraphs.jl")
include("PropertyGraphs.jl")
include("GraphAlgorithms.jl")


+ 2
- 2
src/programs/ParseJuliaPrograms.jl View File

@ -97,10 +97,10 @@ function parse_wiring_diagram(pres::Presentation, call::Expr0, body::Expr)::Wiri
# Add outgoing wires for return values.
out_ports = normalize_arguments((value,))
diagram.output_ports = [
add_output_ports!(diagram, [
# XXX: Inferring the output port types is not reliable.
port_value(diagram, first(ports)) for ports in out_ports
]
])
add_wires!(diagram, [
port => Port(v_out, InputPort, i)
for (i, ports) in enumerate(out_ports) for port in ports


+ 12
- 23
src/theories/Schema.jl View File

@ -61,16 +61,10 @@ end
CatDescType(pres::Presentation{Schema}) = typeof(CatDesc(pres))
function Base.getproperty(AD::Type{T},i::Symbol) where
{Ob,Hom,Dom,Codom,T<:CatDesc{Ob,Hom,Dom,Codom}}
@match i begin
:ob => Ob
:hom => Hom
:dom => Dom
:codom => Codom
_ => getfield(AD,i)
end
end
ob(::Type{T}) where {Ob,T <: CatDesc{Ob}} = Ob
hom(::Type{T}) where {Ob,Hom, T <: CatDesc{Ob,Hom}} = Hom
dom(::Type{T}) where {Ob,Hom,Dom, T <: CatDesc{Ob,Hom,Dom}} = Dom
codom(::Type{T}) where {Ob,Hom,Dom,Codom, T <: CatDesc{Ob,Hom,Dom,Codom}} = Codom
function ob_num(CD::Type{T}, ob::Symbol) where {Ob,Hom,Dom,Codom, T <: CatDesc{Ob,Hom,Dom,Codom}}
findfirst(Ob .== ob)::Int
@ -117,7 +111,7 @@ struct AttrDesc{CD,Data,Attr,ADom,ACodom}
CD = CatDescType(pres)
datas, attrs = generators(pres, :Data), generators(pres,:Attr)
data_syms, attr_syms = nameof.(datas), nameof.(attrs)
ob_num = ob -> findfirst(CD.ob .== ob)::Int
ob_num = ob -> findfirst(Theories.ob(CD) .== ob)::Int
data_num = ob -> findfirst(data_syms .== ob)::Int
new{CD,Tuple(data_syms), Tuple(attr_syms),
Tuple(@. ob_num(nameof(dom(attrs)))), Tuple(@. data_num(nameof(codom(attrs))))}()
@ -129,17 +123,12 @@ end
AttrDescType(pres::Presentation{Schema}) = typeof(AttrDesc(pres))
function Base.getproperty(AD::Type{T}, i::Symbol) where
{CD,Data,Attr,ADom,ACodom,T <: AttrDesc{CD,Data,Attr,ADom,ACodom}}
@match i begin
:cd => CD
:data => Data
:attr => Attr
:adom => ADom
:acodom => ACodom
_ => getfield(AD,i)
end
end
data(::Type{T}) where {CD,Data, T <: AttrDesc{CD,Data}} = Data
attr(::Type{T}) where {CD,Data,Attr, T <: AttrDesc{CD,Data,Attr}} = Attr
adom(::Type{T}) where {CD,Data,Attr,ADom,
T <: AttrDesc{CD,Data,Attr,ADom}} = ADom
acodom(::Type{T}) where {CD,Data,Attr,ADom,ACodom,
T <: AttrDesc{CD,Data,Attr,ADom,ACodom}} = ACodom
function data_num(AD::Type{T}, data::Symbol) where
{CD,Data,Attr,ADom,ACodom,T <: AttrDesc{CD,Data,Attr,ADom,ACodom}}
@ -171,7 +160,7 @@ end
function dom(AD::Type{T}, attr::Union{Int,Symbol}) where
{CD,Data,Attr,ADom,ACodom,T <: AttrDesc{CD,Data,Attr,ADom,ACodom}}
CD.ob[dom_num(AD,attr)]
ob(CD)[dom_num(AD,attr)]
end
function codom(AD::Type{T}, attr::Union{Int,Symbol}) where


+ 9
- 9
src/wiring_diagrams/Algorithms.jl View File

@ -19,7 +19,7 @@ import ..DirectedWiringDiagrams: set_box
Returns a list of box IDs, excluding the outer box's input and output IDs.
"""
function topological_sort(d::WiringDiagram)::AbstractVector{Int}
filter(v -> v outer_ids(d), topological_sort(graph(d)))
topological_sort(internal_graph(d))
end
# Normalization
@ -49,7 +49,7 @@ The main difference is the possibility of zero or many function outputs.
"""
function normalize_copy!(d::WiringDiagram)
# Compute equivalence classes of boxes (without modifying the diagram).
sets = IntDisjointSets(nboxes(d)+2)
sets = DisjointSets{Int}(vcat([input_id(d),output_id(d)], box_ids(d)))
initial = filter(box_ids(d)) do v
all(u == input_id(d) for u in inneighbors(d,v))
end
@ -74,7 +74,7 @@ function normalize_copy!(d::WiringDiagram)
d
end
function merge_if_congruent!(d::WiringDiagram, sets::IntDisjointSets, v1::Int, v2::Int)
function merge_if_congruent!(d::WiringDiagram, sets::DisjointSets{Int}, v1::Int, v2::Int)
if v1 == v2 || (!in_same_set(sets, v1, v2) && is_congruent(d, sets, v1, v2))
union!(sets, v1, v2)
for out1 in filter(v -> v != output_id(d), outneighbors(d, v1))
@ -85,7 +85,7 @@ function merge_if_congruent!(d::WiringDiagram, sets::IntDisjointSets, v1::Int, v
end
end
function is_congruent(d::WiringDiagram, sets::IntDisjointSets, v1::Int, v2::Int)::Bool
function is_congruent(d::WiringDiagram, sets::DisjointSets{Int}, v1::Int, v2::Int)::Bool
box(d, v1) == box(d, v2) && all(eachindex(input_ports(box(d,v1)))) do port
wires1, wires2 = in_wires(d,v1,port), in_wires(d,v2,port)
n1, n2 = length(wires1), length(wires2)
@ -110,7 +110,7 @@ function normalize_delete!(d::WiringDiagram)
push!(unused, v)
end
end
rem_boxes!(d, unused)
rem_boxes!(d, sort(collect(unused)))
d
end
@ -135,9 +135,9 @@ wires. Typical choices are:
In both cases, this algorithm has the property that if there is a permutation
with no crossings, it will find it.
"""
function crossing_minimization_by_sort(d::WiringDiagram, vs::Vector{Int};
sources::Vector{Int}=Int[], targets::Vector{Int}=Int[],
statistic::Function=mean)::Vector{Int}
function crossing_minimization_by_sort(d::WiringDiagram, vs::AbstractVector{Int};
sources::AbstractVector{Int}=Int[], targets::AbstractVector{Int}=Int[],
statistic::Function=mean)::AbstractVector{Int}
@assert allunique(vs) && allunique(sources) && allunique(targets)
if isempty(sources) && isempty(targets)
# Degenerate case: nothing to sort, so preserve original order.
@ -160,7 +160,7 @@ end
""" Make function mapping ports to logical coordinates.
"""
function port_coords(d::WiringDiagram, vs::Vector{Int}, kind::PortKind)
function port_coords(d::WiringDiagram, vs::AbstractVector{Int}, kind::PortKind)
get_ports = kind == InputPort ? input_ports : output_ports
index = Dict(vs[i] => i for i in eachindex(vs))
sizes = [ length(get_ports(d,v)) for v in vs ]


+ 396
- 143
src/wiring_diagrams/Directed.jl View File

@ -17,8 +17,10 @@ Graphviz or other declarative diagram languages.
"""
module DirectedWiringDiagrams
export AbstractBox, Box, WiringDiagram, Wire, Port, PortKind,
InputPort, OutputPort, input_ports, output_ports, input_id, output_id,
outer_ids, boxes, box_ids, nboxes, nwires, box, wires, has_wire, graph,
InputPort, OutputPort, input_ports, output_ports, set_input_ports!, set_output_ports!,
add_input_ports!, add_output_ports!,