From 047bccdbb8780873e4868359223f0b77205ad8ed Mon Sep 17 00:00:00 2001 From: epatters Date: Fri, 3 Jan 2020 02:52:34 -0500 Subject: [PATCH 01/12] ENH: Add (adjoint) `mate` to compact closed category signature. --- src/core/Syntax.jl | 10 +++++++++- src/doctrines/Monoidal.jl | 29 +++++++++++++++-------------- test/doctrines/Monoidal.jl | 11 +++++++---- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/core/Syntax.jl b/src/core/Syntax.jl index bf465ca66..0c5a54551 100644 --- a/src/core/Syntax.jl +++ b/src/core/Syntax.jl @@ -535,13 +535,21 @@ end function show_latex_infix(io::IO, expr::GATExpr, op::String; paren::Bool=false, kw...) - show_latex_paren(io, expr) = show_latex(io, expr; paren=true, kw...) + show_latex_paren(io, expr) = show_latex(io, expr, paren=true) sep = op == " " ? op : " $op " if (paren) print(io, "\\left(") end join(io, [sprint(show_latex_paren, arg) for arg in args(expr)], sep) if (paren) print(io, "\\right)") end end +function show_latex_postfix(io::IO, expr::GATExpr, op::String; kw...) + @assert length(args(expr)) == 1 + print(io, "{") + show_latex(io, first(expr), paren=true) + print(io, "}") + print(io, op) +end + function show_latex_script(io::IO, expr::GATExpr, head::String; super::Bool=false, kw...) print(io, head, super ? "^" : "_", "{") diff --git a/src/doctrines/Monoidal.jl b/src/doctrines/Monoidal.jl index 0b1d3c85b..b1923c374 100644 --- a/src/doctrines/Monoidal.jl +++ b/src/doctrines/Monoidal.jl @@ -6,7 +6,7 @@ export MonoidalCategory, otimes, munit, ⊗, collect, ndims, mmerge, create, copair, incl1, incl2, ∇, □, MonoidalCategoryWithBidiagonals, BiproductCategory, FreeBiproductCategory, CartesianClosedCategory, FreeCartesianClosedCategory, hom, ev, curry, - CompactClosedCategory, FreeCompactClosedCategory, dual, dunit, dcounit, + CompactClosedCategory, FreeCompactClosedCategory, dual, dunit, dcounit, mate, DaggerCategory, FreeDaggerCategory, dagger, DaggerCompactCategory, FreeDaggerCompactCategory @@ -289,8 +289,9 @@ See also `FreeCartesianCategory`. end function show_latex(io::IO, expr::ObExpr{:hom}; kw...) - show_latex(io, last(expr)) - print(io, "^{") + print(io, "{") + show_latex(io, last(expr), paren=true) + print(io, "}^{") show_latex(io, first(expr)) print(io, "}") end @@ -317,13 +318,15 @@ end # Counit of duality, aka the evaluation map dcounit(A::Ob)::Hom(otimes(A,dual(A)), munit()) + # Adjoint mate of morphism f. + mate(f::Hom(A,B))::Hom(dual(B),dual(A)) <= (A::Ob, B::Ob) + # Closed monoidal category hom(A::Ob, B::Ob) = otimes(B, dual(A)) ev(A::Ob, B::Ob) = otimes(id(B), compose(braid(dual(A),A), dcounit(A))) curry(A::Ob, B::Ob, f::Hom) = compose( otimes(id(A), compose(dunit(B), braid(dual(B),B))), - otimes(f, id(dual(B))) - ) + otimes(f, id(dual(B)))) end @syntax FreeCompactClosedCategory(ObExpr,HomExpr) CompactClosedCategory begin @@ -334,8 +337,7 @@ end end function show_latex(io::IO, expr::ObExpr{:dual}; kw...) - show_latex(io, first(expr)) - print(io, "^*") + Syntax.show_latex_postfix(io, expr, "^*") end function show_latex(io::IO, expr::HomExpr{:dunit}; kw...) Syntax.show_latex_script(io, expr, "\\eta") @@ -343,6 +345,9 @@ end function show_latex(io::IO, expr::HomExpr{:dcounit}; kw...) Syntax.show_latex_script(io, expr, "\\varepsilon") end +function show_latex(io::IO, expr::HomExpr{:mate}; kw...) + Syntax.show_latex_postfix(io, expr, "^*") +end # Dagger category ################# @@ -350,7 +355,7 @@ end """ Doctrine of *dagger category* """ @signature Category(Ob,Hom) => DaggerCategory(Ob,Hom) begin - dagger(f::Hom(A,B))::Hom(B,A) <= (A::Ob,B::Ob) + dagger(f::Hom(A,B))::Hom(B,A) <= (A::Ob, B::Ob) end @syntax FreeDaggerCategory(ObExpr,HomExpr) DaggerCategory begin @@ -364,7 +369,7 @@ FIXME: This signature should extend both `DaggerCategory` and `CompactClosedCategory`, but we don't support multiple inheritance yet. """ @signature CompactClosedCategory(Ob,Hom) => DaggerCompactCategory(Ob,Hom) begin - dagger(f::Hom(A,B))::Hom(B,A) <= (A::Ob,B::Ob) + dagger(f::Hom(A,B))::Hom(B,A) <= (A::Ob, B::Ob) end @syntax FreeDaggerCompactCategory(ObExpr,HomExpr) DaggerCompactCategory begin @@ -379,9 +384,5 @@ end end function show_latex(io::IO, expr::HomExpr{:dagger}; kw...) - f = first(expr) - if (head(f) != :generator) print(io, "\\left(") end - show_latex(io, f) - if (head(f) != :generator) print(io, "\\right)") end - print(io, "^\\dagger") + Syntax.show_latex_postfix(io, expr, "^\\dagger") end diff --git a/test/doctrines/Monoidal.jl b/test/doctrines/Monoidal.jl index 872761fc8..8f3349d67 100644 --- a/test/doctrines/Monoidal.jl +++ b/test/doctrines/Monoidal.jl @@ -121,7 +121,9 @@ f = Hom(:f, otimes(A,B), C) @test codom(curry(A,B,f)) == hom(B,C) # Infix notation (LaTeX) -@test latex(hom(A,B)) == "B^{A}" +@test latex(hom(A,B)) == "{B}^{A}" +@test latex(hom(otimes(A,B),C)) == "{C}^{A \\otimes B}" +@test latex(hom(A,otimes(B,C))) == "{\\left(B \\otimes C\\right)}^{A}" @test latex(ev(A,B)) == "\\mathrm{eval}_{A,B}" @test latex(curry(A,B,f)) == "\\lambda f" @@ -149,9 +151,10 @@ f = Hom(:f, otimes(A,B), C) @test codom(curry(A,B,f)) == hom(B,C) # Infix notation (LaTeX) -@test latex(dual(A)) == "A^*" +@test latex(dual(A)) == "{A}^*" @test latex(dunit(A)) == "\\eta_{A}" @test latex(dcounit(A)) == "\\varepsilon_{A}" +@test latex(mate(f)) == "{f}^*" # Dagger category ################# @@ -169,8 +172,8 @@ f, g = Hom(:f, A, B), Hom(:g, B, A) @test dagger(dagger(f)) == f # Infix notation (LaTeX) -@test latex(dagger(f)) == "f^\\dagger" -#@test latex(dagger(compose(f,g))) == "\\left(f \\cdot g\\right)^\\dagger" +@test latex(dagger(f)) == "{f}^\\dagger" +#@test latex(dagger(compose(f,g))) == "{\\left(f \\cdot g\\right)}^\\dagger" # Dagger compact category ######################### From 17b46944e89ec1de6cd45a3d175ef6350ebcba7b Mon Sep 17 00:00:00 2001 From: epatters Date: Fri, 3 Jan 2020 16:09:53 -0800 Subject: [PATCH 02/12] REFACTOR: Replace `anti_involute` w/ generalized `distribute_unary`. --- src/core/Rewrite.jl | 46 +++++++++++++++++--------------------- src/doctrines/Monoidal.jl | 20 +++++++++++------ src/doctrines/Relations.jl | 16 ++++--------- test/doctrines/Monoidal.jl | 1 + 4 files changed, 38 insertions(+), 45 deletions(-) diff --git a/src/core/Rewrite.jl b/src/core/Rewrite.jl index 461b89710..8f24a6519 100644 --- a/src/core/Rewrite.jl +++ b/src/core/Rewrite.jl @@ -4,8 +4,9 @@ The current content of this module is just a stopgap until I can implement a generic term rewriting system. """ module Rewrite -export associate, associate_unit, distribute_unary, anti_involute +export associate, associate_unit, distribute_unary, involute +using Compat using ..Syntax """ Simplify associative binary operation. @@ -31,36 +32,29 @@ function associate_unit(expr::GATExpr, unit::Function)::GATExpr else associate(expr) end end -""" Distribute unary operation over a binary operation. +""" Distribute unary operation over binary operation. """ -function distribute_unary(raw_expr::GATExpr, un_op::Function, - bin_op::Function)::GATExpr - if head(raw_expr) != nameof(un_op) - return raw_expr - end - expr = first(raw_expr) - if head(expr) == nameof(bin_op) - bin_op([un_op(A) for A in args(expr)]...) +function distribute_unary(expr::GATExpr, unary::Function, binary::Function; + unit::Union{Function,Nothing}=nothing, + contravariant::Bool=false)::GATExpr + if (head(expr) != nameof(unary)) return expr end + @assert length(args(expr)) == 1 + arg = first(expr) + if head(arg) == nameof(binary) + binary(map(unary, (contravariant ? reverse : identity)(args(arg)))) + elseif !isnothing(unit) && head(arg) == nameof(unit) + arg else - raw_expr + expr end end -""" Simplify unary operation that is an anti-involution on a (typed) monoid. -""" -function anti_involute(raw_expr::GATExpr, inv::Function, op::Function, - unit::Function)::GATExpr - if head(raw_expr) != nameof(inv) - return raw_expr - end - expr = first(raw_expr) - if head(expr) == nameof(inv) - first(expr) - elseif head(expr) == nameof(op) - op([inv(A) for A in reverse(args(expr))]...) - elseif head(expr) == nameof(unit) - expr - else raw_expr end +""" Simplify involutive unary operation. +""" +function involute(expr::GATExpr) + @assert length(args(expr)) == 1 + arg = first(expr) + head(expr) == head(arg) ? first(arg) : expr end end diff --git a/src/doctrines/Monoidal.jl b/src/doctrines/Monoidal.jl index b1923c374..39c6c8512 100644 --- a/src/doctrines/Monoidal.jl +++ b/src/doctrines/Monoidal.jl @@ -330,7 +330,8 @@ end end @syntax FreeCompactClosedCategory(ObExpr,HomExpr) CompactClosedCategory begin - dual(A::Ob) = anti_involute(Super.dual(A), dual, otimes, munit) + dual(A::Ob) = distribute_unary(involute(Super.dual(A)), dual, otimes, + unit=munit, contravariant=true) otimes(A::Ob, B::Ob) = associate_unit(Super.otimes(A,B), munit) otimes(f::Hom, g::Hom) = associate(Super.otimes(f,g)) compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) @@ -360,7 +361,13 @@ end @syntax FreeDaggerCategory(ObExpr,HomExpr) DaggerCategory begin compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) - dagger(f::Hom) = anti_involute(Super.dagger(f), dagger, compose, id) + dagger(f::Hom) = distribute_dagger(involute(Super.dagger(f))) +end + +""" Distribute dagger over composition. +""" +function distribute_dagger(f::HomExpr) + distribute_unary(f, dagger, compose, unit=id, contravariant=true) end """ Doctrine of *dagger compact category* @@ -373,14 +380,13 @@ FIXME: This signature should extend both `DaggerCategory` and end @syntax FreeDaggerCompactCategory(ObExpr,HomExpr) DaggerCompactCategory begin - dual(A::Ob) = anti_involute(Super.dual(A), dual, otimes, munit) + dual(A::Ob) = distribute_unary(involute(Super.dual(A)), dual, otimes, + unit=munit, contravariant=true) otimes(A::Ob, B::Ob) = associate_unit(Super.otimes(A,B), munit) otimes(f::Hom, g::Hom) = associate(Super.otimes(f,g)) compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) - function dagger(f::Hom) - f = anti_involute(Super.dagger(f), dagger, compose, id) - distribute_unary(f, dagger, otimes) - end + dagger(f::Hom) = distribute_unary(distribute_dagger(involute(Super.dagger(f))), + dagger, otimes) end function show_latex(io::IO, expr::HomExpr{:dagger}; kw...) diff --git a/src/doctrines/Relations.jl b/src/doctrines/Relations.jl index 123ce9833..d763a9bf5 100644 --- a/src/doctrines/Relations.jl +++ b/src/doctrines/Relations.jl @@ -32,12 +32,8 @@ end otimes(A::Ob, B::Ob) = associate_unit(Super.otimes(A,B), munit) otimes(f::Hom, g::Hom) = associate(Super.otimes(f,g)) compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) - - function dagger(f::Hom) - f = anti_involute(Super.dagger(f), dagger, compose, id) - distribute_unary(f, dagger, otimes) - end - + dagger(f::Hom) = distribute_unary(distribute_dagger(involute(Super.dagger(f))), + dagger, otimes) meet(f::Hom, g::Hom) = compose(mcopy(dom(f)), otimes(f,g), mmerge(codom(f))) top(A::Ob, B::Ob) = compose(delete(A), create(B)) end @@ -63,12 +59,8 @@ end otimes(A::Ob, B::Ob) = associate_unit(Super.otimes(A,B), munit) otimes(f::Hom, g::Hom) = associate(Super.otimes(f,g)) compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) - - function dagger(f::Hom) - f = anti_involute(Super.dagger(f), dagger, compose, id) - distribute_unary(f, dagger, otimes) - end - + dagger(f::Hom) = distribute_unary(distribute_dagger(involute(Super.dagger(f))), + dagger, otimes) meet(f::Hom, g::Hom) = compose(mcopy(dom(f)), otimes(f,g), mmerge(codom(f))) top(A::Ob, B::Ob) = compose(delete(A), create(B)) end diff --git a/test/doctrines/Monoidal.jl b/test/doctrines/Monoidal.jl index 8f3349d67..84a2f7b74 100644 --- a/test/doctrines/Monoidal.jl +++ b/test/doctrines/Monoidal.jl @@ -138,6 +138,7 @@ f = Hom(:f, otimes(A,B), C) @test dual(otimes(A,B)) == otimes(dual(B),dual(A)) @test dual(I) == I @test dual(dual(A)) == A +@test dual(otimes(dual(A),dual(B))) == otimes(B,A) # Domains and codomains @test dom(dunit(A)) == I From 2cb934cbf3be1d7d29aba2f01eccb2ced899a428 Mon Sep 17 00:00:00 2001 From: epatters Date: Fri, 3 Jan 2020 16:42:31 -0800 Subject: [PATCH 03/12] ENH: Simplify adjoint mate expressions in compact closed categories. --- src/doctrines/Monoidal.jl | 28 ++++++++++++++++++++++++---- test/doctrines/Monoidal.jl | 10 +++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/doctrines/Monoidal.jl b/src/doctrines/Monoidal.jl index 39c6c8512..77930ee3c 100644 --- a/src/doctrines/Monoidal.jl +++ b/src/doctrines/Monoidal.jl @@ -211,7 +211,8 @@ intended to mean a monoidal category with coherent diagonals and codiagonals. Unlike in a biproduct category, the naturality axioms need not be satisfied. FIXME: This signature should extend both `MonoidalCategoryWithDiagonals` and -`MonoidalCategoryWithCodiagonals`, but we don't support multiple inheritance. +`MonoidalCategoryWithCodiagonals`, but multiple inheritance is not yet +supported. """ @signature SymmetricMonoidalCategory(Ob,Hom) => MonoidalCategoryWithBidiagonals(Ob,Hom) begin mcopy(A::Ob)::Hom(A,otimes(A,A)) @@ -231,8 +232,8 @@ end Also known as a *semiadditive category*. FIXME: This signature should extend `MonoidalCategoryWithBidiagonals`, -`CartesianCategory`, and `CocartesianCategory`, but we don't support multiple -inheritance. +`CartesianCategory`, and `CocartesianCategory`, but multiple inheritance is not +yet supported. """ @signature MonoidalCategoryWithBidiagonals(Ob,Hom) => BiproductCategory(Ob,Hom) begin pair(f::Hom(A,B), g::Hom(A,C))::Hom(A,otimes(B,C)) <= (A::Ob, B::Ob, C::Ob) @@ -335,6 +336,15 @@ end otimes(A::Ob, B::Ob) = associate_unit(Super.otimes(A,B), munit) otimes(f::Hom, g::Hom) = associate(Super.otimes(f,g)) compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) + mate(f::Hom) = distribute_mate(involute(Super.mate(f))) +end + +""" Distribute adjoint mates over composition and products. +""" +function distribute_mate(f::HomExpr) + distribute_unary( + distribute_unary(f, mate, compose, contravariant=true), + mate, otimes, contravariant=true) end function show_latex(io::IO, expr::ObExpr{:dual}; kw...) @@ -372,8 +382,17 @@ end """ Doctrine of *dagger compact category* +In a dagger compact category, there are two kinds of adjoints of a morphism +`f::Hom(A,B)`, the adjoint mate `mate(f)::Hom(dual(B),dual(A))` and the dagger +adjoint `dagger(f)::Hom(B,A)`. In the category of Hilbert spaces, these are +respectively the Banach space adjoint and the Hilbert space adjoint (Reed-Simon, +Vol I, Sec VI.2). In Julia, they would correspond to `transpose` and `adjoint` +in the official `LinearAlegbra` module. For the general relationship between +mates and daggers, see Selinger's survey of graphical languages for monoidal +categories. + FIXME: This signature should extend both `DaggerCategory` and -`CompactClosedCategory`, but we don't support multiple inheritance yet. +`CompactClosedCategory`, but multiple inheritance is not yet supported. """ @signature CompactClosedCategory(Ob,Hom) => DaggerCompactCategory(Ob,Hom) begin dagger(f::Hom(A,B))::Hom(B,A) <= (A::Ob, B::Ob) @@ -387,6 +406,7 @@ end compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) dagger(f::Hom) = distribute_unary(distribute_dagger(involute(Super.dagger(f))), dagger, otimes) + mate(f::Hom) = distribute_mate(involute(Super.mate(f))) end function show_latex(io::IO, expr::HomExpr{:dagger}; kw...) diff --git a/test/doctrines/Monoidal.jl b/test/doctrines/Monoidal.jl index 84a2f7b74..850baaf0d 100644 --- a/test/doctrines/Monoidal.jl +++ b/test/doctrines/Monoidal.jl @@ -132,7 +132,7 @@ f = Hom(:f, otimes(A,B), C) A, B, C = Ob(FreeCompactClosedCategory, :A, :B, :C) I = munit(FreeCompactClosedCategory.Ob) -f = Hom(:f, otimes(A,B), C) +f, g = Hom(:f, A, B), Hom(:g, B, A) # Duals @test dual(otimes(A,B)) == otimes(dual(B),dual(A)) @@ -140,12 +140,20 @@ f = Hom(:f, otimes(A,B), C) @test dual(dual(A)) == A @test dual(otimes(dual(A),dual(B))) == otimes(B,A) +# Mates +@test mate(mate(f)) == f +@test mate(compose(f,g)) == compose(mate(g),mate(f)) +@test mate(otimes(f,g)) == otimes(mate(g),mate(f)) + # Domains and codomains @test dom(dunit(A)) == I @test codom(dunit(A)) == otimes(dual(A), A) @test dom(dcounit(A)) == otimes(A, dual(A)) @test codom(dcounit(A)) == I +@test dom(mate(f)) == dual(B) +@test codom(mate(f)) == dual(A) +f = Hom(:f, otimes(A,B), C) @test dom(ev(A,B)) == otimes(hom(A,B),A) @test codom(ev(A,B)) == B @test dom(curry(A,B,f)) == A From 8ff19df0d74c39530c057bd9a3fb4da99252f17e Mon Sep 17 00:00:00 2001 From: epatters Date: Fri, 3 Jan 2020 18:15:31 -0800 Subject: [PATCH 04/12] REFACTOR: Allow inhomogeneous (co)domain in junction nodes. --- src/graphics/GraphvizWiringDiagrams.jl | 7 ++++--- src/wiring_diagrams/Algebraic.jl | 14 ++++++++------ src/wiring_diagrams/Expressions.jl | 4 ++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/graphics/GraphvizWiringDiagrams.jl b/src/graphics/GraphvizWiringDiagrams.jl index 0b5e83bc2..933a017ba 100644 --- a/src/graphics/GraphvizWiringDiagrams.jl +++ b/src/graphics/GraphvizWiringDiagrams.jl @@ -188,9 +188,10 @@ function graphviz_box(junction::Junction, node_id::String; width = junction_size, height = junction_size, ) - inputs = repeat([Graphviz.NodeID(node_id)], junction.ninputs) - outputs = repeat([Graphviz.NodeID(node_id)], junction.noutputs) - GraphvizBox([node], inputs, outputs) + nin, nout = length(input_ports(junction)), length(output_ports(junction)) + GraphvizBox([node], + repeat([Graphviz.NodeID(node_id)], nin), + repeat([Graphviz.NodeID(node_id)], nout)) end """ Create "HTML-like" node label for a box. diff --git a/src/wiring_diagrams/Algebraic.jl b/src/wiring_diagrams/Algebraic.jl index f14270ff1..ae39b55ae 100644 --- a/src/wiring_diagrams/Algebraic.jl +++ b/src/wiring_diagrams/Algebraic.jl @@ -241,11 +241,12 @@ creations, caps, and cups. """ @auto_hash_equals struct Junction{Value} <: AbstractBox value::Value - ninputs::Int - noutputs::Int + input_ports::Vector + output_ports::Vector end -input_ports(junction::Junction) = repeat([junction.value], junction.ninputs) -output_ports(junction::Junction) = repeat([junction.value], junction.noutputs) + +Junction(value, ninputs::Int, noutputs::Int) = + Junction(value, repeat([value], ninputs), repeat([value], noutputs)) """ Wiring diagram with a junction node for each port. """ @@ -315,8 +316,9 @@ function rem_junctions(d::WiringDiagram) junction_ids = filter(v -> box(d,v) isa Junction, box_ids(d)) junction_diagrams = map(junction_ids) do v junction = box(d,v)::Junction - layer = complete_layer(junction.ninputs, junction.noutputs) - to_wiring_diagram(layer, input_ports(junction), output_ports(junction)) + inputs, outputs = input_ports(junction), output_ports(junction) + layer = complete_layer(length(inputs), length(outputs)) + to_wiring_diagram(layer, inputs, outputs) end substitute(d, junction_ids, junction_diagrams) end diff --git a/src/wiring_diagrams/Expressions.jl b/src/wiring_diagrams/Expressions.jl index 91fff4126..c6da3c6ec 100644 --- a/src/wiring_diagrams/Expressions.jl +++ b/src/wiring_diagrams/Expressions.jl @@ -239,8 +239,8 @@ end function junction_to_expr(Ob::Type, junction::Junction) ob = coerce_ob(Ob, junction.value) compose_simplify_id( - mmerge_foldl(ob, junction.ninputs), - mcopy_foldl(ob, junction.noutputs) + mmerge_foldl(ob, length(input_ports(junction))), + mcopy_foldl(ob, length(output_ports(junction))) ) end From a66ce467c1f6fb06b4dc1f7b36a323e6bf240469 Mon Sep 17 00:00:00 2001 From: epatters Date: Fri, 3 Jan 2020 18:55:13 -0800 Subject: [PATCH 05/12] BUG: Don't merge junctions with different values. --- src/wiring_diagrams/Algebraic.jl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/wiring_diagrams/Algebraic.jl b/src/wiring_diagrams/Algebraic.jl index ae39b55ae..18406196f 100644 --- a/src/wiring_diagrams/Algebraic.jl +++ b/src/wiring_diagrams/Algebraic.jl @@ -328,16 +328,17 @@ end function merge_junctions(d::WiringDiagram) junction_ids = filter(v -> box(d,v) isa Junction, box_ids(d)) junction_graph, vmap = induced_subgraph(graph(d), junction_ids) - components = [ [ vmap[v] for v in component ] + for edge in edges(junction_graph) + # Only merge junctions with equal values. + if box(d, vmap[src(edge)]).value != box(d, vmap[dst(edge)]).value + rem_edge!(junction_graph, edge) + end + end + components = [ [vmap[v] for v in component] for component in weakly_connected_components(junction_graph) if length(component) > 1 ] - values = map(components) do component - values = unique(box(d,v).value for v in component) - @assert length(values) == 1 - first(values) - end - encapsulate(d, components; discard_boxes=true, values=values, - make_box = (value, in, out) -> Junction(value, length(in), length(out))) + values = [ box(d, first(component)).value for component in components ] + encapsulate(d, components; discard_boxes=true, values=values, make_box=Junction) end end From c17ea9554e673aa4afe4005ea541db6b28cec361 Mon Sep 17 00:00:00 2001 From: epatters Date: Fri, 3 Jan 2020 21:53:45 -0800 Subject: [PATCH 06/12] ENH: (Co)units via junctions for (non-self-dual) compact closed cats. --- src/wiring_diagrams/Algebraic.jl | 68 ++++++++++++++++++++++++++----- test/wiring_diagrams/Algebraic.jl | 17 ++++++++ 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/wiring_diagrams/Algebraic.jl b/src/wiring_diagrams/Algebraic.jl index 18406196f..d19f37a44 100644 --- a/src/wiring_diagrams/Algebraic.jl +++ b/src/wiring_diagrams/Algebraic.jl @@ -2,21 +2,21 @@ This module provides a high-level functional and algebraic interface to wiring diagrams, building on the low-level imperative interface. It also defines data -types and functions to represent diagonals, codiagonals, units, and counits in -wiring diagrams as special *junction nodes*. +types and functions to represent diagonals, codiagonals, duals, caps, cups, +daggers, and other structures in wiring diagrams. """ module AlgebraicWiringDiagrams export Ports, dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, permute, - mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dunit, dcounit, ocompose, - Junction, junction_diagram, add_junctions, add_junctions!, rem_junctions, - merge_junctions + mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, ocompose, + Junction, junction_diagram, junction_caps, junction_cups, add_junctions, + add_junctions!, rem_junctions, merge_junctions, DualPort using AutoHashEquals using LightGraphs using ...GAT, ...Doctrines import ...Doctrines: dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, - mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dunit, dcounit + mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit using ..WiringDiagramCore, ..WiringLayers import ..WiringDiagramCore: Box, WiringDiagram, input_ports, output_ports @@ -205,11 +205,12 @@ create(A::Ports{BiproductCategory.Hom}) = implicit_create(A) # Compact closed category #------------------------ -# Wiring diagrams as self-dual compact closed category. -# FIXME: What about compact categories that are not self-dual? +junctioned_dunit(A::Ports) = junction_caps(A, otimes(dual(A),A)) +junctioned_dcounit(A::Ports) = junction_cups(A, otimes(A,dual(A))) -dunit(A::Ports) = junction_diagram(A, 0, 2) -dcounit(A::Ports) = junction_diagram(A, 2, 0) +dual(A::Ports{CompactClosedCategory.Hom}) = dual_ports(A) +dunit(A::Ports{CompactClosedCategory.Hom}) = junctioned_dunit(A) +dcounit(A::Ports{CompactClosedCategory.Hom}) = junctioned_dcounit(A) # Operadic interface #################### @@ -248,7 +249,7 @@ end Junction(value, ninputs::Int, noutputs::Int) = Junction(value, repeat([value], ninputs), repeat([value], noutputs)) -""" Wiring diagram with a junction node for each port. +""" Wiring diagram with a junction node for each of the given ports. """ function junction_diagram(A::Ports, nin::Int, nout::Int) f = WiringDiagram(otimes(repeat([A], nin)), otimes(repeat([A], nout))) @@ -261,6 +262,38 @@ function junction_diagram(A::Ports, nin::Int, nout::Int) return f end +""" Wiring diagram of nested cups made out of junction nodes. +""" +junction_cups(A::Ports) = junction_cups(A, cat(A,A)) + +function junction_cups(A::Ports, inputs::Ports) + @assert length(inputs) == 2*length(A) + f = WiringDiagram(inputs, munit(typeof(inputs))) + m, ports = length(A), collect(inputs) + for (i, value) in enumerate(A) + j1, j2 = m-i+1, m+i + v = add_box!(f, Junction(value, ports[[j1,j2]], empty(ports))) + add_wires!(f, [(input_id(f),j1) => (v,1), (input_id(f),j2) => (v,2)]) + end + return f +end + +""" Wiring diagram of nested caps made out of junction nodes. +""" +junction_caps(A::Ports) = junction_caps(A, cat(A,A)) + +function junction_caps(A::Ports, outputs::Ports) + @assert length(outputs) == 2*length(A) + f = WiringDiagram(munit(typeof(outputs)), outputs) + m, ports = length(A), collect(outputs) + for (i, value) in enumerate(A) + j1, j2 = m-i+1, m+i + v = add_box!(f, Junction(value, empty(ports), ports[[j1,j2]])) + add_wires!(f, [(v,1) => (output_id(f),j1), (v,2) => (output_id(f),j2)]) + end + return f +end + """ Add junction nodes to wiring diagram. Transforms from the implicit to the explicit representation of diagonals and @@ -341,4 +374,17 @@ function merge_junctions(d::WiringDiagram) encapsulate(d, components; discard_boxes=true, values=values, make_box=Junction) end +# Duals and daggers +################### + +""" Dual of port value in wiring diagram. +""" +@auto_hash_equals struct DualPort{Value} + value::Value +end +DualPort(dual::DualPort) = dual.value + +dual_ports(ports::Vector) = [ DualPort(x) for x in Iterators.reverse(ports) ] +dual_ports(ports::Ports{T}) where T = Ports{T}(dual_ports(collect(ports))) + end diff --git a/test/wiring_diagrams/Algebraic.jl b/test/wiring_diagrams/Algebraic.jl index 9cb986cbd..eaf6cbac5 100644 --- a/test/wiring_diagrams/Algebraic.jl +++ b/test/wiring_diagrams/Algebraic.jl @@ -143,6 +143,23 @@ A = Ports{BiproductCategory.Hom}([:A]) @test compose(create(A), mcopy(A)) == create(otimes(A,A)) @test compose(mmerge(A), delete(A)) == delete(otimes(A,A)) +# Duals +#------ + +A, B = [ Ports{CompactClosedCategory.Hom}([sym]) for sym in [:A, :B] ] +I = munit(typeof(A)) + +@test boxes(dunit(A)) == [ Junction(:A, [], [DualPort(:A), :A]) ] +@test boxes(dcounit(A)) == [ Junction(:A, [:A, DualPort(:A)], []) ] + +# Domains and codomains +@test dom(dunit(A)) == I +@test codom(dunit(A)) == otimes(dual(A),A) +@test dom(dcounit(A)) == otimes(A,dual(A)) +@test codom(dcounit(A)) == I +@test codom(dunit(otimes(A,B))) == otimes(dual(B),dual(A),A,B) +@test dom(dcounit(otimes(A,B))) == otimes(A,B,dual(B),dual(A)) + # Operadic interface #################### From 17c73557b6c6bd97cac51e391b0fbaa4cd94b7f6 Mon Sep 17 00:00:00 2001 From: epatters Date: Fri, 3 Jan 2020 23:40:17 -0800 Subject: [PATCH 07/12] ENH: Arrow reversal in wiring diagram layouts and TikZ diagrams. Used by compact closed categories. --- .../literate/graphics/tikz_wiring_diagrams.jl | 10 +++-- src/graphics/TikZWiringDiagrams.jl | 45 ++++++++++++------- src/graphics/WiringDiagramLayouts.jl | 16 ++++--- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/docs/literate/graphics/tikz_wiring_diagrams.jl b/docs/literate/graphics/tikz_wiring_diagrams.jl index b028ac8f1..1db0cf270 100644 --- a/docs/literate/graphics/tikz_wiring_diagrams.jl +++ b/docs/literate/graphics/tikz_wiring_diagrams.jl @@ -65,17 +65,19 @@ to_tikz(mcopy(A)⋅(f⊗f)⋅mmerge(B), labels=true) # The unit and co-unit of a compact closed category appear as caps and cups. -A, B = Ob(FreeBicategoryRelations, :A, :B) -f = Hom(:f, A, B) +A, B = Ob(FreeCompactClosedCategory, :A, :B) -to_tikz(dunit(A)) +to_tikz(dunit(A), arrowtip="Stealth") #- -to_tikz(dcounit(A)) +to_tikz(dcounit(A), arrowtip="Stealth") # In a self-dual compact closed category, such as a bicategory of relations, # every morphism $f: A \to B$ has a transpose $f^\dagger: B \to A$ given by # bending wires: +A, B = Ob(FreeBicategoryRelations, :A, :B) +f = Hom(:f, A, B) + to_tikz((dunit(A) ⊗ id(B)) ⋅ (id(A) ⊗ f ⊗ id(B)) ⋅ (id(A) ⊗ dcounit(B))) # ## Custom styles diff --git a/src/graphics/TikZWiringDiagrams.jl b/src/graphics/TikZWiringDiagrams.jl index 3b87a80e2..e6f70bfaf 100644 --- a/src/graphics/TikZWiringDiagrams.jl +++ b/src/graphics/TikZWiringDiagrams.jl @@ -123,9 +123,13 @@ function tikz_wire(diagram::WiringDiagram, wire::Wire, opts::TikZOptions)::TikZ. # Use source port for wire label, following the Graphviz wiring diagrams. src_layout = port_value(diagram, wire.source) tgt_layout = port_value(diagram, wire.target) - label = opts.labels && src_layout.wire_labels && tgt_layout.wire_labels ? + label = opts.labels && src_layout.label_wires && tgt_layout.label_wires ? tikz_label(src_layout.value, opts) : nothing props = [ TikZ.Property("wire", label) ] + if !isnothing(opts.arrowtip) + reversed = src_layout.reverse_wires && tgt_layout.reverse_wires + push!(props, TikZ.Property(reversed ? "<-" : "->")) + end TikZ.Edge(exprs...; props=props) end @@ -227,29 +231,30 @@ function tikz_styles(opts::TikZOptions) styles = deepcopy(default_tikz_styles) libraries = String[] - # Options for box styles. + # Box style options. if opts.rounded_boxes push!(styles["box"], TikZ.Property("rounded corners")) end - # Options for wire style. - decorations = TikZ.Property[] + # Wire style options. if opts.labels anchor = tikz_anchor(svector(opts, 0, 1)) - push!(decorations, TikZ.Property("mark", - "at position $(opts.labels_pos) with {\\node[anchor=$anchor] {#1};}")) + append!(styles["wire"], tikz_decorate_markings([ + "at position $(opts.labels_pos) with {\\node[anchor=$anchor] {#1};}" + ])) + push!(libraries, "decorations.markings") end if !isnothing(opts.arrowtip) - push!(decorations, TikZ.Property("mark", - "at position $(opts.arrowtip_pos) with {\\arrow{$(opts.arrowtip)}}")) - push!(libraries, "arrows.meta") - end - if !isempty(decorations) - append!(styles["wire"], [ - TikZ.Property("postaction", [ TikZ.Property("decorate") ]), - TikZ.Property("decoration", [ TikZ.Property("markings"); decorations ]), - ]) - push!(libraries, "decorations.markings") + pos, arrowtip = opts.arrowtip_pos, opts.arrowtip + merge!(styles, OrderedDict( + "->" => tikz_decorate_markings([ + "at position $pos with {\\arrow{$arrowtip}}" + ]), + "<-" => tikz_decorate_markings([ + "at position $pos with {\\arrow{$arrowtip[reversed]}}" + ]), + )) + append!(libraries, ["arrows.meta", "decorations.markings"]) end (styles, libraries) @@ -281,6 +286,14 @@ const default_tikz_styles = OrderedDict{String,Vector{TikZ.Property}}( ], ) +function tikz_decorate_markings(marks::Vector{TikZ.Property}) + [ TikZ.Property("postaction", [ TikZ.Property("decorate") ]), + TikZ.Property("decoration", [ TikZ.Property("markings"); marks ]) ] +end +function tikz_decorate_markings(marks::Vector{String}) + tikz_decorate_markings([ TikZ.Property("mark", mark) for mark in marks ]) +end + const tikz_shapes = Dict( RectangleShape => "box", CircleShape => "circular box", diff --git a/src/graphics/WiringDiagramLayouts.jl b/src/graphics/WiringDiagramLayouts.jl index 476c04148..bc95b2285 100644 --- a/src/graphics/WiringDiagramLayouts.jl +++ b/src/graphics/WiringDiagramLayouts.jl @@ -93,7 +93,8 @@ contents_upper_corner(diagram::WiringDiagram) = value::Value = nothing position::Vector2D = zeros(Vector2D) # Position relative to box center. normal::Vector2D = zeros(Vector2D) # Outward unit normal vector. - wire_labels::Bool = true # Show labels for wires into/out of port? + label_wires::Bool = true # Show labels for wires into/out of port? + reverse_wires::Bool = false # Reverse wires into/out of port? end position(layout::PortLayout) = layout.position @@ -155,6 +156,9 @@ layout_hom_expr(f::HomExpr{:mcopy}, opts) = layout_junction_expr(f, opts) layout_hom_expr(f::HomExpr{:delete}, opts) = layout_junction_expr(f, opts) layout_hom_expr(f::HomExpr{:mmerge}, opts) = layout_junction_expr(f, opts) layout_hom_expr(f::HomExpr{:create}, opts) = layout_junction_expr(f, opts) + +layout_port(A::ObExpr{:dual}; kw...) = + PortLayout(; value=A, reverse_wires=true, kw...) layout_hom_expr(f::HomExpr{:dunit}, opts) = layout_junction_expr(f, opts; visible=false, pad=false) layout_hom_expr(f::HomExpr{:dcounit}, opts) = @@ -288,9 +292,9 @@ function layout_junction(value::Any, inputs::Vector, outputs::Vector, size = 2*SVector(radius, radius) box = Box(BoxLayout(value=value, shape=shape, size=size), layout_circular_ports(InputPort, inputs, radius, opts; - pad=pad, wire_labels=length(inputs) <= 1), + pad=pad, label_wires=length(inputs) <= 1), layout_circular_ports(OutputPort, outputs, radius, opts; - pad=pad, wire_labels=length(outputs) <= 1)) + pad=pad, label_wires=length(outputs) <= 1)) size_to_fit!(singleton_diagram(box), opts) end @@ -322,7 +326,7 @@ function layout_linear_ports(port_kind::PortKind, port_values::Vector, n = length(port_values) coeffs = range(-(n-1), n-1, step=2) / 2 # Always length n positions = [ start + coeff .* offset for coeff in coeffs ] - PortLayout[ PortLayout(value=value, position=pos, normal=normal_dir; kw...) + PortLayout[ layout_port(value, position=pos, normal=normal_dir; kw...) for (value, pos) in zip(port_values, positions) ] end @@ -339,7 +343,7 @@ function layout_circular_ports(port_kind::PortKind, port_values::Vector, elseif port_dir == SVector(0,-1); (0, π) end θs = collect(pad ? range(θ1,θ2,length=n+2)[2:n+1] : range(θ1,θ2,length=n)) dirs = [ SVector(cos(θ),-sin(θ)) for θ in θs ] # positive y-axis downwards - PortLayout[ PortLayout(value=value, position=radius*dir, normal=dir; kw...) + PortLayout[ layout_port(value, position=radius*dir, normal=dir; kw...) for (value, dir) in zip(port_values, dirs) ] end @@ -352,6 +356,8 @@ function layout_ports!(diagram::WiringDiagram, opts::LayoutOptions) diagram end +layout_port(value; kw...) = PortLayout(; value=value, kw...) + function position(diagram::WiringDiagram, port::Port) pos = position(port_value(diagram, port)) if !(port.box in outer_ids(diagram)) From cf06a727ffbb0509784611a1c1af0ed15b876ba1 Mon Sep 17 00:00:00 2001 From: epatters Date: Sat, 4 Jan 2020 12:03:22 -0800 Subject: [PATCH 08/12] BUG: No wire labels shown for cups or caps in TikZ wiring diagrams. --- docs/literate/graphics/tikz_wiring_diagrams.jl | 4 ++-- src/graphics/WiringDiagramLayouts.jl | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/literate/graphics/tikz_wiring_diagrams.jl b/docs/literate/graphics/tikz_wiring_diagrams.jl index 1db0cf270..2c0238730 100644 --- a/docs/literate/graphics/tikz_wiring_diagrams.jl +++ b/docs/literate/graphics/tikz_wiring_diagrams.jl @@ -67,9 +67,9 @@ to_tikz(mcopy(A)⋅(f⊗f)⋅mmerge(B), labels=true) A, B = Ob(FreeCompactClosedCategory, :A, :B) -to_tikz(dunit(A), arrowtip="Stealth") +to_tikz(dunit(A), arrowtip="Stealth", labels=true) #- -to_tikz(dcounit(A), arrowtip="Stealth") +to_tikz(dcounit(A), arrowtip="Stealth", labels=true) # In a self-dual compact closed category, such as a bicategory of relations, # every morphism $f: A \to B$ has a transpose $f^\dagger: B \to A$ given by diff --git a/src/graphics/WiringDiagramLayouts.jl b/src/graphics/WiringDiagramLayouts.jl index bc95b2285..b5a6d6d2c 100644 --- a/src/graphics/WiringDiagramLayouts.jl +++ b/src/graphics/WiringDiagramLayouts.jl @@ -288,13 +288,14 @@ end """ function layout_junction(value::Any, inputs::Vector, outputs::Vector, opts::LayoutOptions; visible::Bool=true, pad::Bool=true) + nin, nout = length(inputs), length(outputs) shape, radius = visible ? (JunctionShape, opts.junction_size) : (NoShape, 0) size = 2*SVector(radius, radius) box = Box(BoxLayout(value=value, shape=shape, size=size), layout_circular_ports(InputPort, inputs, radius, opts; - pad=pad, label_wires=length(inputs) <= 1), + pad=pad, label_wires = nin == 1 || nout == 0), layout_circular_ports(OutputPort, outputs, radius, opts; - pad=pad, label_wires=length(outputs) <= 1)) + pad=pad, label_wires = nin == 0 || nout == 1)) size_to_fit!(singleton_diagram(box), opts) end From 65d2e5196f73d897314eb2cec5e456417e7448bf Mon Sep 17 00:00:00 2001 From: epatters Date: Sat, 4 Jan 2020 12:46:06 -0800 Subject: [PATCH 09/12] REFACTOR: Common interface for box and wire labels. Now used in Graphviz, Compose.jl, and TikZ wiring diagrams. --- .../graphics/composejl_wiring_diagrams.jl | 6 ++-- src/graphics/ComposeWiringDiagrams.jl | 8 ++--- src/graphics/GraphvizWiringDiagrams.jl | 8 ++--- src/graphics/TikZWiringDiagrams.jl | 23 ++++++--------- src/graphics/WiringDiagramLayouts.jl | 29 +++++++++++++++++-- 5 files changed, 46 insertions(+), 28 deletions(-) diff --git a/docs/literate/graphics/composejl_wiring_diagrams.jl b/docs/literate/graphics/composejl_wiring_diagrams.jl index ac041c23d..20e2a7f2e 100644 --- a/docs/literate/graphics/composejl_wiring_diagrams.jl +++ b/docs/literate/graphics/composejl_wiring_diagrams.jl @@ -61,8 +61,7 @@ to_composejl(mcopy(A)⋅(f⊗f)⋅mmerge(B)) # The unit and co-unit of a compact closed category appear as caps and cups. -A, B = Ob(FreeBicategoryRelations, :A, :B) -f = Hom(:f, A, B) +A, B = Ob(FreeCompactClosedCategory, :A, :B) to_composejl(dunit(A)) #- @@ -72,6 +71,9 @@ to_composejl(dcounit(A)) # every morphism $f: A \to B$ has a transpose $f^\dagger: B \to A$ given by # bending wires: +A, B = Ob(FreeBicategoryRelations, :A, :B) +f = Hom(:f, A, B) + to_composejl((dunit(A) ⊗ id(B)) ⋅ (id(A) ⊗ f ⊗ id(B)) ⋅ (id(A) ⊗ dcounit(B))) # ## Custom styles diff --git a/src/graphics/ComposeWiringDiagrams.jl b/src/graphics/ComposeWiringDiagrams.jl index cbbf616a9..b8f1c00a0 100644 --- a/src/graphics/ComposeWiringDiagrams.jl +++ b/src/graphics/ComposeWiringDiagrams.jl @@ -10,9 +10,9 @@ const C = Compose using ...WiringDiagrams using ..WiringDiagramLayouts -using ..WiringDiagramLayouts: AbstractVector2D, Vector2D, - BoxShape, RectangleShape, JunctionShape, NoShape, - position, size, lower_corner, upper_corner, normal, tangent, wire_points +using ..WiringDiagramLayouts: AbstractVector2D, Vector2D, BoxShape, + RectangleShape, JunctionShape, NoShape, box_label, position, size, + lower_corner, upper_corner, normal, tangent, wire_points # Data types ############ @@ -130,7 +130,7 @@ function render_box(shape::BoxShape, value::Any, opts::ComposeOptions) render_box(Val(shape), value, opts) end function render_box(::Val{RectangleShape}, value::Any, opts::ComposeOptions) - labeled_rectangle(string(value), rounded=opts.rounded_boxes, + labeled_rectangle(box_label(value), rounded=opts.rounded_boxes, rect_props=opts.box_props, text_props=opts.text_props) end function render_box(::Val{JunctionShape}, ::Any, opts::ComposeOptions) diff --git a/src/graphics/GraphvizWiringDiagrams.jl b/src/graphics/GraphvizWiringDiagrams.jl index 933a017ba..053b4726c 100644 --- a/src/graphics/GraphvizWiringDiagrams.jl +++ b/src/graphics/GraphvizWiringDiagrams.jl @@ -8,7 +8,7 @@ using ...WiringDiagrams, ...WiringDiagrams.WiringDiagramSerialization import ..Graphviz import ..Graphviz: to_graphviz using ..WiringDiagramLayouts: LayoutOrientation, LeftToRight, RightToLeft, - TopToBottom, BottomToTop, is_horizontal, is_vertical + TopToBottom, BottomToTop, is_horizontal, is_vertical, box_label, wire_label # Constants and data types ########################## @@ -342,13 +342,11 @@ const port_anchors = Dict{Tuple{PortKind,LayoutOrientation},String}( """ Create a label for the main content of a box. """ -node_label(box_value::Any) = string(box_value) -node_label(::Nothing) = "" +node_label(box_value) = box_label(box_value) """ Create a label for an edge. """ -edge_label(port_value::Any) = string(port_value) -edge_label(::Nothing) = "" +edge_label(port_value) = wire_label(port_value) """ Encode attributes for Graphviz HTML-like labels. """ diff --git a/src/graphics/TikZWiringDiagrams.jl b/src/graphics/TikZWiringDiagrams.jl index e6f70bfaf..72fcac143 100644 --- a/src/graphics/TikZWiringDiagrams.jl +++ b/src/graphics/TikZWiringDiagrams.jl @@ -12,7 +12,7 @@ using ...WiringDiagrams, ...WiringDiagrams.WiringDiagramSerialization using ..WiringDiagramLayouts using ..WiringDiagramLayouts: AbstractVector2D, Vector2D, BoxLayout, BoxShape, RectangleShape, CircleShape, JunctionShape, NoShape, - position, normal, tangent, port_sign, wire_points + box_label, wire_label, position, normal, tangent, port_sign, wire_points import ..WiringDiagramLayouts: svector import ..TikZ @@ -75,7 +75,7 @@ function tikz_box(diagram::WiringDiagram, vpath::Vector{Int}, opts::TikZOptions) tikz_node(diagram.value, opts, name=box_id(vpath), style="outer box"); reduce(vcat, [ tikz_box(box(diagram, v), [vpath; v], opts) for v in box_ids(diagram) ], init=[]); - [ tikz_wire(diagram, wire, opts) for wire in wires(diagram) ]; + [ tikz_edge(diagram, wire, opts) for wire in wires(diagram) ]; ] end @@ -92,7 +92,7 @@ function tikz_node(layout::BoxLayout, opts::TikZOptions; style = tikz_shapes[layout.shape] end content = layout.shape in tikz_content_shapes ? - tikz_label(layout.value, opts) : "" + tikz_node_label(layout.value, opts) : "" TikZ.Node(name, props=[TikZ.Property(style); tikz_size(layout.size)], coord=tikz_coordinate(layout.position), @@ -101,7 +101,7 @@ end """ Make a TikZ edge/path for a wire. """ -function tikz_wire(diagram::WiringDiagram, wire::Wire, opts::TikZOptions)::TikZ.Edge +function tikz_edge(diagram::WiringDiagram, wire::Wire, opts::TikZOptions)::TikZ.Edge src, src_angle = tikz_port(diagram, wire.source, opts) tgt, tgt_angle = tikz_port(diagram, wire.target, opts) exprs, prev_angle = TikZ.PathExpression[ src ], src_angle @@ -124,7 +124,7 @@ function tikz_wire(diagram::WiringDiagram, wire::Wire, opts::TikZOptions)::TikZ. src_layout = port_value(diagram, wire.source) tgt_layout = port_value(diagram, wire.target) label = opts.labels && src_layout.label_wires && tgt_layout.label_wires ? - tikz_label(src_layout.value, opts) : nothing + tikz_edge_label(src_layout.value, opts) : nothing props = [ TikZ.Property("wire", label) ] if !isnothing(opts.arrowtip) reversed = src_layout.reverse_wires && tgt_layout.reverse_wires @@ -165,17 +165,12 @@ function tikz_port(diagram::WiringDiagram, port::Port, opts::TikZOptions) (TikZ.NodeCoordinate(coord), normal_angle) end -function tikz_label(x, opts::TikZOptions) - opts.math_mode ? string("\$", x, "\$") : string(x) +function tikz_node_label(value, opts::TikZOptions) + box_label(MIME(opts.math_mode ? "text/latex" : "text/plain"), value) end -function tikz_label(expr::GATExpr, opts::TikZOptions) - if opts.math_mode - string("\$", sprint(show_latex, expr), "\$") - else - sprint(show, expr) - end +function tikz_edge_label(value, opts::TikZOptions) + wire_label(MIME(opts.math_mode ? "text/latex" : "text/plain"), value) end -tikz_label(::Nothing, opts::TikZOptions) = "" # TikZ geometry ############### diff --git a/src/graphics/WiringDiagramLayouts.jl b/src/graphics/WiringDiagramLayouts.jl index b5a6d6d2c..107603f9d 100644 --- a/src/graphics/WiringDiagramLayouts.jl +++ b/src/graphics/WiringDiagramLayouts.jl @@ -156,13 +156,14 @@ layout_hom_expr(f::HomExpr{:mcopy}, opts) = layout_junction_expr(f, opts) layout_hom_expr(f::HomExpr{:delete}, opts) = layout_junction_expr(f, opts) layout_hom_expr(f::HomExpr{:mmerge}, opts) = layout_junction_expr(f, opts) layout_hom_expr(f::HomExpr{:create}, opts) = layout_junction_expr(f, opts) - -layout_port(A::ObExpr{:dual}; kw...) = - PortLayout(; value=A, reverse_wires=true, kw...) layout_hom_expr(f::HomExpr{:dunit}, opts) = layout_junction_expr(f, opts; visible=false, pad=false) layout_hom_expr(f::HomExpr{:dcounit}, opts) = layout_junction_expr(f, opts; visible=false, pad=false) + +layout_port(A::ObExpr{:dual}; kw...) = + PortLayout(; value=A, reverse_wires=true, kw...) +wire_label(mime::MIME, A::ObExpr{:dual}) = wire_label(mime, first(A)) layout_box_expr(f::HomExpr, opts; kw...) = layout_box(f, collect(dom(f)), collect(codom(f)), opts; kw...) @@ -398,4 +399,26 @@ end merge_wire_layouts(left::WireLayout, middle::WireLayout, right::WireLayout) = vcat(wire_points(left), wire_points(middle), wire_points(right)) +# Labels +######## + +""" Label for box in wiring diagram. +""" +box_label(value) = box_label(MIME("text/plain"), value) +box_label(mime::MIME, value) = diagram_element_label(mime, value) + +""" Label for wire in wiring diagram. + +Note: This function takes a port value, not a wire value. +""" +wire_label(value) = wire_label(MIME("text/plain"), value) +wire_label(mime::MIME, value) = diagram_element_label(mime, value) + +diagram_element_label(::MIME, value) = string(value) +diagram_element_label(::MIME, ::Nothing) = "" + +function diagram_element_label(::MIME"text/latex", expr::GATExpr) + string("\$", sprint(show_latex, expr), "\$") +end + end From ea0f391c2adadc3098bcff87bc4cfaa852ae9b08 Mon Sep 17 00:00:00 2001 From: epatters Date: Sat, 4 Jan 2020 13:18:20 -0800 Subject: [PATCH 10/12] WIP: Generic data structure for unary operations on ports and boxes. --- src/wiring_diagrams/Algebraic.jl | 71 +++++++++++++++++++++++++------ test/wiring_diagrams/Algebraic.jl | 4 +- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/wiring_diagrams/Algebraic.jl b/src/wiring_diagrams/Algebraic.jl index d19f37a44..b9449b75c 100644 --- a/src/wiring_diagrams/Algebraic.jl +++ b/src/wiring_diagrams/Algebraic.jl @@ -6,17 +6,18 @@ types and functions to represent diagonals, codiagonals, duals, caps, cups, daggers, and other structures in wiring diagrams. """ module AlgebraicWiringDiagrams -export Ports, dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, permute, - mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, ocompose, - Junction, junction_diagram, junction_caps, junction_cups, add_junctions, - add_junctions!, rem_junctions, merge_junctions, DualPort +export Ports, Junction, PortOp, BoxOp, + dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, permute, + mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger, + ocompose, junction_diagram, junction_caps, junction_cups, add_junctions, + add_junctions!, rem_junctions, merge_junctions using AutoHashEquals using LightGraphs using ...GAT, ...Doctrines import ...Doctrines: dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, - mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit + mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger using ..WiringDiagramCore, ..WiringLayers import ..WiringDiagramCore: Box, WiringDiagram, input_ports, output_ports @@ -374,17 +375,63 @@ function merge_junctions(d::WiringDiagram) encapsulate(d, components; discard_boxes=true, values=values, make_box=Junction) end -# Duals and daggers -################### +# Operations on ports and boxes +############################### -""" Dual of port value in wiring diagram. +""" Port value wrapping another value. + +Represents unary operations on ports in wiring diagrams. """ -@auto_hash_equals struct DualPort{Value} - value::Value +@auto_hash_equals struct PortOp{T} + value::Any end -DualPort(dual::DualPort) = dual.value -dual_ports(ports::Vector) = [ DualPort(x) for x in Iterators.reverse(ports) ] +""" Box wrapping another box. + +Represents unary operations on boxes in wiring diagrams. +""" +@auto_hash_equals struct BoxOp{T} <: AbstractBox + box::AbstractBox +end + +input_ports(op::BoxOp) = input_ports(op.box) +output_ports(op::BoxOp) = output_ports(op.box) + +# Duals +#------ + +const DualPort = PortOp{:dual} + +dual_port(x) = DualPort(x) +dual_port(dual::DualPort) = dual.x + +dual_ports(ports::Vector) = [ dual_port(x) for x in Iterators.reverse(ports) ] dual_ports(ports::Ports{T}) where T = Ports{T}(dual_ports(collect(ports))) +# Adjoints +#--------- + +const DaggerBox = BoxOp{:dagger} + +input_ports(dagger::DaggerBox) = output_ports(dagger.box) +output_ports(dagger::DaggerBox) = input_ports(dagger.box) + +dagger(box::Box) = DaggerBox(box) +dagger(dagger::DaggerBox) = dagger.box +dagger(junction::Junction) = Junction( + junction.value, output_ports(junction), input_ports(junction)) + +const MateBox = BoxOp{:mate} + +input_ports(mate::MateBox) = dual_ports(output_ports(mate.box)) +output_ports(mate::MateBox) = dual_ports(input_ports(mate.box)) + +mate(box::Box) = MateBox(box) +mate(mate::MateBox) = mate.box + +# Assume that mates and daggers commute, as in a dagger compact category. +# Normalize to apply mates before daggers. +dagger(mate::MateBox) = DaggerBox(mate) +mate(dagger::DaggerBox) = dagger(mate(dagger.box)) + end diff --git a/test/wiring_diagrams/Algebraic.jl b/test/wiring_diagrams/Algebraic.jl index eaf6cbac5..ec03c0788 100644 --- a/test/wiring_diagrams/Algebraic.jl +++ b/test/wiring_diagrams/Algebraic.jl @@ -149,8 +149,8 @@ A = Ports{BiproductCategory.Hom}([:A]) A, B = [ Ports{CompactClosedCategory.Hom}([sym]) for sym in [:A, :B] ] I = munit(typeof(A)) -@test boxes(dunit(A)) == [ Junction(:A, [], [DualPort(:A), :A]) ] -@test boxes(dcounit(A)) == [ Junction(:A, [:A, DualPort(:A)], []) ] +@test boxes(dunit(A)) == [ Junction(:A, [], [PortOp{:dual}(:A), :A]) ] +@test boxes(dcounit(A)) == [ Junction(:A, [:A, PortOp{:dual}(:A)], []) ] # Domains and codomains @test dom(dunit(A)) == I From 5294f55e5cbb4ca8fb519cbde7a39abf8e209740 Mon Sep 17 00:00:00 2001 From: epatters Date: Sat, 4 Jan 2020 16:00:33 -0800 Subject: [PATCH 11/12] ENH: Support for dagger monoidal and compact closed wiring diagrams. Also, define covariant and contravariant functors for wiring diagrams. --- src/doctrines/Monoidal.jl | 21 +++++++++++ src/wiring_diagrams/Algebraic.jl | 60 +++++++++++++++++++++++++++++-- test/wiring_diagrams/Algebraic.jl | 47 ++++++++++++++++++++++-- 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/doctrines/Monoidal.jl b/src/doctrines/Monoidal.jl index 77930ee3c..7b27a86e6 100644 --- a/src/doctrines/Monoidal.jl +++ b/src/doctrines/Monoidal.jl @@ -8,6 +8,7 @@ export MonoidalCategory, otimes, munit, ⊗, collect, ndims, CartesianClosedCategory, FreeCartesianClosedCategory, hom, ev, curry, CompactClosedCategory, FreeCompactClosedCategory, dual, dunit, dcounit, mate, DaggerCategory, FreeDaggerCategory, dagger, + DaggerSymmetricMonoidalCategory, FreeDaggerSymmetricMonoidalCategory, DaggerCompactCategory, FreeDaggerCompactCategory import Base: collect, ndims @@ -380,6 +381,26 @@ function distribute_dagger(f::HomExpr) distribute_unary(f, dagger, compose, unit=id, contravariant=true) end +""" Doctrine of *dagger symmetric monoidal category* + +Also known as a [symmetric monoidal dagger +category](https://ncatlab.org/nlab/show/symmetric+monoidal+dagger-category). + +FIXME: This signature should extend both `DaggerCategory` and +`SymmetricMonoidalCategory`, but multiple inheritance is not yet supported. +""" +@signature SymmetricMonoidalCategory(Ob,Hom) => DaggerSymmetricMonoidalCategory(Ob,Hom) begin + dagger(f::Hom(A,B))::Hom(B,A) <= (A::Ob, B::Ob) +end + +@syntax FreeDaggerSymmetricMonoidalCategory(ObExpr,HomExpr) DaggerSymmetricMonoidalCategory begin + otimes(A::Ob, B::Ob) = associate_unit(Super.otimes(A,B), munit) + otimes(f::Hom, g::Hom) = associate(Super.otimes(f,g)) + compose(f::Hom, g::Hom) = associate(Super.compose(f,g; strict=true)) + dagger(f::Hom) = distribute_unary(distribute_dagger(involute(Super.dagger(f))), + dagger, otimes) +end + """ Doctrine of *dagger compact category* In a dagger compact category, there are two kinds of adjoints of a morphism diff --git a/src/wiring_diagrams/Algebraic.jl b/src/wiring_diagrams/Algebraic.jl index b9449b75c..e7bbbf9aa 100644 --- a/src/wiring_diagrams/Algebraic.jl +++ b/src/wiring_diagrams/Algebraic.jl @@ -7,7 +7,7 @@ daggers, and other structures in wiring diagrams. """ module AlgebraicWiringDiagrams export Ports, Junction, PortOp, BoxOp, - dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, permute, + functor, dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, permute, mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger, ocompose, junction_diagram, junction_caps, junction_cups, add_junctions, add_junctions!, rem_junctions, merge_junctions @@ -18,6 +18,7 @@ using LightGraphs using ...GAT, ...Doctrines import ...Doctrines: dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger +import ...Syntax: functor using ..WiringDiagramCore, ..WiringLayers import ..WiringDiagramCore: Box, WiringDiagram, input_ports, output_ports @@ -134,6 +135,45 @@ function permute(A::Ports, σ::Vector{Int}; inverse::Bool=false) return f end +# Functors +#--------- + +""" Apply functor in a category of wiring diagrams. + +Defined by compatible mappings of ports and boxes. +""" +function functor(d::WiringDiagram, f_ports, f_box; contravariant::Bool=false, kw...) + functor_impl = contravariant ? contravariant_functor : covariant_functor + functor_impl(d, f_ports, f_box; kw...) +end + +function covariant_functor(d::WiringDiagram, f_ports, f_box) + result = WiringDiagram(f_ports(dom(d)), f_ports(codom(d))) + add_boxes!(result, (f_box(box(d, v)) for v in box_ids(d))) + add_wires!(result, wires(d)) + result +end + +function contravariant_functor(d::WiringDiagram, f_ports, f_box; + monoidal_contravariant::Bool=false) + result = WiringDiagram(f_ports(codom(d)), f_ports(dom(d))) + add_boxes!(result, (f_box(box(d, v)) for v in box_ids(d))) + + nports = (port::Port) -> length( + (port.kind == InputPort ? input_ports : output_ports)(d, port.box)) + map_port = (port::Port) -> Port( + if (port.box == input_id(d)) output_id(d) + elseif (port.box == output_id(d)) input_id(d) + else port.box end, + port.kind == InputPort ? OutputPort : InputPort, + monoidal_contravariant ? nports(port) - port.port + 1 : port.port + ) + add_wires!(result, map(wires(d)) do wire + Wire(wire.value, map_port(wire.target), map_port(wire.source)) + end) + result +end + # Diagonals and codiagonals #-------------------------- @@ -203,6 +243,12 @@ mmerge(A::Ports{BiproductCategory.Hom}, n::Int) = implicit_mmerge(A, n) delete(A::Ports{BiproductCategory.Hom}) = implicit_delete(A) create(A::Ports{BiproductCategory.Hom}) = implicit_create(A) +# Dagger category +#---------------- + +dagger(f::WiringDiagram{DaggerSymmetricMonoidalCategory.Hom}) = + functor(f, identity, dagger, contravariant=true) + # Compact closed category #------------------------ @@ -212,6 +258,16 @@ junctioned_dcounit(A::Ports) = junction_cups(A, otimes(A,dual(A))) dual(A::Ports{CompactClosedCategory.Hom}) = dual_ports(A) dunit(A::Ports{CompactClosedCategory.Hom}) = junctioned_dunit(A) dcounit(A::Ports{CompactClosedCategory.Hom}) = junctioned_dcounit(A) +mate(f::WiringDiagram{CompactClosedCategory.Hom}) = + functor(f, dual, mate, contravariant=true, monoidal_contravariant=true) + +dual(A::Ports{DaggerCompactCategory.Hom}) = dual_ports(A) +dunit(A::Ports{DaggerCompactCategory.Hom}) = junctioned_dunit(A) +dcounit(A::Ports{DaggerCompactCategory.Hom}) = junctioned_dcounit(A) +dagger(f::WiringDiagram{DaggerCompactCategory.Hom}) = + functor(f, identity, dagger, contravariant=true) +mate(f::WiringDiagram{DaggerCompactCategory.Hom}) = + functor(f, dual, mate, contravariant=true, monoidal_contravariant=true) # Operadic interface #################### @@ -403,7 +459,7 @@ output_ports(op::BoxOp) = output_ports(op.box) const DualPort = PortOp{:dual} dual_port(x) = DualPort(x) -dual_port(dual::DualPort) = dual.x +dual_port(dual::DualPort) = dual.value dual_ports(ports::Vector) = [ dual_port(x) for x in Iterators.reverse(ports) ] dual_ports(ports::Ports{T}) where T = Ports{T}(dual_ports(collect(ports))) diff --git a/test/wiring_diagrams/Algebraic.jl b/test/wiring_diagrams/Algebraic.jl index ec03c0788..83b504dfd 100644 --- a/test/wiring_diagrams/Algebraic.jl +++ b/test/wiring_diagrams/Algebraic.jl @@ -143,8 +143,31 @@ A = Ports{BiproductCategory.Hom}([:A]) @test compose(create(A), mcopy(A)) == create(otimes(A,A)) @test compose(mmerge(A), delete(A)) == delete(otimes(A,A)) -# Duals -#------ +# Dagger category +#---------------- + +f = singleton_diagram(DaggerSymmetricMonoidalCategory.Hom, Box(:f,[:A],[:B])) +g = singleton_diagram(DaggerSymmetricMonoidalCategory.Hom, Box(:g,[:B],[:A])) + +@test boxes(dagger(f)) == [ BoxOp{:dagger}(Box(:f,[:A],[:B])) ] + +# Domain and codomain +@test dom(dagger(f)) == codom(f) +@test codom(dagger(f)) == dom(f) + +# Functoriality +@test is_permuted_equal(dagger(compose(f,g)), compose(dagger(g),dagger(f)), [2,1]) +@test dagger(otimes(f,g)) == otimes(dagger(f),dagger(g)) + +# Involutivity +@test dagger(dagger(f)) == f +@test dagger(dagger(compose(f,g))) == compose(f,g) +@test dagger(dagger(otimes(f,g))) == otimes(f,g) + +# Compact closed category +#------------------------ + +### Duals A, B = [ Ports{CompactClosedCategory.Hom}([sym]) for sym in [:A, :B] ] I = munit(typeof(A)) @@ -160,6 +183,26 @@ I = munit(typeof(A)) @test codom(dunit(otimes(A,B))) == otimes(dual(B),dual(A),A,B) @test dom(dcounit(otimes(A,B))) == otimes(A,B,dual(B),dual(A)) +### Adjoint mates + +f = singleton_diagram(CompactClosedCategory.Hom, Box(:f,[:A],[:B])) +g = singleton_diagram(CompactClosedCategory.Hom, Box(:g,[:B],[:A])) + +@test boxes(mate(f)) == [ BoxOp{:mate}(Box(:f,[:A],[:B])) ] + +# Domain and codomain +@test dom(mate(f)) == dual(codom(f)) +@test codom(mate(f)) == dual(dom(f)) + +# Functoriality +@test is_permuted_equal(mate(compose(f,g)), compose(mate(g),mate(f)), [2,1]) +@test is_permuted_equal(mate(otimes(f,g)), otimes(mate(g),mate(f)), [2,1]) + +# Involutivity +@test mate(mate(f)) == f +@test mate(mate(compose(f,g))) == compose(f,g) +@test mate(mate(otimes(f,g))) == otimes(f,g) + # Operadic interface #################### From 86b0f023f50f1c02459077744284a7c197979cb8 Mon Sep 17 00:00:00 2001 From: epatters Date: Sat, 4 Jan 2020 16:16:22 -0800 Subject: [PATCH 12/12] ENH: Wiring diagrams for bicategories of relations (#49). --- src/wiring_diagrams/Algebraic.jl | 26 +++++++++++++++++++++++--- test/wiring_diagrams/Algebraic.jl | 17 +++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/wiring_diagrams/Algebraic.jl b/src/wiring_diagrams/Algebraic.jl index e7bbbf9aa..bd2a13a40 100644 --- a/src/wiring_diagrams/Algebraic.jl +++ b/src/wiring_diagrams/Algebraic.jl @@ -9,15 +9,16 @@ module AlgebraicWiringDiagrams export Ports, Junction, PortOp, BoxOp, functor, dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, permute, mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger, - ocompose, junction_diagram, junction_caps, junction_cups, add_junctions, - add_junctions!, rem_junctions, merge_junctions + meet, top, ocompose, junction_diagram, junction_caps, junction_cups, + add_junctions, add_junctions!, rem_junctions, merge_junctions using AutoHashEquals using LightGraphs using ...GAT, ...Doctrines import ...Doctrines: dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, - mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger + mcopy, delete, Δ, ◇, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger, + meet, top import ...Syntax: functor using ..WiringDiagramCore, ..WiringLayers import ..WiringDiagramCore: Box, WiringDiagram, input_ports, output_ports @@ -269,6 +270,25 @@ dagger(f::WiringDiagram{DaggerCompactCategory.Hom}) = mate(f::WiringDiagram{DaggerCompactCategory.Hom}) = functor(f, dual, mate, contravariant=true, monoidal_contravariant=true) +# Bicategory of relations +#------------------------ + +mcopy(A::Ports{BicategoryRelations.Hom}, n::Int) = junctioned_mcopy(A, n) +mmerge(A::Ports{BicategoryRelations.Hom}, n::Int) = junctioned_mmerge(A, n) +delete(A::Ports{BicategoryRelations.Hom}) = junctioned_delete(A) +create(A::Ports{BicategoryRelations.Hom}) = junctioned_create(A) + +dagger(f::WiringDiagram{BicategoryRelations.Hom}) = + functor(f, identity, dagger, contravariant=true) + +dunit(A::Ports{BicategoryRelations.Hom}) = junction_caps(A) +dcounit(A::Ports{BicategoryRelations.Hom}) = junction_cups(A) + +meet(f::WiringDiagram{BicategoryRelations.Hom}, g::WiringDiagram{BicategoryRelations.Hom}) = + compose(mcopy(dom(f)), otimes(f,g), mmerge(codom(f))) +top(A::Ports{BicategoryRelations.Hom}, B::Ports{BicategoryRelations.Hom}) = + compose(delete(A), create(B)) + # Operadic interface #################### diff --git a/test/wiring_diagrams/Algebraic.jl b/test/wiring_diagrams/Algebraic.jl index 83b504dfd..0ed201a10 100644 --- a/test/wiring_diagrams/Algebraic.jl +++ b/test/wiring_diagrams/Algebraic.jl @@ -203,6 +203,23 @@ g = singleton_diagram(CompactClosedCategory.Hom, Box(:g,[:B],[:A])) @test mate(mate(compose(f,g))) == compose(f,g) @test mate(mate(otimes(f,g))) == otimes(f,g) +# Bicategory of relations +#------------------------ + +A, B = [ Ports{BicategoryRelations.Hom}([sym]) for sym in [:A, :B] ] +R = singleton_diagram(BicategoryRelations.Hom, Box(:R,[:A],[:B])) +S = singleton_diagram(BicategoryRelations.Hom, Box(:S,[:A],[:B])) + +# Domains and codomains +@test dom(meet(R,S)) == A +@test codom(meet(R,S)) == B +@test dom(top(A,B)) == A +@test codom(top(A,B)) == B + +# Units and counits +@test dunit(A) == merge_junctions(compose(create(A), mcopy(A))) +@test dcounit(A) == merge_junctions(compose(mmerge(A), delete(A))) + # Operadic interface ####################