diff --git a/src/categorical_algebra/CSetDataStructures.jl b/src/categorical_algebra/CSetDataStructures.jl index d475d670a..bf98a4759 100644 --- a/src/categorical_algebra/CSetDataStructures.jl +++ b/src/categorical_algebra/CSetDataStructures.jl @@ -604,8 +604,9 @@ function Base.show(io::IO, acs::T) where {S,T<:StructACSet{S}} s = SchemaDesc(S) if get(io, :compact, false) print(io, nameof(T)) - print(io, ": ") + print(io, " {") join(io, ("$ob = $(nparts(acs,ob))" for ob in s.obs), ", ") + print(io, "}") else print(io, T) println(io, ":") diff --git a/src/categorical_algebra/CSets.jl b/src/categorical_algebra/CSets.jl index 76a8c6c58..f13b8d33a 100644 --- a/src/categorical_algebra/CSets.jl +++ b/src/categorical_algebra/CSets.jl @@ -253,6 +253,14 @@ type_components(α::TightACSetTransformation{S}) where S = map_components(f, α::TightACSetTransformation) = TightACSetTransformation(map(f, components(α)), dom(α), codom(α)) +function Base.show(io::IO, α::TightACSetTransformation) + print(io, "ACSetTransformation(") + show(io, components(α)) + print(io, ", ") + Categories.show_domains(io, α) + print(io, ")") +end + """ Loose transformation between attributed C-sets. See [`ACSetTranformation`](@ref) for the distinction between tight and loose. @@ -308,6 +316,14 @@ function Base.getindex(α::LooseACSetTransformation, c::Symbol) end end +function Base.show(io::IO, α::LooseACSetTransformation) + print(io, "ACSetTransformation(") + show(io, merge(components(α), type_components(α))) + print(io, ", ") + Categories.show_domains(io, α) + print(io, ")") +end + function is_natural(α::ACSetTransformation{S}) where {S} X, Y = dom(α), codom(α) for (f, c, d) in flatten((zip(hom(S), dom(S), codom(S)), diff --git a/src/categorical_algebra/Categories.jl b/src/categorical_algebra/Categories.jl index d971ccff6..98afd4800 100644 --- a/src/categorical_algebra/Categories.jl +++ b/src/categorical_algebra/Categories.jl @@ -88,6 +88,9 @@ TypeCat(Ob::Type, Hom::Type) = TypeCat{Ob,Hom}() #ob(::TypeCat{Ob,Hom}, x) where {Ob,Hom} = convert(Ob, x) #hom(::TypeCat{Ob,Hom}, f) where {Ob,Hom} = convert(Hom, f) +Base.show(io::IO, ::TypeCat{Ob,Hom}) where {Ob,Hom} = + print(io, "TypeCat($Ob, $Hom)") + # Functors ########## @@ -119,6 +122,26 @@ codom(F::IdentityFunctor) = F.dom do_ob_map(F::IdentityFunctor, x) = ob(F.dom, x) do_hom_map(F::IdentityFunctor, f) = hom(F.dom, f) +function Base.show(io::IO, F::IdentityFunctor) + print(io, "id(") + show_domains(io, F, codomain=false) + print(io, ")") +end + +show_type_constructor(io::IO, ::Type{<:Functor}) = print(io, "Functor") + +function show_domains(io::IO, f; codomain::Bool=true, recurse::Bool=true) + if get(io, :hide_domains, false) + print(io, "…") + else + show(IOContext(io, :compact=>true, :hide_domains=>!recurse), dom(f)) + if codomain + print(io, ", ") + show(IOContext(io, :compact=>true, :hide_domains=>!recurse), codom(f)) + end + end +end + # Instances #---------- diff --git a/src/categorical_algebra/FinCats.jl b/src/categorical_algebra/FinCats.jl index f64a0710f..0df67a725 100644 --- a/src/categorical_algebra/FinCats.jl +++ b/src/categorical_algebra/FinCats.jl @@ -75,6 +75,14 @@ graph(C::FinCatGraph) = C.graph ob_generators(C::FinCatGraph) = vertices(graph(C)) hom_generators(C::FinCatGraph) = edges(graph(C)) +function Base.show(io::IO, C::FinCatGraph) + print(io, "FinCat(") + show(io, graph(C)) + print(io, ", [") + join(io, equations(C), ", ") + print(io, "])") +end + # Paths in graphs #---------------- @@ -114,6 +122,12 @@ function Base.vcat(p1::Path, p2::Path) Path(vcat(edges(p1), edges(p2)), src(p1), tgt(p2)) end +function Base.show(io::IO, path::Path) + print(io, "Path(") + show(io, edges(path)) + print(io, ": $(src(path)) → $(tgt(path)))") +end + """ Abstract type for category whose morphisms are paths in a graph. (Or equivalence classes of paths in a graph, but we compute with @@ -152,6 +166,12 @@ FinCatGraph(g::HasGraph) = FreeCatGraph(g) is_free(::FreeCatGraph) = true +function Base.show(io::IO, C::FreeCatGraph) + print(io, "FinCat(") + show(io, graph(C)) + print(io, ")") +end + # Category on graph with equations #--------------------------------- @@ -228,6 +248,12 @@ hom(C::FinCatPresentation{Schema}, f::GATExpr) = gat_typeof(f) ∈ (:Hom, :Attr) ? f : error("Expression $f is not a morphism or attribute") +function Base.show(io::IO, C::FinCatPresentation) + print(io, "FinCat(") + show(io, presentation(C)) + print(io, ")") +end + # Functors ########## @@ -256,6 +282,9 @@ end (F::FinDomFunctor)(expr::ObExpr) = ob_map(F, expr) (F::FinDomFunctor)(expr::HomExpr) = hom_map(F, expr) +Categories.show_type_constructor(io::IO, ::Type{<:FinDomFunctor}) = + print(io, "FinDomFunctor") + """ Is the purported functor on a presented category functorial? This function checks that functor preserves domains and codomains. When @@ -291,6 +320,9 @@ FinFunctor(ob_map, hom_map, dom::FinCat, codom::FinCat) = FinFunctor(ob_map, hom_map, dom::Presentation, codom::Presentation) = FinDomFunctor(ob_map, hom_map, FinCat(dom), FinCat(codom)) +Categories.show_type_constructor(io::IO, ::Type{<:FinFunctor}) = + print(io, "FinFunctor") + # Mapping-based functors #----------------------- @@ -328,25 +360,23 @@ functor_key(C::FinCat, expr::GATExpr) = head(expr) == :generator ? Categories.do_ob_map(F::FinDomFunctorMap, x) = F.ob_map[x] Categories.do_hom_map(F::FinDomFunctorMap, f) = F.hom_map[f] +collect_ob(F::FinDomFunctorMap) = values(F.ob_map) +collect_hom(F::FinDomFunctorMap) = values(F.hom_map) + function Categories.do_compose(F::FinDomFunctorMap, G::FinDomFunctorMap) FinDomFunctorMap(mapvals(x -> ob_map(G, x), F.ob_map), mapvals(f -> hom_map(G, f), F.hom_map), dom(F), codom(G)) end -""" Functor object and morphism maps given as vectors. -""" -const FinDomFunctorVector{Dom<:FinCat,Codom<:Cat, - ObMap<:AbstractVector,HomMap<:AbstractVector} = - FinDomFunctorMap{Dom,Codom,ObMap,HomMap} - -collect_ob(F::FinDomFunctorVector) = F.ob_map -collect_hom(F::FinDomFunctorVector) = F.hom_map - -""" Functor with object and morphism maps given as dictionaries. -""" -const FinDomFunctorDict{Dom<:FinCat,Codom<:Cat, - ObMap<:AbstractDict,HomMap<:AbstractDict} = - FinDomFunctorMap{Dom,Codom,ObMap,HomMap} +function Base.show(io::IO, F::T) where T <: FinDomFunctorMap + Categories.show_type_constructor(io, T); print(io, "(") + show(io, F.ob_map) + print(io, ", ") + show(io, F.hom_map) + print(io, ", ") + Categories.show_domains(io, F) + print(io, ")") +end # Natural transformations ######################### @@ -431,11 +461,8 @@ transformation_key(C::FinCat, x) = x transformation_key(C::FinCat, expr::GATExpr) = head(expr) == :generator ? first(expr) : error("Natural transformation must be defined on generators") -component(α::FinTransformationMap{C,D,F,G,Comp}, c::Integer) where - {C,D,F,G,Comp<:AbstractVector} = α.components[c] -component(α::FinTransformationMap{C,D,F,G,Comp}, c::Key) where - {Key,C,D,F,G,Comp<:AbstractDict{Key}} = α.components[c] -component(α::FinTransformationMap, expr::GATExpr) = +component(α::FinTransformationMap, x) = α.components[x] +component(α::FinTransformationMap, expr::GATExpr{:generator}) = component(α, first(expr)) function Categories.do_compose(α::FinTransformationMap, β::FinTransformation) @@ -458,6 +485,16 @@ function Categories.do_composeH(α::FinTransformationMap, H::Functor) compose(F, H), compose(G, H)) end +function Base.show(io::IO, α::FinTransformationMap) + print(io, "FinTransformation(") + show(io, components(α)) + print(io, ", ") + Categories.show_domains(io, α, recurse=false) + print(io, ", ") + Categories.show_domains(io, dom(α)) + print(io, ")") +end + # Dict utilities ################ diff --git a/src/categorical_algebra/FinSets.jl b/src/categorical_algebra/FinSets.jl index 080226aa3..e0171a9c3 100644 --- a/src/categorical_algebra/FinSets.jl +++ b/src/categorical_algebra/FinSets.jl @@ -71,7 +71,11 @@ Base.iterate(set::FinSetCollection, args...) = iterate(set.collection, args...) Base.length(set::FinSetCollection) = length(set.collection) Base.in(set::FinSetCollection, elem) = in(elem, set.collection) -Base.show(io::IO, set::FinSetCollection) = print(io, "FinSet($(set.collection)") +function Base.show(io::IO, set::FinSetCollection) + print(io, "FinSet(") + show(io, set.collection) + print(io, ")") +end """ Finite set whose elements are rows of a table. @@ -86,7 +90,11 @@ FinSet(nt::NamedTuple) = TabularSet(nt) Base.iterate(set::TabularSet, args...) = iterate(Tables.rows(set.table), args...) Base.length(set::TabularSet) = Tables.rowcount(set.table) -Base.show(io::IO, set::TabularSet) = print(io, "TabularSet($(set.table))") +function Base.show(io::IO, set::TabularSet) + print(io, "TabularSet(") + show(io, set.table) + print(io, ")") +end function Base.show(io::IO, ::MIME"text/plain", set::TabularSet{T}) where T print(io, "$(length(set))-element TabularSet{$T}") @@ -96,6 +104,14 @@ function Base.show(io::IO, ::MIME"text/plain", set::TabularSet{T}) where T end end +function Base.show(io::IO, ::MIME"text/html", set::TabularSet) + println(io, "
") + println(io, "$(length(set))-element TabularSet") + PrettyTables.pretty_table(io, set.table, backend=Val(:html), standalone=false, + nosubheader=true) + println(io, "
") +end + # Discrete categories #-------------------- @@ -128,6 +144,9 @@ FinDomFunctor(ob_map, ::Nothing, dom::DiscreteCat, codom::Cat) = hom_map(F::FinDomFunctor{<:DiscreteCat}, x) = id(codom(F), ob_map(F, x)) +Base.show(io::IO, C::DiscreteCat{Int,FinSetInt}) = + print(io, "FinCat($(length(C.set)))") + # Finite functions ################## @@ -158,7 +177,8 @@ function FinFunction(f::AbstractVector{Int}, args...; index=false) end end -Sets.show_type(io::IO, ::Type{<:FinFunction}) = print(io, "FinFunction") +Sets.show_type_constructor(io::IO, ::Type{<:FinFunction}) = + print(io, "FinFunction") """ Function out of a finite set. @@ -180,7 +200,8 @@ function FinDomFunction(f::AbstractVector, args...; index=false) end end -Sets.show_type(io::IO, ::Type{<:FinDomFunction}) = print(io, "FinDomFunction") +Sets.show_type_constructor(io::IO, ::Type{<:FinDomFunction}) = + print(io, "FinDomFunction") """ Function in **Set** represented by a vector. @@ -206,8 +227,11 @@ dom(f::FinDomFunctionVector) = FinSet(length(f.func)) (f::FinDomFunctionVector)(x) = f.func[x] -Base.show(io::IO, f::FinDomFunctionVector) = - print(io, "FinDomFunction($(f.func), $(dom(f)), $(codom(f)))") +function Base.show(io::IO, f::FinDomFunctionVector) + print(io, "FinDomFunction($(f.func), ") + Sets.show_domains(io, f) + print(io, ")") +end """ Force evaluation of lazy function or relation. """ @@ -232,7 +256,7 @@ Sets.do_compose(f::FinFunctionVector, g::FinDomFunctionVector) = @cocartesian_monoidal_instance FinSet FinFunction Ob(C::FinCat{Int}) = FinSet(length(ob_generators(C))) -Ob(F::FinCats.FinDomFunctorVector) = FinDomFunction(F.ob_map, Ob(codom(F))) +Ob(F::Functor{<:FinCat{Int}}) = FinDomFunction(collect_ob(F), Ob(codom(F))) # Indexed functions #------------------ @@ -268,8 +292,11 @@ Base.:(==)(f::Union{FinDomFunctionVector,IndexedFinDomFunction}, # Ignore index when comparing for equality. f.func == g.func && codom(f) == codom(g) -Base.show(io::IO, f::IndexedFinDomFunction) = - print(io, "FinDomFunction($(f.func), $(dom(f)), $(codom(f)), index=true)") +function Base.show(io::IO, f::IndexedFinDomFunction) + print(io, "FinDomFunction($(f.func), ") + Sets.show_domains(io, f) + print(io, ", index=true)") +end dom(f::IndexedFinDomFunction) = FinSet(length(f.func)) force(f::IndexedFinDomFunction) = f diff --git a/src/categorical_algebra/Sets.jl b/src/categorical_algebra/Sets.jl index e4d4c08a2..6ea41e2d4 100644 --- a/src/categorical_algebra/Sets.jl +++ b/src/categorical_algebra/Sets.jl @@ -14,6 +14,7 @@ using FunctionWrappers: FunctionWrapper using ...GAT, ..Categories, ..FreeDiagrams, ..Limits using ...Theories: Category import ...Theories: Ob, dom, codom, id, compose, ⋅, ∘ +import ..Categories: show_type_constructor, show_domains import ..Limits: limit, universal # Data types @@ -52,7 +53,7 @@ abstract type SetFunction{Dom <: SetOb, Codom <: SetOb} end SetFunction(f::Function, args...) = SetFunctionCallable(f, args...) SetFunction(::typeof(identity), args...) = SetFunctionIdentity(args...) -show_type(io::IO, ::Type{<:SetFunction}) = print(io, "SetFunction") +show_type_constructor(io::IO, ::Type{<:SetFunction}) = print(io, "SetFunction") """ Function in **Set** defined by a callable Julia object. """ @@ -75,8 +76,10 @@ end function Base.show(io::IO, f::F) where F <: SetFunctionCallable func = f.func.obj[] # Deference FunctionWrapper - show_type(io, F) - print(io, "($(nameof(func)), $(f.dom), $(f.codom))") + show_type_constructor(io, F) + print(io, "($(nameof(func)), ") + show_domains(io, f) + print(io, ")") end """ Identity function in **Set**. @@ -95,8 +98,9 @@ codom(f::SetFunctionIdentity) = f.dom (f::SetFunctionIdentity)(x) = x function Base.show(io::IO, f::F) where F <: SetFunctionIdentity - show_type(io, F) - print(io, "(identity, $(f.dom))") + print(io, "id(") + show_domains(io, f, codomain=false) + print(io, ")") end """ Function in **Set** taking a constant value. diff --git a/test/categorical_algebra/CSetDataStructures.jl b/test/categorical_algebra/CSetDataStructures.jl index ef02175ac..3d620ce2b 100644 --- a/test/categorical_algebra/CSetDataStructures.jl +++ b/test/categorical_algebra/CSetDataStructures.jl @@ -77,22 +77,22 @@ rem_parts!(dds, :X, [1,4]) dds = DDS() add_parts!(dds, :X, 3, Φ=[2,3,3]) s = sprint(show, dds) -@test occursin("DDS:", s) -@test occursin("X = 1:3", s) -@test occursin("Φ : X → X = ", s) -s = sprint(show, dds, context=:compact => true) -@test occursin("DDS:", s) -@test !occursin("\n", s) -@test occursin("X = 3", s) +@test contains(s, "DDS:") +@test contains(s, "X = 1:3") +@test contains(s, "Φ : X → X = ") +s = sprint(show, dds, context=:compact=>true) +@test contains(s, "DDS") +@test !contains(s, "\n") +@test contains(s, "X = 3") s = sprint(show, MIME"text/plain"(), dds) -@test occursin("DDS", s) -@test occursin("X = 1:3", s) -@test occursin("│ X │", s) +@test contains(s, "DDS") +@test contains(s, "X = 1:3") +@test contains(s, "│ X │") s = sprint(show, MIME"text/html"(), dds) @test startswith(s, "
") -@test occursin("", s) +@test contains(s, "
") @test endswith(rstrip(s), "") # Special case of pretty print: empty table. @@ -202,11 +202,11 @@ du = disjoint_union(d, d2) # Pretty printing of data attributes. s = sprint(show, d) -@test occursin("Dendrogram{Int64}:", s) -@test occursin("height : X → R = ", s) +@test contains(s, "Dendrogram{Int64}:") +@test contains(s, "height : X → R = ") s = sprint(show, MIME"text/plain"(), d) -@test occursin("Dendrogram{Int64}", s) +@test contains(s, "Dendrogram{Int64}") # Allow type inheritance for data attributes. d_abs = Dendrogram{Number}() @@ -302,9 +302,9 @@ rem_part!(lset, :X, 1) # Pretty-printing with unitialized data attribute. lset = IndexedLabeledSet{Symbol}() add_part!(lset, :X) -@test occursin("#undef", sprint(show, lset)) -@test occursin("#undef", sprint(show, MIME"text/plain"(), lset)) -@test occursin("#undef", sprint(show, MIME"text/html"(), lset)) +@test contains(sprint(show, lset), "#undef") +@test contains(sprint(show, MIME"text/plain"(), lset), "#undef") +@test contains(sprint(show, MIME"text/html"(), lset), "#undef") # Labeled sets with unique index #------------------------------- diff --git a/test/categorical_algebra/CSets.jl b/test/categorical_algebra/CSets.jl index d7fb280d4..836137870 100644 --- a/test/categorical_algebra/CSets.jl +++ b/test/categorical_algebra/CSets.jl @@ -51,6 +51,7 @@ g, h = path_graph(Graph, 4), cycle_graph(Graph, 2) @test α[:V] isa FinFunction{Int} && α[:E] isa FinFunction{Int} @test α[:V](3) == 1 @test α[:E](2) == 2 +@test startswith(sprint(show, α), "ACSetTransformation((V = ") α′ = CSetTransformation(g, h, V=[1,2,1,2], E=[1,2,1]) @test components(α′) == components(α) @@ -229,6 +230,7 @@ h = path_graph(WeightedGraph{Float64}, 4, E=(weight=[1.,2.,3.],)) @test α isa LooseACSetTransformation @test α[:Weight](10.0) == 5.0 @test is_natural(α) +@test contains(sprint(show, α), "Weight =") g = star_graph(WeightedGraph{Bool}, 3, E=(weight=[true,false],)) α = ACSetTransformation((V=[2,1,3], E=[2,1], Weight=~), g, g) diff --git a/test/categorical_algebra/Categories.jl b/test/categorical_algebra/Categories.jl index 58c84f7b3..5a082532e 100644 --- a/test/categorical_algebra/Categories.jl +++ b/test/categorical_algebra/Categories.jl @@ -9,11 +9,14 @@ using Catlab.CategoricalAlgebra.Sets, Catlab.CategoricalAlgebra.Categories C = TypeCat(FreeCategory.Ob, FreeCategory.Hom) @test Ob(C) == TypeSet(FreeCategory.Ob) +@test sprint(show, C) == "TypeCat($(FreeCategory.Ob), $(FreeCategory.Hom))" -x, y = Ob(FreeCategory, :x, :y) -f = Hom(:f, x, y) F = id(C) @test (dom(F), codom(F)) == (C, C) +@test startswith(sprint(show, F), "id(TypeCat(") + +x, y = Ob(FreeCategory, :x, :y) +f = Hom(:f, x, y) @test F(x) == x @test F(f) == f diff --git a/test/categorical_algebra/FinCats.jl b/test/categorical_algebra/FinCats.jl index 933b952ab..0c3ae5282 100644 --- a/test/categorical_algebra/FinCats.jl +++ b/test/categorical_algebra/FinCats.jl @@ -18,6 +18,7 @@ C = FinCat(g) @test hom(C, 1) == Path(g, 1) @test ob_generators(C) == 1:2 @test hom_generators(C) == 1:3 +@test startswith(sprint(show, C), "FinCat($(Graph)") h = Graph(4) add_edges!(h, [1,1,2,3], [2,3,4,4]) @@ -35,6 +36,7 @@ F = FinFunctor((V=[1,4], E=[[1,3], [2,4]]), C, D) @test codom(F) == D @test is_functorial(F) @test Ob(F) == FinFunction([1,4], FinSet(4)) +@test startswith(sprint(show, F), "FinFunctor($([1,4]),") @test ob_map(F, 2) == 4 @test hom_map(F, 1) == Path(h, [1,3]) @@ -59,6 +61,8 @@ F = FinDomFunctor([FinSet(2), FinSet(3)], [f,g], C) @test is_functorial(F) @test dom(F) == C @test codom(F) isa TypeCat{<:FinSet{Int},<:FinFunction{Int}} +@test startswith(sprint(show, F), "FinDomFunctor(") + @test ob_map(F, 1) == FinSet(2) @test hom_map(F, 2) == g @@ -83,6 +87,9 @@ add_edges!(Δ¹_graph, [1,1,2], [2,2,1]) @test graph(Δ¹) == Δ¹_graph @test length(equations(Δ¹)) == 2 @test !is_free(Δ¹) +s = sprint(show, Δ¹) +@test startswith(s, "FinCat($(Graph)") +@test contains(s, "Path") # Symbolic categories ##################### @@ -104,6 +111,7 @@ end @test first.(hom_generators(Δ¹)) == [:δ₀, :δ₁, :σ₀] @test length(equations(Δ¹)) == 2 @test !is_free(Δ¹) +@test startswith(sprint(show, Δ¹), "FinCat(") # Graph as set-valued functor on a free category. F = FinDomFunctor(TheoryGraph, path_graph(Graph, 3)) @@ -128,6 +136,8 @@ G = FinDomFunctor(TheoryGraph, g) @test codom_ob(α) isa TypeCat{<:FinSet{Int},<:FinFunction{Int}} @test is_natural(α) @test α[:V](3) == 2 +@test startswith(sprint(show, α), "FinTransformation(") + σ = FinTransformation(G, G, V=id(FinSet(2)), E=FinFunction([2,1,4,3])) @test σ⋅σ == FinTransformation(G, G, V=id(FinSet(2)), E=FinFunction(1:4)) @test α⋅σ == FinTransformation(F, G, V=FinFunction([1,2,2]), E=FinFunction([2,4])) diff --git a/test/categorical_algebra/FinSets.jl b/test/categorical_algebra/FinSets.jl index a146b7832..902c4437e 100644 --- a/test/categorical_algebra/FinSets.jl +++ b/test/categorical_algebra/FinSets.jl @@ -22,15 +22,14 @@ set = FinSet(Set(1:2:5)) @test startswith(sshow(set), "FinSet(Set(") # Tables as sets. -set = FinSet((x=[1,3,5], y=['a','b','c'])) +set = FinSet((x=[1,3,5], y=["a","b","c"])) @test length(set) == 3 -@test map(NamedTuple, set) == [(x=1, y='a'), (x=3, y='b'), (x=5, y='c')] +@test map(NamedTuple, set) == [(x=1, y="a"), (x=3, y="b"), (x=5, y="c")] @test startswith(sshow(set), "TabularSet(") -@test startswith(sprint(show, MIME("text/plain"), set), "3-element TabularSet") - -# Discrete categories -##################### +@test startswith(sshow(MIME("text/plain"), set), "3-element TabularSet") +@test startswith(sshow(MIME("text/html"), set), "