Skip to content

Tutorials

The Category of Discrete Sub-Unit Intervals,

Definition: Let   be the induced subposet of   spanned by intervals of length at most one. More concretely, is the posetal category consisting of:

  • Objects   and   for  ;

  • Non-identity morphisms    and     for  .

We call the category of discrete subunit-intervals.


While the precise name of this category is the category of discrete subunit-intervals, letter was chose to be reminiscent of as well as to evoke the zigzag shape of this category. For that reason, in TemporalSheaves.jl and across the literature here, this category is also affectionately referred to as the category as a shorthand and to emphasize how we are using the shape of this category for our computational applications.

Examples Using the Category,

Computing and on Time Points

Underlying TemporalSheaves.jl is the SchZigZag Presentation based on Catlab.jl's FreeSchema and the abstract acset type, AbstractZigZag. These foundational abstractions allow flexibility in defining methods based on a zigzag schema category with finite number of time points. Using this basis, TemporalSheaves.jl provides the following functionality to produce a zigzag schema and constructor for number of time points one wants:

TemporalSheaves.zigzag Function
julia
zigzag(time_points::Int; opposite::Bool = true)

Generates a discrete representation of the discrete subunit interval category (referred to as the ZigZag category) for n time points.

Arguments

  • time_points::Int - how many time points to represent

Keyword Arguments

  • opposite::Bool - if true make the underlying schema associated to the ZigZag category opposite; if false, the schema remains unchanged (default: true)

Returns

  • sch::Presentation - a Presentation representing the schema for n number of time_points

  • cons::DataType - an ACSet DataType that represents the constructor built by @acset_type

Examples

Create a ZigZag with 2 time points:

julia
julia> s, c = zigzag(2);
source

Using this function, we can define a zigzag schema on time points as follows:

julia
schema, constructor = zigzag(2; opposite = false);

This produces a schema and constructor object. We can look at the schema produced:

julia
to_graphviz(schema, graph_attrs=Dict(:dpi => "70"), prog = "circo")

Through this schema, we see two time points: t0 and t1. Additionally, the apex of this span, t0_to_t1, is an object in this category that relates information over these two time points via the homomorphisms, t0_inclS and t1_inclF.

However, for our purposes, we want to work with , the opposite category of . TemporalSheaves.jl provides op functionality to compute on time points with the following approach:

julia
schema, constructor = zigzag(2; opposite = true);

Which yields, reversed homomorphisms for this category:

julia
to_graphviz(schema, graph_attrs=Dict(:dpi => "70"), prog = "circo")

Working with Subobjects of

To view the subobjects of this category, one can use Catlab.jl's subobject_classifier method on the constructor object. However, the resulting subobjects are not immediately convenient to visualize or read so TemporalSheaves.jl provides the following method:

TemporalSheaves.subobject_graph_embedding Function
julia
subobject_graph_embedding(cons)

Computes the subobjects associated to a ZigZag and interprets them as graph embeddings associated to the underlying graph of the constructor's schema.

Note: If you want raw subobjects, please see the subobject_classifier function.

Arguments

  • cons::DataType - an ACSet DataType that represents the constructor built by @acset_type

Returns

  • Vector of subobjects as graph embeddings (specifically, NamedGraph's).

Examples

Compute the subobjects on the ZigZag schema

julia
julia> s, c = zigzag(c);

julia> subobs = subobject_graph_embedding(c);
source

Calling this function, we can obtain all subobjects as graph embeddings into the graph of the zigzag category:

julia
subobs = subobject_graph_embedding(constructor)

Furthermore, we can view these subobjects with the following method:

julia
to_graphviz_htl(subobs, style = "", node_labels = true, edge_labels = true)
G n1 t0 n2 t1 n1->n2 t0_to_t1
G n1 t0 n2 t1 n1->n2 t0_to_t1
G n1 t0 n2 t1 n1->n2 t0_to_t1
G n1 t0 n2 t1 n1->n2 t0_to_t1
G n1 t0 n2 t1 n1->n2 t0_to_t1

Furthermore, using these subobject embeddings, one can readily perform propositional logic operations with them such as follows:

julia
A = subobs[4]
B = subobs[1]

C = @withmodel ACSetCategory(constructor()) () begin
    B  A
end

to_graphviz_htl([B, A, C], node_labels = true, edge_labels = true)
G n1 t0 n2 t1 n1->n2 t0_to_t1
G n1 t0 n2 t1 n1->n2 t0_to_t1
G n1 t0 n2 t1 n1->n2 t0_to_t1

You can read the output as     giving .

Adding Data To Finite Structures

Preparing across Time Points

To add data to the objects of a zigzag schema category we construct, we proceed in a very similar way with the non-attributed case. However, this time, we start with the following method:

TemporalSheaves.zigzag! Function
julia
zigzag!(time_points::Int; opposite::Bool = true)

Generates a discrete representation of the discrete subunit interval category (referred to as the ZigZag category) for n time points prepared for combinatorial data.

Arguments

  • time_points::Int - how many time points to represent

Keyword Arguments

  • opposite::Bool - if true make the underlying schema associated to the ZigZag category opposite; if false, the schema remains unchanged (default: true)

Returns

  • sch::Presentation - a Presentation representing the schema for n number of time_points

  • cons::DataType - an ACSet DataType that represents the constructor built by @acset_type

Examples

Create a ZigZag with 2 time points:

julia
julia> s, c = zigzag!(2);
source

Applying the same approach as before, we can generate a schema and constructor object:

julia
schema, constructor = zigzag!(2);

Attributed - on Time Points

Before continuing, let's examine the new graph of this schema:

julia
to_graphviz(schema, graph_attrs=Dict(:dpi => "70"), prog = "circo")

While this schema looks radically different than the previous schema, it is not as different as one may think. For complete clarity, let's look at the definition of this schema:

julia
@present SchZop2! <: SchZigZag begin
  (t0, t1, t0_to_t1)::Ob
  state::Ob

  t0_inclS::Hom(t0, t0_to_t1)
  t1_inclF::Hom(t1, t0_to_t1)

  t0_state::Hom(t0, state)
  t1_state::Hom(t1, state)

  temporal_set::AttrType
  label::AttrType

  t0_temporal_set::Attr(t0, temporal_set)
  t1_temporal_set::Attr(t1, temporal_set)
  t0_to_t1_temporal_set::Attr(t0_to_t1, temporal_set)
  state_label::Attr(state, label)
end

Between the last two schemas, the time point objects and inclusion homomorphsisms are the same. What has been added to this Presentation is attribute information to attach temporal sets to each time point. These are the Attr's denoted as t0_temporal_set, t1_temporal_set, and t0_to_t1_temporal_set. Moreover, the state object has been added so as to record the potential states associated to each element of a temporal set. The state_label Attr associates a given state to labels provided at an instance of this Presentation. The addition of these features to this schema is motivated by applications (see section on applications) and allow full exploitation of the categorical machinery provided by Catlab.jl on such zigzag categories.

Computational Applications of -

To better understand how to use these features, we provide a few examples

Example: Temporal Paths of Flu Transmission

Here, we use the zigzag schema associated to time points to define flu cases.

julia
flu_transmission = @acset constructor{String, String} begin
    state   = 4
    state_labels = ["S", "I", "R", "D"]

    t0      = 2
    t1      = 3
    t0_to_t1 = 2

    t0_inclS = [1, 2]
    t1_inclF = [1, 2]

    t0_state = [1, 2]
    t1_state = [2, 3, 1]

    t0_temporal_set          = ["Jacob", "Sanna"]
    t1_temporal_set          = ["Jacob", "Sanna", "Tino"]
    t0_to_t1_temporal_set    = ["Jacob", "Sanna"]
end;
TemporalSheaves.Zop2!{String, String} {t0:2, t0_to_t1:2, t1:3, state:4, temporal_set:0, label:0}
t0 t0_state t0_temporal_set
1 1 Jacob
2 2 Sanna
t0_to_t1 t0_inclS t1_inclF t0_to_t1_temporal_set
1 1 1 Jacob
2 2 2 Sanna
t1 t1_state t1_temporal_set
1 2 Jacob
2 3 Sanna
3 1 Tino
state state_labels
1 S
2 I
3 R
4 D

We define temporal sets with people named "Jacob", "Sanna", and "Tino". Using this acset, let's interrogate it:

julia
function temporal_path(acs, sch, name::String)
    s = acset_schema(acs)

    t_obs = get_timepoints(sch, type = :discrete)
    span_obs = get_timepoints(sch, type = :cumulative)

    function get_state(ob)
        ob_sym = nameof(ob)

        temporal_attr = only(
            attr for (attr, dom, codom) in attrs(s; from=ob_sym)
            if occursin("temporal_set", string(attr))
        )
        rows = incident(acs, name, temporal_attr)
        isempty(rows) && return nothing

        state_hom = findfirst(
            ((hom, dom, codom),) -> occursin("state", string(hom)),
            collect(homs(s; from=ob_sym))
        )
        isnothing(state_hom) && return nothing
        hom_name = homs(s; from=ob_sym)[state_hom][1]

        subpart(acs, subpart(acs, only(rows), hom_name), :state_labels)
    end

    tps = []
    states = []
    for ob in t_obs
        state = get_state(ob)
        push!(tps, nameof(ob))
        push!(states, state)
    end

    return hcat(tps, states)
end
temporal_path (generic function with 1 method)

Using this function, we can find across the temporal path

julia
temporal_path(flu_transmission, schema, "Jacob")
2×2 Matrix{Any}:
 :t0  "S"
 :t1  "I"

We can also do this across more time points; for example:

julia
schema, constructor = zigzag!(4; opposite = true);

longer_flu_transmission = @acset constructor{String, String} begin
    state        = 4
    state_labels = ["S", "I", "R", "D"]

    # --- t0 ---
    t0              = 3
    t0_state        = [1, 1, 1]                    # S, S, S
    t0_temporal_set = ["Jacob", "Sanna", "Tino"]

    # --- t1 ---
    t1              = 3
    t1_state        = [2, 1, 1]                    # I, S, S
    t1_temporal_set = ["Jacob", "Sanna", "Tino"]

    # --- t2 ---
    t2              = 3
    t2_state        = [3, 2, 1]                    # R, I, S
    t2_temporal_set = ["Jacob", "Sanna", "Tino"]

    # --- t3 ---
    t3              = 3
    t3_state        = [3, 3, 2]                    # R, R, I
    t3_temporal_set = ["Jacob", "Sanna", "Tino"]

    # --- t0_to_t1 span ---
    t0_to_t1              = 3
    t0_to_t1_temporal_set = ["Jacob", "Sanna", "Tino"]
    t0_inclS              = [1, 2, 3]              # all of t0 persists
    t1_inclF              = [1, 2, 3]              # all of t1 persists

    # --- t1_to_t2 span ---
    t1_to_t2              = 3
    t1_to_t2_temporal_set = ["Jacob", "Sanna", "Tino"]
    t1_inclS              = [1, 2, 3]              # all of t1 persists
    t2_inclF              = [1, 2, 3]              # all of t2 persists

    # --- t2_to_t3 span ---
    t2_to_t3              = 3
    t2_to_t3_temporal_set = ["Jacob", "Sanna", "Tino"]
    t2_inclS              = [1, 2, 3]              # all of t2 persists
    t3_inclF              = [1, 2, 3]              # all of t3 persists
end

temporal_path(longer_flu_transmission, schema, "Sanna")
4×2 Matrix{Any}:
 :t0  "S"
 :t1  "S"
 :t2  "I"
 :t3  "R"

Tips and Tricks

For visualization convenience beyond Catlab.jl's to_graphviz method, TemporalSheaves.jl provides the following methods:

TemporalSheaves.to_graphviz_htl Function
julia
to_graphviz_htl(graphs; style="display: flex; flex-wrap: wrap;",
                graph_attrs=Dict(:splines=>"false", :rankdir=>"LR", :dpi=>"70"),
                kwargs...)

Render a collection of Graphviz graphs as a single HypertextLiteral HTML object, suitable for display in Documenter.jl @example blocks.

Each graph is converted to an SVG string and embedded as a raw HTML <div>, laid out according to style.

Arguments

  • graphs - a collection of Graphviz-renderable graph objects

  • style::String - CSS style string applied to the outer wrapper <div>

  • graph_attrs - dictionary of Graphviz graph-level attributes passed to to_graphviz

  • kwargs... - additional keyword arguments forwarded to to_graphviz

Returns

  • A HypertextLiteral HTML object containing all graphs as embedded SVGs

Examples

julia
julia> subobs = subobject_graph_embedding(constructor);
julia> to_graphviz_htl(subobs)
source
TemporalSheaves.display_in_repl Function
julia
display_in_repl(g::Graph; image_gap=true)

Render a Graphviz Graph as a PNG and display it inline in the terminal using Sixel encoding.

Arguments

  • g::Graph - a Graphviz graph to render

  • image_gap::Bool - if true, prints a blank line above the image for visual spacing

Returns

  • nothing (displays the image to stdout as a side effect)

Examples

julia
julia> display_in_repl(my_graph)
julia> display_in_repl(my_graph; image_gap=false)
source