Skip to content
Open
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
5 changes: 5 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
AbstractTrees = "0.4"
AMD = "0.5"
Catlab = "0.16"
CuthillMcKee = "0.1"
DataStructures = "0.18"
MLStyle = "0.4"
Metis = "1"
PartialFunctions = "1.1"
julia = "1.7"
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]
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/Fixed Graph 3-Coloring.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/bags increasing example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/example bags and adhesion.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/example colored bags.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/example graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/example tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/fixed graph bags.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/fixed graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/increasing bag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/nodes increasing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
```
187 changes: 187 additions & 0 deletions docs/src/pages/decidingsheaves.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# [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 graph with 5 vertices forming an 'X'.
We first seperate the nodes into bags with adhesions and what our adhesions look like.
Notice that the number of nodes in the graph is equal to the sum of the nodes in each bag minus the adhesions between each bag.

```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 = 1
end

#bag 2
H₂ = @acset Graph begin
V = 3
E = 2
src = [1, 2]
tgt = [2, 3]
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=[2]),
2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[2]),
),
∫(Gₛ)
)

my_decomp1 = StrDecomp(Gₛ, Γₛ)
```

The complete graph is pictured below with the adhesion vertex colored yellow.

<img src='docs\src\assets\example graph.png' width='512' alt='Complete example graph'>

We can recognize the bags that are formed. The bags are pictured as red and blue circles below, overlapping at the yellow adhesion vertex.

<img src='docs\src\assets\example colored bags.png' width='512' alt='Example graph with highlighted bags'>

We can thus split the graph by these bags to form the decomposition shown below.

<img src='docs\src\assets\example bags and adhesion.png' width='512' alt='Example graph split by bags and adhesions'>

and the resulting tree decomposition below.

<img src='docs\src\assets\example tree.png' width='512' alt='Example tree decomposition'>

The algorithm will work by coloring the individual bags then checking if the results will connect at the adhesion point. If no match is found at the adhesion point then there will be a no result.

## Understanding the Complexity

For a c-coloring with tree-width w and total vertices |VT|, we know that to determine a brute force solution we are at complexity O(c<sup>|VT|</sup>). With this dynamic solution we expect complexity of O(c<sup>2w</sup>)|VT|.

We will also consider some other factors. The brute force solution will be better in some edge cases. In a yes instance, if brute force is looking for only 1 solution, a yes instance could get lucky, especially if there are many possible c-colorings. In a yes instance, if brute force is looking for all solutions, the dynamic solution will always be slower in the 2-bag case which can be easily confirmed in the expected complexity calculation. In a no instance, again, the dynamic solution will be slower in the 2-bag case due to the same reason.

## Benchmarking Complexity

As stated above, due to the structure of the algorithm, we expect a complexity of O(c<sup>2w</sup>)|VT| for the dynamic solution. Thus we want to see an exponential decrease in run time as a fixed graph is decomposed into more bags. We also want to show a linear increase in runtime as the number of bags increases linearly and we want to show an exponential increase in runtime as the number of vertices in the graph increases linearly.

First consider the fixed graph case with the following fixed graph.

<img src='docs\src\assets\fixed graph.png' width='512' alt='Example fixed graph'>

We considered the runtime when the graph above is decomposed into smaller and smaller bags as shown below.

<img src='docs\src\assets\fixed graph bags.png' width='512' alt='Example fixed graph decompositions'>

The runtimes for computing if the fixed graph is 3-colorable with varying bag sizes is shown below.

<img src='docs\src\assets\Fixed Graph 3-Coloring.png' width='256' alt='Runtime graph for fixed graph 3-coloring'>

The results show an exponential decrease in runtime as the number of bags is increased on a fixed graph as expected from the complexity calculation above.

Now consider the following benchmarks from increasing the number of bags of a fixed size at a linear rate. When using the following bag of size 4 with some examples below

<img src='docs\src\assets\increasing bag.png' width='128' alt='An example of 1 possible bag used in the benchmarking'>

<img src='docs\src\assets\bags increasing example.png' width='128' alt='Example graphs used in the increasing bag case'>

The following are the results for fixed bags of size 4 and 8 for 2, 3, and 4 coloring tests.

First the results for the fixed bag of size 4 and for a 2-coloring test. The graph shows a linear increase in runtime.

<img src='docs\src\assets\Bags Increasing Linearly of size 4 2 color.png' width='256' alt='Bags increasing size 4 2 coloring results'>

The following results are for the fixed bag of size 4 and for a 3-coloring test. The graph shows a linear increase in runtime.

<img src='docs\src\assets\Bags Increasing Linearly of size 4 3 color.png' width='256' alt='Bags increasing size 4 3 coloring results'>

The following results are for the fixed bag of size 4 and for a 4-coloring test. The graph shows a linear increase in runtime.

<img src='docs\src\assets\Bags Increasing Linearly of size 4 4 color.png' width='256' alt='Bags increasing size 4 4 coloring results'>

The following results are for the fixed bag of size 8 and for a 2-coloring test. The graph shows a linear increase in runtime.

<img src='docs\src\assets\Bags Increasing Linearly of size 8 2 color.png' width='256' alt='Bags increasing size 8 2 coloring results'>

The following results are for the fixed bag of size 8 and for a 3-coloring test. The graph shows a linear increase in runtime.

<img src='docs\src\assets\Bags Increasing Linearly of size 8 3 color.png' width='256' alt='Bags increasing size 8 3 coloring results'>

The following results are for the fixed bag of size 8 and for a 4-coloring test. The graph shows a linear increase in runtime.

<img src='docs\src\assets\Bags Increasing Linearly of size 8 4 color.png' width='256' alt='Bags increasing size 8 4 coloring results'>

These results all confirm the expected complexity from the calculation above.

Now finally consider the following benchmarks for increasing the total vertices in a graph while keeping the number of bags at 2 and the number of adhesions at 1. Below are some examples to illustrate the idea.

<img src='docs\src\assets\nodes increasing.png' width='256' alt='Example of graphs with increasing nodes'>

The following are the results for 2, 3, and 4 coloring tests with similar graphs.

<img src='docs\src\assets\Increase Vertices Linearly 2 color.png' width='256' alt='Nodes increasing results for 2 coloring'>

<img src='docs\src\assets\Increase Vertices Linearly 3 color.png' width='256' alt='Nodes increasing results for 3 coloring'>

<img src='docs\src\assets\Increase Vertices Linearly 4 color.png' width='256' alt='Nodes increasing results for 4 coloring'>
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
Loading
Loading