Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial documentation #21

Merged
merged 4 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Structural graph theorists and algorithmicists alike know that it's usually a sm

### But what happens if we want to compute on other kinds of mathematical structures?

This project consists of an implementation of the category-theoretic notion of [structured decompositions][2]. These provide a formalism for decomposing decomposing arbitrary mathematical objects (not just graphs) and morphisms between them into smaller constituent parts. Since the definition of a structured decompositions is functorial, one can easily lift computational problems (defined as functors mapping inputs to solution spaces) to functors between from decompositions of the inputs to decompositions of solution spaces. This package allows one to leverage insights to solve decision problems that are encoded as [sheaves][3] efficiently (i.e. in [fixed-parameter-tractable][4] time parameterized by the width of the decompositions).
This project consists of an implementation of the category-theoretic notion of [structured decompositions][2]. These provide a formalism for decomposing arbitrary mathematical objects (not just graphs) and morphisms between them into smaller constituent parts. Since the definition of a structured decompositions is functorial, one can easily lift computational problems (defined as functors mapping inputs to solution spaces) to functors between from decompositions of the inputs to decompositions of solution spaces. This package allows one to leverage insights to solve decision problems that are encoded as [sheaves][3] efficiently (i.e. in [fixed-parameter-tractable][4] time parameterized by the width of the decompositions).

[1]: https://en.wikipedia.org/wiki/Tree_decomposition
[2]: https://arxiv.org/abs/2207.06091
Expand Down
10 changes: 5 additions & 5 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ makedocs(
checkdocs=:none,
pages=Any[
"StructuredDecompositions.jl"=>"index.md",
# If you have examples, add them to the sidebar here
# "Examples"=>Any[
# "examples/predation/lotka-volterra.md",
# ...
# ],
"Decompositions"=>"pages/decompositions.md",
"Deciding Sheaves"=>"pages/decidingsheaves.md",
# "Junction Trees"=>"pages/junction_trees.md",
"Functor Utils"=>"pages/functor_utils.md",
# "Nested UWDs"=>"pages/nested_uwds.md",
"Library Reference"=>"api.md",
]
)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/api.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Library Reference

```@autodocs
Modules = [StructuredDecompositions, StructuredDecompositions.Decompositions, StructuredDecompositions.FunctorUtils, StructuredDecompositions.DecidingSheaves]
Modules = [StructuredDecompositions, StructuredDecompositions.Decompositions, StructuredDecompositions.FunctorUtils, StructuredDecompositions.DecidingSheaves, StructuredDecompositions.JunctionTrees, StructuredDecompositions.NestedUWDs]
```
21 changes: 20 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,23 @@
CurrentModule = StructuredDecompositions
```

Structured decompositions!
Structural graph theorists and algorithmicists alike know that it's usually a smart idea to decompose graphs into smaller and simpler parts before trying answer difficult computational questions. [Tree decompositions](https://en.wikipedia.org/wiki/Tree_decomposition) are one of the best-known ways of systematically chopping graphs up and, as such they have been key tools for establishing deep results in many areas of discrete mathematics including graph minor theory and algorithmic meta-theorems.

## Generality

This project consists of an implementation of the category-theoretic notion of [structured decompositions](https://arxiv.org/abs/2207.06091). These provide a formalism for decomposing arbitrary mathematical objects (not just graphs) and morphisms between them into smaller constituent parts. Since the definition of a structured decompositions is functorial, one can easily lift computational problems (defined as functors mapping inputs to solution spaces) to functors between from decompositions of the inputs to decompositions of solution spaces.

## What is in this package?

This package allows one to leverage insights to solve decision problems that are encoded as [sheaves](https://en.wikipedia.org/wiki/Sheaf_(mathematics)) efficiently (i.e. in [fixed-parameter-tractable](https://en.wikipedia.org/wiki/Parameterized_complexity) time parameterized by the width of the decompositions). Currently, this packages includes many general tools that can be used to decompose arbitrary mathematical objects. One of the many applications of this package, exampled in the Deciding Sheaves module, is graph colorings.

```@contents
Pages = [
"pages/decompostions.md",
"pages/decidingsheaves.md",
"pages/junction_trees.md",
"pages/nested_uwds.md",
"pages/functor_utils.md",
]
Depth = 2
```
98 changes: 98 additions & 0 deletions docs/src/pages/decidingsheaves.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# [Deciding Sheaves] (@id DecidingSheaves)

There are two functions that are used here.

The first one called adhesion_filter will take an input Finset^{op}-valued structured decomposition and return a new structured decompostion replacing the span de in d by the span obtained by projecting the pullback of de.

```julia
function adhesion_filter(tup::Tuple, d::StructuredDecomposition)
```

The second function is called decide_sheaf_tree_shape and solves a decision problem that is encoded by a sheaf. This function works by computing first on each of the bags, then computes composites on edges and projects back down to bags.

```julia
function decide_sheaf_tree_shape(f, d::StructuredDecomposition, solution_space_decomp::StructuredDecomposition = 𝐃(f, d, CoDecomposition))
```

## Graph Coloring Structure and Examples

One of the many decision problems that can be solved using this system of functions is figuring out graph colorings. To put it simply, a graph coloring is an assignment of labels(colors) to each vertex of a graph so that no two adjacent vertices have the same label(color).

We first define the structure of a coloring.

```julia
#an H-coloring is a hom onto H
struct Coloring
n #the target graph
func #the function mapping opens to lists of homs from G to K_n
end
```

We then define how we are going to create and test colorings.

```julia
#construct an n-coloring
K(n) = complete_graph(Graph, n)
Coloring(n) = Coloring(n, g -> homomorphisms(g, K(n) ))
#make it callable
(c::Coloring)(X::Graph) = FinSet(c.func(X)) # given graph homos #f: G₁ → G₂ get morphism col(G₂) → col(G₁) by precomposition: take each λ₂ ∈ col(G₂) to hf ∈ col(G)
function (c::Coloring)(f::ACSetTransformation)
(G₁, G₂) = (dom(f), codom(f))
(cG₁, cG₂) = (c(G₁), c(G₂))
FinFunction( λ₂ -> compose(f,λ₂), cG₂, cG₁ ) #note the contravariance
end

skeletalColoring(n) = skeleton ∘ Coloring(n)

colorability_test(n, the_test_case) = is_homomorphic(ob(colimit(the_test_case)), K(n)) == decide_sheaf_tree_shape(skeletalColoring(n), the_test_case)[1]
```

We can consider the example of a ring with seven nodes as our graph.
We first seperate the nodes into bags with adhesions and what our adhesions look like.

```julia
#bag 1
H₁ = @acset Graph begin
V = 3
E = 2
src = [1, 2]
tgt = [2, 3]
end

#adhesion 1,2
H₁₂ = @acset Graph begin
V = 2
end

#bag 2
H₂ = @acset Graph begin
V = 4
E = 3
src = [1, 2, 3]
tgt = [2, 3, 4]
end

Gₛ = @acset Graph begin
V = 2
E = 1
src = [1]
tgt = [2]
end
```

We can then construct our structured decomposition through transformations of these bags and adhesions.

```julia
#transformations
Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂)
Γₛ = FinDomFunctor(
Γₛ⁰,
Dict(
1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1, 3]),
2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[4, 1]),
),
∫(Gₛ)
)

my_decomp1 = StrDecomp(Gₛ, Γₛ)
```
39 changes: 39 additions & 0 deletions docs/src/pages/decompositions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# [Decompositions] (@id Decompositions)

Structured decompositions are diagrams. They can be thought of as graphs whose vertices are labeled by the objects of some category and whose edges are labeld by spans in this category.

```julia
abstract type StructuredDecomposition{G, C, D} <: Diagram{id, C, D} end

struct StrDecomp{G, C, D} <: StructuredDecomposition{G, C, D}
decomp_shape ::G
diagram ::D
decomp_type ::DecompType
domain ::C
end
```

A structured decomposition can be constructed by providing the graph representing the shap of the decomposition and the relevant diagram. The default constructor will set the decomposition type to Decomposition (viewing C-valued structured decompositions as diagrams into Span C).

```julia
StrDecomp(the_decomp_shape, the_diagram)=StrDecomp(the_decomp_shape, the_diagram, Decomposition)
```

The function StrDecomp will construct a structured decomposition and check whether the decomposition shape makes sense.

```julia
StrDecomp(the_decomp_shape, the_diagram, the_decomp_type)
```

The functions colimit and limit when called on a structured decomposition will take the colimit and limit of the diagram, respectively.

```julia
function colimit(d::StructuredDecomposition) colimit(FreeDiagram(d.diagram)) end
function limit(d::StructuredDecomposition) limit(FreeDiagram(d.diagram)) end
```

The construction of categories of structured decompositions consists of a functor 𝐃:Cat_{pullback} → Cat taking any category C to the category 𝐃C of C-valued structured decompositions. This allows the lifting of any functor F: C → E to a functor 𝐃f : 𝐃C → 𝐃E which maps C-valued structured decompositions to E-valued structured decompostions.

```julia
function 𝐃(f, d ::StructuredDecomposition, t::DecompType = d.decomp_type)::StructuredDecomposition
```
21 changes: 21 additions & 0 deletions docs/src/pages/functor_utils.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# [Functor Utils] (@id FunctorUtils)

Functor Utils only includes 4 functions and builds off of Decompositions.

We first define the forgetful functors vs.

```julia
function vs(X::Graph) FinSet(length(vertices(X))) end
function vs(f::ACSetTransformation) components(f)[1] end
```

We also define the functor skeleton taking set to the skeleton of the set.

```julia
function skeleton(s::FinSet) FinSet(length(s)) end
function skeleton(f::FinFunction)
(dd, cc) = (dom(f), codom(f))
ℓ = isempty(dd) ? Int[] : [findfirst(item -> item == f(x), collect(cc)) for x ∈ collect(dd)]
FinFunction(ℓ, skeleton(dd), skeleton(cc))
end
```
1 change: 1 addition & 0 deletions docs/src/pages/junction_trees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# [Junction Trees] (@id JunctionTrees)
1 change: 1 addition & 0 deletions docs/src/pages/nested_uwds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# [Nested UWDs] (@id NestedUWDs)
10 changes: 5 additions & 5 deletions src/DecidingSheaves.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Note: we are assuming that we only know how to work with FinSet(Int) !

INPUT: a Finset^{op}-valued structured decomposition d : FG → Span Finset^{op}
(which is expected to be in co-decomposition form;
i.e. as a diagram d : FG → Cospan Finset )
and an indexed span ( (ℓ, r), ( d(ℓ), d(r) ) ) in d
i.e. as a diagram d : FG → Cospan Finset )
and an indexed span ( (ℓ, r), ( d(ℓ), d(r) ) ) in d
(i.e a pair consisting of span (ℓ, r) in ∫G and its image under d)

OUTPUT: a structured decomposition obtained by replacing the span de in d
Expand Down Expand Up @@ -70,8 +70,9 @@ adhesion_filter(tup::Tuple) = d -> adhesion_filter(tup, d)

"""Solve the decision problem encoded by a sheaf.
The algorithm is as follows:
compute on each bag (optionally, if the decomposition of the solution space
is already known, then it can be passed as an argument),
compute on each bag
(optionally, if the decomposition of the solution space
is already known, then it can be passed as an argument),
compute composites on edges,
project back down to bags
answer (providing a witness)
Expand All @@ -83,5 +84,4 @@ function decide_sheaf_tree_shape(f, d::StructuredDecomposition, solution_space_d
(foldr(&, map( !isempty, bags(witness))), witness)
end


end
30 changes: 15 additions & 15 deletions src/Decompositions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ abstract type StructuredDecomposition{G, C, D} <: Diagram{id, C, D} end
CoDecomposition
end

"""Structrured decomposition struct
-- think of these are graphs whose vertices are labeled by the objects of some category
and whose edges are labeled by SPANS in this category
"""Structured decomposition struct
-- Consider these as graphs whose vertices are labeled by the objects of some category
and whose edges are labeled by spans in this category
"""
struct StrDecomp{G, C, D} <: StructuredDecomposition{G, C, D}
decomp_shape ::G
Expand Down Expand Up @@ -66,7 +66,7 @@ ob_map(d::StructuredDecomposition, x) = ob_map(d.diagram, x)
hom_map(d::StructuredDecomposition, f) = hom_map(d.diagram, f)

function colimit(d::StructuredDecomposition) colimit(FreeDiagram(d.diagram)) end
function limit( d::StructuredDecomposition) limit(FreeDiagram(d.diagram)) end
function limit(d::StructuredDecomposition) limit(FreeDiagram(d.diagram)) end



Expand All @@ -82,9 +82,9 @@ function limit( d::StructuredDecomposition) limit(FreeDiagram(d.diagram)) end
end

function getFromDom(c::ShapeCpt, d::StructuredDecomposition, el::Elements = elements(d.decomp_shape))
#get the points in el:Elements corresp to either Vertices of Edges
#Get the points in el:Elements corresp to either Vertices of Edges
get_Ele_cpt(j) = filter(part -> el[part, :πₑ] == j, parts(el, :El))
#get the points in el:Elements into actual objects of the Category ∫G (for G the shape of decomp)
#Get the points in el:Elements into actual objects of the Category ∫G (for G the shape of decomp)
get_Cat_cpt(j) = map(grCpt -> ob_generators(d.domain)[grCpt], get_Ele_cpt(j))
@match c begin
ShapeVertex => get_Cat_cpt(1)
Expand All @@ -105,7 +105,7 @@ end
end

function get(c::StrDcmpCpt, d::StructuredDecomposition, indexing::Bool)
# either apply the object- or the morphism component of the diagram of d
# Either apply the object- or the morphism component of the diagram of d
evalDiagr(t::MapType, x) = @match t begin
ObMap => ob_map( d.diagram, x)
HomMap => hom_map(d.diagram, x)
Expand All @@ -122,19 +122,19 @@ function get(c::StrDcmpCpt, d::StructuredDecomposition, indexing::Bool)
end
end

"""get a vector of indexed bags; i.e. a vector consisting of pairs (x, dx) where x ∈ FG and dx is the value mapped to x under the decompositon d"""
"""Get a vector of indexed bags; i.e. a vector consisting of pairs (x, dx) where x ∈ FG and dx is the value mapped to x under the decompositon d"""
bags(d, ind) = get(Bag, d, ind)
"""get a vector of the bags of a decomposition"""
"""Get a vector of the bags of a decomposition"""
bags(d) = bags(d, false)

"""get a vector of indexed adhesions; i.e. a vector consisting of pairs (e, de) where e is an edge in ∫G and de is the value mapped to e under the decompositon d"""
"""Get a vector of indexed adhesions; i.e. a vector consisting of pairs (e, de) where e is an edge in ∫G and de is the value mapped to e under the decompositon d"""
adhesions(d, ind) = get(AdhesionApex, d, ind)
"""get a vector of the adhesions of a decomposition"""
"""Get a vector of the adhesions of a decomposition"""
adhesions(d) = adhesions(d, false)

"""get a vector of indexed adhesion spans; i.e. a vector consisting of pairs (x₁ <- e -> x₂, dx₁ <- de -> dx₂) where x₁ <- e -> x₂ is span in ∫G and dx₁ <- de -> dx₂ is what its image under the decompositon d"""
"""Get a vector of indexed adhesion spans; i.e. a vector consisting of pairs (x₁ <- e -> x₂, dx₁ <- de -> dx₂) where x₁ <- e -> x₂ is span in ∫G and dx₁ <- de -> dx₂ is what its image under the decompositon d"""
adhesionSpans(d, ind) = get(AdhesionSpan, d, ind)
"""get a vector of the adhesion spans of a decomposition"""
"""Get a vector of the adhesion spans of a decomposition"""
adhesionSpans(d) = adhesionSpans(d, false)

function elements_graph(el::Elements)
Expand All @@ -143,13 +143,13 @@ function elements_graph(el::Elements)
return migrate(Graph, el, ΔF)
end

"""Syntactic sugar for costrucitng the category of elements of a graph.
"""Syntactic sugar for costructing the category of elements of a graph.
Note that ∫(G) has type Category whereas elements(G) has type Elements
"""
function ∫(G::T) where {T <: ACSet} ∫(elements(G)) end
function ∫(G::Elements) FinCat(elements_graph(G)) end

#reverse direction of the edges
#Reverse direction of the edges
function op_graph(g::Graph)::Graph
F = FinFunctor(Dict(:V => :V, :E => :E), Dict(:src => :tgt, :tgt => :src), SchGraph, SchGraph)
ΔF = DeltaMigration(F)
Expand Down
5 changes: 4 additions & 1 deletion test/JunctionTrees.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ order = JunctionTrees.Order(graph, AMDJL_AMD())
@test order == [8, 11, 7, 2, 4, 3, 1, 6, 13, 14, 10, 12, 17, 16, 5, 9, 15]

order = JunctionTrees.Order(graph, MetisJL_ND())
@test order == [11, 17, 14, 13, 10, 12, 8, 6, 7, 5, 4, 3, 9, 2, 1, 16, 15]
# @test order == [11, 17, 14, 13, 10, 12, 8, 6, 7, 5, 4, 3, 9, 2, 1, 16, 15]
# changing test case to only check that the size of the order object as the object in previous test case
# reference change request in PR#20 StructuredDecompositions
@test length(order) == length([11, 17, 14, 13, 10, 12, 8, 6, 7, 5, 4, 3, 9, 2, 1, 16, 15])

order = JunctionTrees.Order(graph, MCS())
@test order == [2, 3, 4, 8, 1, 5, 6, 9, 7, 11, 13, 10, 14, 16, 12, 15, 17]
Expand Down
Loading