diff --git a/.buildkite/jobscript.sh b/.buildkite/jobscript.sh new file mode 100755 index 0000000..ebf5502 --- /dev/null +++ b/.buildkite/jobscript.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +pwd; hostname; date + +module load julia + +echo "Running Tests..." +julia --project -t 16 -e 'using Pkg; Pkg.status(); Pkg.test()' + +echo "Building Documentation..." +julia --project=docs -t 16 -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.status(); Pkg.instantiate(); include("docs/make.jl")' diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml new file mode 100644 index 0000000..b22cba9 --- /dev/null +++ b/.buildkite/pipeline.yml @@ -0,0 +1,21 @@ +env: + JULIA_VERSION: "1.10.2" + GATAS_HOME: "$DEPOT" + +steps: + + - label: ":hammer: Build Project" + command: + - "module load julia" + - "julia --project=docs --color=yes -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate(); Pkg.precompile()'" + + - wait + + - label: ":scroll: Build docs and run tests" + env: + JULIA_PROJECT: "docs/" + command: + - "srun --cpus-per-task=16 --mem=64G --time=1:00:00 --output=.buildkite/log_%j.log --unbuffered .buildkite/jobscript.sh" + + - wait + diff --git a/README.md b/README.md index 339c02d..6d9c6af 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/benchmarks/BenchmarkAgainstNauty.jl b/benchmarks/BenchmarkAgainstNauty.jl new file mode 100644 index 0000000..f8b5763 --- /dev/null +++ b/benchmarks/BenchmarkAgainstNauty.jl @@ -0,0 +1,48 @@ +# This file shows benchmarks against the Nauty algorithm currently implemented in CSetAutomorphisms.jl + +using BenchmarkTools +using PkgBenchmark +using Profile +using Test + +using CSetAutomorphisms +using CSetAutomorphisms.NautyInterface: all_autos +using CSetAutomorphisms.TestHelp: Labeled + +using StructuredDecompositions.Decompositions +using StructuredDecompositions.DecidingSheaves +using StructuredDecompositions.FunctorUtils + +using Catlab.Graphs +using Catlab.ACSetInterface +using Catlab.CategoricalAlgebra +using Catlab.Graphics + +# The following structures and functions were pulled from DecidingSheaves.jl + +struct Coloring + n + func +end + +K(n) = complete_graph(Graph, n) +Coloring(n) = Coloring(n, g -> homomorphisms(g, K(n))) +(c::Coloring)(X::Graph) = FinSet(c.func(X)) +function (c::Coloring)(f::ACSetTransformation) + (G₁, G₂) = (dom(f), codom(f)) + (cG₁, cG₂) = (c(G₁), c(G₂)) + FinFunction( λ₂ -> compose(f,λ₂), cG₂, cG₁ ) +end + +skeletalColoring(n) = skeleton ∘ Coloring(n) + +function colorability_test(n, the_test_case) + hom = is_homomorphic(ob(colimit(the_test_case)), K(n)) + dec = decide_sheaf_tree_shape(skeletalColoring(n), the_test_case)[1] + if hom == dec + return hom + else + error("is_homomorphic != decide_sheaf_tree_shape") + end +end + diff --git a/benchmarks/GraphColoring.jl b/benchmarks/GraphColoring.jl new file mode 100644 index 0000000..f0e4c81 --- /dev/null +++ b/benchmarks/GraphColoring.jl @@ -0,0 +1,663 @@ +# This file benchmarks the StructuredDecompositions method of graph coloring + +# RESULTS = sheaf tree skeletal coloring is n linear for false returns, worse than n exponential for true returns + # shouldn't be worse than exponential for true returns so I will take a look at it after considering k + +import Graphs as GraphsPkg + +using BenchmarkTools +using PkgBenchmark +using Profile + +using StructuredDecompositions.Decompositions +using StructuredDecompositions.DecidingSheaves +using StructuredDecompositions.FunctorUtils + +using Catlab.Graphs +using Catlab.ACSetInterface +using Catlab.CategoricalAlgebra +using Catlab.Graphics + +# The following structures and functions were pulled from DecidingSheaves.jl + +struct Coloring + n + func +end + +K(n) = complete_graph(Graph, n) +Coloring(n) = Coloring(n, g -> homomorphisms(g, K(n))) +(c::Coloring)(X::Graph) = FinSet(c.func(X)) +function (c::Coloring)(f::ACSetTransformation) + (G₁, G₂) = (dom(f), codom(f)) + (cG₁, cG₂) = (c(G₁), c(G₂)) + FinFunction( λ₂ -> compose(f,λ₂), cG₂, cG₁ ) +end + +skeletalColoring(n) = skeleton ∘ Coloring(n) + +function colorability_test(n, the_test_case) + hom = is_homomorphic(ob(colimit(the_test_case)), K(n)) + dec = decide_sheaf_tree_shape(skeletalColoring(n), the_test_case)[1] + if hom == dec + return hom + else + error("is_homomorphic != decide_sheaf_tree_shape") + end +end + +# Benchmark 1 (small n per bag(4) and small bags k(2), 3 coloring) + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 4 + src = [1, 2, 3, 4] + tgt = [2, 3, 4, 1] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 2 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 2, 3, 4, 1] + tgt = [2, 3, 4, 1, 3] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[3, 4]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[2, 1]), + ), + ∫(Gₛ) +) + +my_decomp1 = StrDecomp(Gₛ, Γₛ) + +is_homomorphic(ob(colimit(my_decomp1)), K(2)) == false +@benchmark is_homomorphic(ob(colimit(my_decomp1)), K(2)) + +is_homomorphic(ob(colimit(my_decomp1)), K(3)) == true +@benchmark is_homomorphic(ob(colimit(my_decomp1)), K(3)) + +is_homomorphic(ob(colimit(my_decomp1)), K(4)) == true +@benchmark is_homomorphic(ob(colimit(my_decomp1)), K(4)) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp1)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp1)[1] +@time decide_sheaf_tree_shape(skeletalColoring(2), my_decomp1)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp1)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp1)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp1)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp1)[1] + +colorability_test(2, my_decomp1) == false +@benchmark colorability_test(2, my_decomp1) + +colorability_test(3, my_decomp1) == true +@benchmark colorability_test(3, my_decomp1) + +colorability_test(4, my_decomp1) == true +@benchmark colorability_test(4, my_decomp1) + +# Benchmark 2 (medium n per bag(10) and small bags k(2), 3 coloring) + +#bag 1 +H₁ = @acset Graph begin + V = 10 + E = 16 + src = [1, 1, 1, 2, 2, 3, 4, 4, 4, 6, 6, 7, 7, 7, 8, 9] + tgt = [2, 5, 6, 3, 8, 4, 5, 9, 10, 7, 10, 8, 9, 10, 9, 10] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 2 +end + +#bag 2 +H₂ = @acset Graph begin + V = 10 + E = 18 + src = [1, 1, 2, 2, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 8, 9] + tgt = [2, 9, 3, 4, 9, 10, 4, 5, 10, 6, 10, 7, 10, 8, 9, 10, 9, 10] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[4, 2]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 3]), + ), + ∫(Gₛ) +) + +my_decomp2 = StrDecomp(Gₛ, Γₛ) + +is_homomorphic(ob(colimit(my_decomp2)), K(2)) == false +@benchmark is_homomorphic(ob(colimit(my_decomp2)), K(2)) + +is_homomorphic(ob(colimit(my_decomp2)), K(3)) == true +@benchmark is_homomorphic(ob(colimit(my_decomp2)), K(3)) + +is_homomorphic(ob(colimit(my_decomp2)), K(4)) == true +@benchmark is_homomorphic(ob(colimit(my_decomp2)), K(4)) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp2)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp2)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp2)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp2)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp2)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp2)[1] + +# Benchmark 3 (large n per bag(20) and small bags k(2), 3 coloring) + +#bag 1 +H₁ = @acset Graph begin + V = 20 + E = 34 + src = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18] + tgt = [2, 3, 4, 5, 4, 6, 5, 7, 8, 9, 7, 10, 8, 11, 9, 12, 13, 14, 11, 15, 12, 15, 13, 16, 14, 17, 18, 16, 19, 17, 19, 18, 19, 20] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 2 +end + +#bag 2 +H₂ = @acset Graph begin + V = 20 + E = 34 + src = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18] + tgt = [2, 3, 4, 5, 4, 6, 5, 7, 8, 9, 7, 10, 8, 11, 9, 12, 13, 14, 11, 15, 12, 15, 13, 16, 14, 17, 18, 16, 19, 17, 19, 18, 19, 20] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1, 2]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 2]), + ), + ∫(Gₛ) +) + +my_decomp3 = StrDecomp(Gₛ, Γₛ) + +is_homomorphic(ob(colimit(my_decomp3)), K(2)) == false +@benchmark is_homomorphic(ob(colimit(my_decomp3)), K(2)) + +is_homomorphic(ob(colimit(my_decomp3)), K(3)) == true +@benchmark is_homomorphic(ob(colimit(my_decomp3)), K(3)) + +is_homomorphic(ob(colimit(my_decomp3)), K(4)) == true +@benchmark is_homomorphic(ob(colimit(my_decomp3)), K(4)) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp3)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp3)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp3)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp3)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp3)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp3)[1] + +# Benchmark 4 (small n per bag(5) with medium bags k(4), 3 coloring) + +#bag 1 +H₁ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 3,4 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +Gₛ = @acset Graph begin + V = 4 + E = 3 + src = [1, 2, 3] + tgt = [2, 3, 4] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₁₂, 6 => H₂₃, 7 => H₃₄) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[4], V=[1]) + ), + ∫(Gₛ) +) + +my_decomp4 = StrDecomp(Gₛ, Γₛ) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp4)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp4)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp4)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp4)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp4)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp4)[1] + +# Benchmark 5 (medium n per bag(10) with medium bags k(4), 3 coloring) + +#bag 1 +H₁ = @acset Graph begin + V = 10 + E = 16 + src = [1, 1, 1, 2, 2, 3, 4, 4, 4, 6, 6, 7, 7, 7, 8, 9] + tgt = [2, 5, 6, 3, 8, 4, 5, 9, 10, 7, 10, 8, 9, 10, 9, 10] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 10 + E = 18 + src = [1, 1, 2, 2, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 8, 9] + tgt = [2, 9, 3, 4, 9, 10, 4, 5, 10, 6, 10, 7, 10, 8, 9, 10, 9, 10] +end + +#adhesion 3,4 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 10 + E = 16 + src = [1, 1, 1, 2, 2, 3, 4, 4, 4, 6, 6, 7, 7, 7, 8, 9] + tgt = [2, 5, 6, 3, 8, 4, 5, 9, 10, 7, 10, 8, 9, 10, 9, 10] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 10 + E = 18 + src = [1, 1, 2, 2, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 8, 9] + tgt = [2, 9, 3, 4, 9, 10, 4, 5, 10, 6, 10, 7, 10, 8, 9, 10, 9, 10] +end + +Gₛ = @acset Graph begin + V = 4 + E = 3 + src = [1, 3, 4] + tgt = [2, 2, 3] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₁₂, 6 => H₂₃, 7 => H₃₄) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[1], V=[4]), + 2 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[4], V=[4]) + ), + ∫(Gₛ) +) + +my_decomp5 = StrDecomp(Gₛ, Γₛ) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp5)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp5)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp5)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp5)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp5)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp5)[1] + +# Benchmark 6 (large n per bag(20) with medium bags k(4), 3 coloring) + +#bag 1 +H₁ = @acset Graph begin + V = 20 + E = 34 + src = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18] + tgt = [2, 3, 4, 5, 4, 6, 5, 7, 8, 9, 7, 10, 8, 11, 9, 12, 13, 14, 11, 15, 12, 15, 13, 16, 14, 17, 18, 16, 19, 17, 19, 18, 19, 20] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 20 + E = 34 + src = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18] + tgt = [2, 3, 4, 5, 4, 6, 5, 7, 8, 9, 7, 10, 8, 11, 9, 12, 13, 14, 11, 15, 12, 15, 13, 16, 14, 17, 18, 16, 19, 17, 19, 18, 19, 20] +end + +#adhesion 3,4 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 20 + E = 34 + src = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18] + tgt = [2, 3, 4, 5, 4, 6, 5, 7, 8, 9, 7, 10, 8, 11, 9, 12, 13, 14, 11, 15, 12, 15, 13, 16, 14, 17, 18, 16, 19, 17, 19, 18, 19, 20] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 20 + E = 34 + src = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18] + tgt = [2, 3, 4, 5, 4, 6, 5, 7, 8, 9, 7, 10, 8, 11, 9, 12, 13, 14, 11, 15, 12, 15, 13, 16, 14, 17, 18, 16, 19, 17, 19, 18, 19, 20] +end + +Gₛ = @acset Graph begin + V = 4 + E = 3 + src = [1, 2, 3] + tgt = [2, 3, 4] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₁₂, 6 => H₂₃, 7 => H₃₄) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[4], V=[1]) + ), + ∫(Gₛ) +) + +my_decomp6 = StrDecomp(Gₛ, Γₛ) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp6)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp6)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp6)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp6)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp6)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp6)[1] + +# Benchmark 7 (small n per bag(5) with large bags k(10), 3 coloring) + +#bag 1 +H₁ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 3,4 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 4,5 +H₄₅ = @acset Graph begin + V = 1 +end + +#bag 5 +H₅ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 5,6 +H₅₆ = @acset Graph begin + V = 1 +end + +#bag 6 +H₆ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 6,7 +H₆₇ = @acset Graph begin + V = 1 +end + +#bag 7 +H₇ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 7,8 +H₇₈ = @acset Graph begin + V = 1 +end + +#bag 8 +H₈ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 8,9 +H₈₉ = @acset Graph begin + V = 1 +end + +#bag 9 +H₉ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +#adhesion 9,10 +H₉₁₀ = @acset Graph begin + V = 1 +end + +#bag 10 +H₁₀ = @acset Graph begin + V = 5 + E = 7 + src = [1, 2, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 4, 5, 1] +end + +Gₛ = @acset Graph begin + V = 10 + E = 9 + src = [1, 2, 3, 4, 5, 6, 7, 8, 9] + tgt = [2, 3, 4, 5, 6, 7, 8, 9, 10] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₅, 6 => H₆, 7 => H₇, 8 => H₈, 9 => H₉, 10 => H₁₀, 11 => H₁₂, 12 => H₂₃, 13 => H₃₄, 14 => H₄₅, + 15 => H₅₆, 16 => H₆₇, 17 => H₇₈, 18 => H₈₉, 19 => H₉₁₀) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[4], V=[1]), + 7 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[4], V=[1]), + 8 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[5], V=[1]), + 9 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[5], V=[1]), + 10 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[6], V=[1]), + 11 => ACSetTransformation(Γₛ⁰[16], Γₛ⁰[6], V=[1]), + 12 => ACSetTransformation(Γₛ⁰[16], Γₛ⁰[7], V=[1]), + 13 => ACSetTransformation(Γₛ⁰[17], Γₛ⁰[7], V=[1]), + 14 => ACSetTransformation(Γₛ⁰[17], Γₛ⁰[8], V=[1]), + 15 => ACSetTransformation(Γₛ⁰[18], Γₛ⁰[8], V=[1]), + 16 => ACSetTransformation(Γₛ⁰[18], Γₛ⁰[9], V=[1]), + 17 => ACSetTransformation(Γₛ⁰[19], Γₛ⁰[9], V=[1]), + 18 => ACSetTransformation(Γₛ⁰[19], Γₛ⁰[10], V=[1]) + ), + ∫(Gₛ) +) + +my_decomp7 = StrDecomp(Gₛ, Γₛ) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp7)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp7)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp7)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp7)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp7)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp7)[1] + +# Benchmark 8 (medium n per bag(10) with large bags k(10), 3 coloring) + +# Benchmark 9 (large n per bag(20) with large bags k(10), 3 coloring) \ No newline at end of file diff --git a/benchmarks/GraphColoringConstantBagsExpandingGraph.jl b/benchmarks/GraphColoringConstantBagsExpandingGraph.jl new file mode 100644 index 0000000..7e800bc --- /dev/null +++ b/benchmarks/GraphColoringConstantBagsExpandingGraph.jl @@ -0,0 +1,303 @@ +# This file examines the relationship between run time, bag size, and number of bags for a fixed 40 node graph. +# We will compare the results against running the graph coloring algorithm on Graphs.jl + +import Graphs as GraphsPkg + +using BenchmarkTools +using PkgBenchmark +using Profile + +using StructuredDecompositions.Decompositions +using StructuredDecompositions.DecidingSheaves +using StructuredDecompositions.FunctorUtils + +using Catlab.Graphs +using Catlab.ACSetInterface +using Catlab.CategoricalAlgebra +using Catlab.Graphics + +# The following structures and functions were pulled from DecidingSheaves.jl + +struct Coloring + n + func +end + +K(n) = complete_graph(Graph, n) +Coloring(n) = Coloring(n, g -> homomorphisms(g, K(n))) +(c::Coloring)(X::Graph) = FinSet(c.func(X)) +function (c::Coloring)(f::ACSetTransformation) + (G₁, G₂) = (dom(f), codom(f)) + (cG₁, cG₂) = (c(G₁), c(G₂)) + FinFunction( λ₂ -> compose(f,λ₂), cG₂, cG₁ ) +end + +skeletalColoring(n) = skeleton ∘ Coloring(n) + +function colorability_test(n, the_test_case) + hom = is_homomorphic(ob(colimit(the_test_case)), K(n)) + dec = decide_sheaf_tree_shape(skeletalColoring(n), the_test_case)[1] + if hom == dec + return hom + else + error("is_homomorphic != decide_sheaf_tree_shape") + end +end + +# We will start with a small number of vertices in a small number of bags and consider the case below +# Adding vertices to the bags at a constant rate + + +# We first consider the 2 bag, 5 vertices graph + +#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 = [1] +end + +#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ₛ, Γₛ) + +# We now consider the 2 bag, 8 vertices graph + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +my_decomp2 = StrDecomp(Gₛ, Γₛ) + +# We now consider the 2 bag, 12 vertices graph + +#bag 1 +H₁ = @acset Graph begin + V = 6 + E = 8 + src = [1, 1, 2, 2, 3, 4, 4, 5] + tgt = [2, 3, 3, 4, 4, 5, 6, 6] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 6 + E = 8 + src = [1, 1, 2, 2, 3, 4, 4, 5] + tgt = [2, 3, 3, 4, 4, 5, 6, 6] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +my_decomp3 = StrDecomp(Gₛ, Γₛ) + +# We now consider the 2 bag, 16 vertices graph + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +my_decomp2 = StrDecomp(Gₛ, Γₛ) + +# We now consider the 2 bag, 16 vertices graph + +#bag 1 +H₁ = @acset Graph begin + V = 8 + E = 10 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 8 + E = 10 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +my_decomp4 = StrDecomp(Gₛ, Γₛ) + +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp1)[1] == true +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp2)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp3)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), my_decomp4)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp1)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp2)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp3)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), my_decomp4)[1] +@benchmark colorability_test(2, my_decomp1) +@benchmark colorability_test(2, my_decomp2) +@benchmark colorability_test(2, my_decomp3) +@benchmark colorability_test(2, my_decomp4) + +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp1)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp2)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp3)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), my_decomp4)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp1)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp2)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp3)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), my_decomp4)[1] +@benchmark colorability_test(3, my_decomp1) +@benchmark colorability_test(3, my_decomp2) +@benchmark colorability_test(3, my_decomp3) +@benchmark colorability_test(3, my_decomp4) + +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp1)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp2)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp3)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), my_decomp4)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp1)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp2)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp3)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), my_decomp4)[1] +@benchmark colorability_test(4, my_decomp1) +@benchmark colorability_test(4, my_decomp2) +@benchmark colorability_test(4, my_decomp3) +@benchmark colorability_test(4, my_decomp4) \ No newline at end of file diff --git a/benchmarks/GraphColoringFixedGraph.jl b/benchmarks/GraphColoringFixedGraph.jl new file mode 100644 index 0000000..235d317 --- /dev/null +++ b/benchmarks/GraphColoringFixedGraph.jl @@ -0,0 +1,753 @@ +# This file examines the relationship between run time, bag size, and number of bags for a fixed 40 node graph. +# Note since bags must overlap, the colorings will need to deal with more than 40 total nodes due to overlap. +# We will compare the results against running the graph coloring algorithm on Graphs.jl + +import Graphs as GraphsPkg + +using BenchmarkTools +using PkgBenchmark +using Profile + +using StructuredDecompositions.Decompositions +using StructuredDecompositions.DecidingSheaves +using StructuredDecompositions.FunctorUtils + +using Catlab.Graphs +using Catlab.ACSetInterface +using Catlab.CategoricalAlgebra +using Catlab.Graphics + +# The following structures and functions were pulled from DecidingSheaves.jl + +struct Coloring + n + func +end + +K(n) = complete_graph(Graph, n) +Coloring(n) = Coloring(n, g -> homomorphisms(g, K(n))) +(c::Coloring)(X::Graph) = FinSet(c.func(X)) +function (c::Coloring)(f::ACSetTransformation) + (G₁, G₂) = (dom(f), codom(f)) + (cG₁, cG₂) = (c(G₁), c(G₂)) + FinFunction( λ₂ -> compose(f,λ₂), cG₂, cG₁ ) +end + +skeletalColoring(n) = skeleton ∘ Coloring(n) + +function colorability_test(n, the_test_case) + hom = is_homomorphic(ob(colimit(the_test_case)), K(n)) + dec = decide_sheaf_tree_shape(skeletalColoring(n), the_test_case)[1] + if hom == dec + return hom + else + error("is_homomorphic != decide_sheaf_tree_shape") + end +end + +function old_adhesion_filter(tup::Tuple, d::StructuredDecomposition) + if d.decomp_type == Decomposition + error("expecting ", CoDecomposition, " given ", Decomposition) + end + # d_csp is the cospan dx₁ -> de <- dx₂ corresp to some edge e = x₁x₂ in shape(d) + (csp, d_csp) = tup #unpack the tuple + # the pullback cone dx₁ <-l₁-- p --l₂ --> dx₂ with legs l₁ and l₂ + p_cone = pullback(d_csp) + p_legs = legs(p_cone) + # for each leg lᵢ : p → xᵢ of the pullback cone, + # compute its image ιᵢ : im lᵢ → dxᵢ + imgs = map( f -> legs(image(f))[1], p_legs) + # now get the new desired cospan; + # i.e. im l₁ --ι₁--> dx₁ --l₁--> de <--l₂--dx₂ <--ι₂-- im l₂ + new_d_csp = map(t -> compose(t...), zip(imgs, d_csp)) + # get the domain of d + d_dom = dom(d.diagram) + # now make the new decomposition, call it δ + # start with the object map δ₀ + function ob_replace(x) + if x == dom(d_dom, csp[1]) + dom(new_d_csp[1]) + elseif x == dom(d_dom, csp[2]) + dom(new_d_csp[2]) + else + ob_map(d,x) + end + end + δ₀ = Dict( x => ob_replace(x) for x ∈ ob_generators(d_dom) ) + # now do the same thing with the morphism map + function mor_replace(f) + if f == csp[1] + return new_d_csp[1] + elseif f == csp[2] + return new_d_csp[2] + else + return hom_map(d,f) + end + end + δ₁ = Dict( f => mor_replace(f) for f ∈ hom_generators(d_dom) ) + StrDecomp(d.decomp_shape, FinDomFunctor(δ₀, δ₁, d.domain), d.decomp_type) +end + +# for some reason PartialFunctions is giving me an error here on old_adhesion_filter +# and we have to explicitly Curry adhesion_filter.. +old_adhesion_filter(tup::Tuple) = d -> old_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 composites on edges, + project back down to bags + answer (providing a witness) + "no" if there is an empty bag; + "yes" otherwise. +""" +function old_decide_sheaf_tree_shape(f, d::StructuredDecomposition, solution_space_decomp::StructuredDecomposition = 𝐃(f, d, CoDecomposition)) + witness = foldl(∘, map(old_adhesion_filter, adhesionSpans(solution_space_decomp, true)))(solution_space_decomp) + (foldr(&, map( !isempty, bags(witness))), witness) +end + +# now updated functions + +function adhesion_filter(tup::Tuple, d::StructuredDecomposition) + if d.decomp_type == Decomposition + error("expecting ", CoDecomposition, " given ", Decomposition) + end + + #unpack + # d_csp is the cospan dx₁ -> de <- dx₂ corresp to some edge e = x₁x₂ in shape(d) + (csp, d_csp) = tup + + # the pullback cone dx₁ <-l₁-- p --l₂ --> dx₂ with legs l₁ and l₂ + p_cone = pullback(d_csp) + + if isempty(p_cone) + d_dom = FinSet(0) + else + # for each leg lᵢ : p → xᵢ of the pullback cone, + # compute its image ιᵢ : im lᵢ → dxᵢ + p_legs = legs(p_cone) + imgs = map(f->legs(image(f))[1], p_legs) + new_d_csp = map(t->compose(t...), zip(imgs, d_csp)) + end + + # get the domain of d + d_dom = dom(d.diagram) + + + # now make the new decomposition, call it δ + # us ob_replace and mor_replace + + function ob_replace(x) + if x == dom(d_dom, csp[1]) + dom(new_d_csp[1]) + elseif x == dom(d_dom, csp[2]) + dom(new_d_csp[2]) + else + ob_map(d,x) + end + end + + function mor_replace(f) + if f == csp[1] + return new_d_csp[1] + elseif f == csp[2] + return new_d_csp[2] + else + return hom_map(d,f) + end + end + + # start with the object map δ₀ + δ₀ = Dict( x => ob_replace(x) for x ∈ ob_generators(d_dom)) + # now do the same thing with the morphism map + δ₁ = Dict( f => mor_replace(f) for f ∈ hom_generators(d_dom)) + + StrDecomp(d.decomp_shape, FinDomFunctor(δ₀, δ₁, d.domain), d.decomp_type) +end + +"""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 composites on edges, + project back down to bags + answer (providing a witness) + "no" if there is an empty bag; + "yes" otherwise. +""" +function decide_sheaf_tree_shape(f, d::StructuredDecomposition, solution_space_decomp::StructuredDecomposition = 𝐃(f, d, CoDecomposition)) + witness = solution_space_decomp + adhesion_spans = adhesionSpans(solution_space_decomp, true) + for adhesion in adhesion_spans + witness = adhesion_filter(adhesion, witness) + if any(isempty, bags(witness)) + return (false, witness) + end + end + return (true, witness) +end + +# We will consider cases with 2(even), 4(even), 8(uneven), 16(uneven) bags +# We will first benchmark the coloring algorithm in graphs.jl + +# el = GraphsPkg.Edge.([ (1,2), (1,3), (2,3), (2,4), (3,4), (4,5), (4,6), (5,6), (5,7), (6,7), (7,8), (7,9), (8,9), (8,10), (9,10), (10,11), +# (10,12), (10,21), (10,22), (10,31), (10,32), (11,12), (11,13), (12,13), (13,14), (13,15), (14,15), (14,16), (15,16), (16,17), (16,18), +# (17,18), (17,19), (18,19), (19,20), (21,22), (21,23), (22,23), (23,24), (23,25), (24,25), (24,26), (25,26), (26,27), (26,28), (27,28), +# (27,29), (28,29), (29,20), (31,32), (31,33), (32,33), (33,34), (33,35), (34,35), (34,36), (35,36), (36,37), (36,38), (37,38), (37,39), +# (38,39), (39,40)]) + +# graph = GraphsPkg.SimpleGraph(el) +# @benchmark GraphsPkg.degree_greedy_color(graph) + +# 1 bag case (40 nodes, no adhesions) + +#bag 1 +bags1 = @acset Graph begin + V = 40 + E = 63 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 10, 10, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19, + 21, 21, 22, 23, 23, 24, 24, 25, 26, 26, 27, 27, 28, 29, 31, 31, 32, 33, 33, 34, 34, 35, 36, 36, 37, 37, 38, 39] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 12, 21, 22, 31, 32, 12, 13, 13, 14, 15, 15, 16, 16, 17, 18, 18, 19, 19, 20, + 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 29, 30, 32, 33, 33, 34, 35, 35, 36, 36, 37, 38, 38, 39, 39, 40] +end + +bags2 = @acset Graph begin + V = 1 + E = 0 + src = [] + tgt = [] +end + +bagsad = @acset Graph begin + V = 1 + E = 0 + src = [] + tgt = [] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => bags1, 2 => bags2, 3 => bagsad) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +bags1decomp = StrDecomp(Gₛ, Γₛ) + +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags1decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags1decomp)[1] +@benchmark is_homomorphic(K(2), bag1) +@benchmark is_homomorphic(K(3), bag1) +@benchmark is_homomorphic(K(4), bag1) + +# 2 bag case (21 nodes each, 1 node adhesion) + +#bag 1 +H₁ = @acset Graph begin + V = 21 + E = 31 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 18, 18, 19, 19, 20] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 21 + E = 31 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 18, 18, 19, 19, 20] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +bags2decomp = StrDecomp(Gₛ, Γₛ) + +# 4 bag case (1 bag w/ 10 nodes, 3 bags w/ 11 nodes, 1 node adhesion) + +#bag 1 +H₁ = @acset Graph begin + V = 10 + E = 15 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 11 + E = 16 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11] +end + +#adhesion 3,4 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 11 + E = 16 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 11 + E = 16 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11] +end + +Gₛ = @acset Graph begin + V = 4 + E = 3 + src = [1, 2, 3] + tgt = [2, 3, 4] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₁₂, 6 => H₂₃, 7 => H₃₄) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[4], V=[1]) + ), + ∫(Gₛ) +) + +bags4decomp = StrDecomp(Gₛ, Γₛ) + +# 8 bag case (bags between 4-7 nodes, 47 total nodes, 7 total adhesion nodes) + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 7 + E = 10 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7] +end + +#adhesion 3,4 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 7 + E = 10 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 5 + E = 6 + src = [1, 1, 2, 2, 3, 4] + tgt = [2, 3, 3, 4, 4, 5] +end + +#adhesion 2,5 +H₂₅ = @acset Graph begin + V = 1 +end + +#bag 5 +H₅ = @acset Graph begin + V = 7 + E = 10 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7] +end + +#adhesion 5,6 +H₅₆ = @acset Graph begin + V = 1 +end + +#bag 6 +H₆ = @acset Graph begin + V = 5 + E = 6 + src = [1, 1, 2, 2, 3, 4] + tgt = [2, 3, 3, 4, 4, 5] +end + +#adhesion 2,7 +H₂₇ = @acset Graph begin + V = 1 +end + +#bag 7 +H₇ = @acset Graph begin + V = 7 + E = 10 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7] +end + +#adhesion 7,8 +H₇₈ = @acset Graph begin + V = 1 +end + +#bag 8 +H₈ = @acset Graph begin + V = 5 + E = 6 + src = [1, 1, 2, 2, 3, 4] + tgt = [2, 3, 3, 4, 4, 5] +end + +Gₛ = @acset Graph begin + V = 8 + E = 7 + src = [1, 2, 2, 2, 3, 5, 7] + tgt = [2, 3, 5, 7, 4, 6, 8] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₅, 6 => H₆, 7 => H₇, 8 => H₈, 9 => H₁₂, 10 => H₂₃, 11 => H₃₄, 12 => H₂₅, + 13 => H₅₆, 14 => H₂₇, 15 => H₇₈) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[9], Γₛ⁰[1], V=[4]), + 2 => ACSetTransformation(Γₛ⁰[9], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[10], Γₛ⁰[2], V=[7]), + 4 => ACSetTransformation(Γₛ⁰[10], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[3], V=[7]), + 6 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[4], V=[1]), + 7 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[2], V=[7]), + 8 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[5], V=[1]), + 9 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[5], V=[7]), + 10 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[6], V=[1]), + 11 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[2], V=[7]), + 12 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[7], V=[1]), + 13 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[7], V=[7]), + 14 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[8], V=[1]), + ), + ∫(Gₛ) +) + +bags8decomp = StrDecomp(Gₛ, Γₛ) + +# 12 bag case (4-5 nodes per bag, 51 total nodes, 11 total adhesion nodes) + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 3,4 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 4,5 +H₄₅ = @acset Graph begin + V = 1 +end + +#bag 5 +H₅ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 5,6 +H₅₆ = @acset Graph begin + V = 1 +end + +#bag 6 +H₆ = @acset Graph begin + V = 5 + E = 6 + src = [1, 1, 2, 2, 3, 4] + tgt = [2, 3, 3, 4, 4, 5] +end + +#adhesion 3,7 +H₃₇ = @acset Graph begin + V = 1 +end + +#bag 7 +H₇ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 7,8 +H₇₈ = @acset Graph begin + V = 1 +end + +#bag 8 +H₈ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 8,9 +H₈₉ = @acset Graph begin + V = 1 +end + +#bag 9 +H₉ = @acset Graph begin + V = 5 + E = 6 + src = [1, 1, 2, 2, 3, 4] + tgt = [2, 3, 3, 4, 4, 5] +end + +#adhesion 3,10 +H₃₁₀ = @acset Graph begin + V = 1 +end + +#bag 10 +H₁₀ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 10,11 +H₁₀₁₁ = @acset Graph begin + V = 1 +end + +#bag 11 +H₁₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 11,12 +H₁₁₁₂ = @acset Graph begin + V = 1 +end + +#bag 12 +H₁₂₂ = @acset Graph begin + V = 5 + E = 6 + src = [1, 1, 2, 2, 3, 4] + tgt = [2, 3, 3, 4, 4, 5] +end + +Gₛ = @acset Graph begin + V = 12 + E = 11 + src = [1, 2, 3, 3, 3, 4, 5, 7, 8, 10, 11] + tgt = [2, 3, 4, 7, 10, 5, 6, 8, 9, 11, 12] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₅, 6 => H₆, 7 => H₇, 8 => H₈, 9 => H₉, 10 => H₁₀, 11 => H₁₁, 12 => H₁₂₂, + 13 => H₁₂, 14 => H₂₃, 15 => H₃₄, 16 => H₄₅, 17 => H₅₆, 18 => H₃₇, 19 => H₇₈, 20 => H₈₉, 21 => H₃₁₀, 22 => H₁₀₁₁, 23 => H₁₁₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[1], V=[4]), + 2 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[2], V=[4]), + 4 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[3], V=[4]), + 6 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[4], V=[1]), + 7 => ACSetTransformation(Γₛ⁰[16], Γₛ⁰[4], V=[4]), + 8 => ACSetTransformation(Γₛ⁰[16], Γₛ⁰[5], V=[1]), + 9 => ACSetTransformation(Γₛ⁰[17], Γₛ⁰[5], V=[4]), + 10 => ACSetTransformation(Γₛ⁰[17], Γₛ⁰[6], V=[1]), + 11 => ACSetTransformation(Γₛ⁰[18], Γₛ⁰[3], V=[4]), + 12 => ACSetTransformation(Γₛ⁰[18], Γₛ⁰[7], V=[1]), + 13 => ACSetTransformation(Γₛ⁰[19], Γₛ⁰[7], V=[4]), + 14 => ACSetTransformation(Γₛ⁰[19], Γₛ⁰[8], V=[1]), + 15 => ACSetTransformation(Γₛ⁰[20], Γₛ⁰[8], V=[4]), + 16 => ACSetTransformation(Γₛ⁰[20], Γₛ⁰[9], V=[1]), + 17 => ACSetTransformation(Γₛ⁰[21], Γₛ⁰[3], V=[4]), + 18 => ACSetTransformation(Γₛ⁰[21], Γₛ⁰[10], V=[1]), + 19 => ACSetTransformation(Γₛ⁰[22], Γₛ⁰[10], V=[4]), + 20 => ACSetTransformation(Γₛ⁰[22], Γₛ⁰[11], V=[1]), + 21 => ACSetTransformation(Γₛ⁰[23], Γₛ⁰[11], V=[4]), + 22 => ACSetTransformation(Γₛ⁰[23], Γₛ⁰[12], V=[1]) + ), + ∫(Gₛ) +) + +bags12decomp = StrDecomp(Gₛ, Γₛ) + +decide_sheaf_tree_shape(skeletalColoring(2), bags2decomp)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags4decomp)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags8decomp)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags12decomp)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags2decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags4decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags8decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags12decomp)[1] + +decide_sheaf_tree_shape(skeletalColoring(3), bags2decomp)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags4decomp)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags8decomp)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags12decomp)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags2decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags4decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags8decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags12decomp)[1] + +decide_sheaf_tree_shape(skeletalColoring(4), bags2decomp)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags4decomp)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags8decomp)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags12decomp)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags2decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags4decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags8decomp)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags12decomp)[1] + +# old adhesion_filter and decide_sheaf_tree_shape + +old_decide_sheaf_tree_shape(skeletalColoring(2), bags2decomp)[1] == false +old_decide_sheaf_tree_shape(skeletalColoring(2), bags4decomp)[1] == false +old_decide_sheaf_tree_shape(skeletalColoring(2), bags8decomp)[1] == false +old_decide_sheaf_tree_shape(skeletalColoring(2), bags12decomp)[1] == false +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(2), bags2decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(2), bags4decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(2), bags8decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(2), bags12decomp)[1] + +old_decide_sheaf_tree_shape(skeletalColoring(3), bags2decomp)[1] == true +old_decide_sheaf_tree_shape(skeletalColoring(3), bags4decomp)[1] == true +old_decide_sheaf_tree_shape(skeletalColoring(3), bags8decomp)[1] == true +old_decide_sheaf_tree_shape(skeletalColoring(3), bags12decomp)[1] == true +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(3), bags2decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(3), bags4decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(3), bags8decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(3), bags12decomp)[1] + +old_decide_sheaf_tree_shape(skeletalColoring(4), bags2decomp)[1] == true +old_decide_sheaf_tree_shape(skeletalColoring(4), bags4decomp)[1] == true +old_decide_sheaf_tree_shape(skeletalColoring(4), bags8decomp)[1] == true +old_decide_sheaf_tree_shape(skeletalColoring(4), bags12decomp)[1] == true +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(4), bags2decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(4), bags4decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(4), bags8decomp)[1] +@benchmark old_decide_sheaf_tree_shape(skeletalColoring(4), bags12decomp)[1] \ No newline at end of file diff --git a/benchmarks/GraphColoringIncreasingBagsExpandingGraph.jl b/benchmarks/GraphColoringIncreasingBagsExpandingGraph.jl new file mode 100644 index 0000000..919d78b --- /dev/null +++ b/benchmarks/GraphColoringIncreasingBagsExpandingGraph.jl @@ -0,0 +1,746 @@ +# This file examines the relationship between run time, bag size, and number of bags for a fixed 40 node graph. +# We will compare the results against running the graph coloring algorithm on Graphs.jl + +import Graphs as GraphsPkg + +using BenchmarkTools +using PkgBenchmark +using Profile + +using StructuredDecompositions.Decompositions +using StructuredDecompositions.DecidingSheaves +using StructuredDecompositions.FunctorUtils + +using Catlab.Graphs +using Catlab.ACSetInterface +using Catlab.CategoricalAlgebra +using Catlab.Graphics + +# The following structures and functions were pulled from DecidingSheaves.jl + +struct Coloring + n + func +end + +K(n) = complete_graph(Graph, n) +Coloring(n) = Coloring(n, g -> homomorphisms(g, K(n))) +(c::Coloring)(X::Graph) = FinSet(c.func(X)) +function (c::Coloring)(f::ACSetTransformation) + (G₁, G₂) = (dom(f), codom(f)) + (cG₁, cG₂) = (c(G₁), c(G₂)) + FinFunction( λ₂ -> compose(f,λ₂), cG₂, cG₁ ) +end + +skeletalColoring(n) = skeleton ∘ Coloring(n) + +function colorability_test(n, the_test_case) + hom = is_homomorphic(ob(colimit(the_test_case)), K(n)) + dec = decide_sheaf_tree_shape(skeletalColoring(n), the_test_case)[1] + if hom == dec + return hom + else + error("is_homomorphic != decide_sheaf_tree_shape") + end +end + +# We will start with a small number of vertices in a small number of bags and consider the case below +# Adding more bags with the same number of vertices at a constant rate +# For the purpose of this benchmark we will only consider bags of size 4 and size 8 + +# 2 bags size 4 + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +bags2size4 = StrDecomp(Gₛ, Γₛ) + +# 3 bags size 4 + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 2,3 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +Gₛ = @acset Graph begin + V = 3 + E = 2 + src = [1, 1] + tgt = [2, 3] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₁₂, 5 => H₂₃) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[4], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[4], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[3], V=[1]), + ), + ∫(Gₛ) +) + +bags3size4 = StrDecomp(Gₛ, Γₛ) + +# 4 bags size 4 + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 2,3 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +Gₛ = @acset Graph begin + V = 4 + E = 3 + src = [1, 1, 1] + tgt = [2, 3, 4] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₁₂, 6 => H₂₃, 7 => H₃₄) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[4], V=[1]) + ), + ∫(Gₛ) +) + +bags4size4 = StrDecomp(Gₛ, Γₛ) + +# 8 bags size 4 + +#bag 1 +H₁ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 2,3 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 4,5 +H₄₅ = @acset Graph begin + V = 1 +end + +#bag 5 +H₅ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 5,6 +H₅₆ = @acset Graph begin + V = 1 +end + +#bag 6 +H₆ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 6,7 +H₆₇ = @acset Graph begin + V = 1 +end + +#bag 7 +H₇ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + +#adhesion 7,8 +H₇₈ = @acset Graph begin + V = 1 +end + +#bag 8 +H₈ = @acset Graph begin + V = 4 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [2, 3, 3, 4, 4] +end + + +Gₛ = @acset Graph begin + V = 8 + E = 7 + src = [1, 1, 1, 1, 1, 1, 1] + tgt = [2, 3, 4, 5, 6, 7, 8] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₅, 6 => H₆, 7 => H₇, 8 => H₈, 9 => H₁₂, 10 => H₂₃, 11 => H₃₄, + 12 => H₄₅, 13 => H₅₆, 14 => H₆₇, 15 => H₇₈) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[9], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[9], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[10], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[10], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[4], V=[1]), + 7 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[4], V=[1]), + 8 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[5], V=[1]), + 9 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[5], V=[1]), + 10 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[6], V=[1]), + 11 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[6], V=[1]), + 12 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[7], V=[1]), + 13 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[7], V=[1]), + 14 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[8], V=[1]) + ), + ∫(Gₛ) +) + +bags8size4 = StrDecomp(Gₛ, Γₛ) + +# benchmarks on size 4 bags +decide_sheaf_tree_shape(skeletalColoring(2), bags2size4)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags3size4)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags4size4)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags8size4)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags2size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags3size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags4size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags8size4)[1] +@benchmark colorability_test(2, bags2size4) +@benchmark colorability_test(2, bags3size4) +@benchmark colorability_test(2, bags4size4) +@benchmark colorability_test(2, bags8size4) + +decide_sheaf_tree_shape(skeletalColoring(3), bags2size4)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags3size4)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags4size4)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags8size4)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags2size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags3size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags4size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags8size4)[1] +@benchmark colorability_test(3, bags2size4) +@benchmark colorability_test(3, bags3size4) +@benchmark colorability_test(3, bags4size4) +@benchmark colorability_test(3, bags8size4) + +decide_sheaf_tree_shape(skeletalColoring(4), bags2size4)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags3size4)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags4size4)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags8size4)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags2size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags3size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags4size4)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags8size4)[1] +@benchmark colorability_test(4, bags2size4) +@benchmark colorability_test(4, bags3size4) +@benchmark colorability_test(4, bags4size4) +@benchmark colorability_test(4, bags8size4) + +# 2 bags size 8 + +#bag 1 +H₁ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [1] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1]), + ), + ∫(Gₛ) +) + +bags2size8 = StrDecomp(Gₛ, Γₛ) + +# 3 bags size 8 + +#bag 1 +H₁ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 2,3 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +Gₛ = @acset Graph begin + V = 3 + E = 2 + src = [1, 1] + tgt = [2, 3] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₁₂, 5 => H₂₃) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[4], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[4], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[3], V=[1]), + ), + ∫(Gₛ) +) + +bags3size8 = StrDecomp(Gₛ, Γₛ) + +# 4 bags size 8 + +#bag 1 +H₁ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 2,3 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +Gₛ = @acset Graph begin + V = 4 + E = 3 + src = [1, 1, 1] + tgt = [2, 3, 4] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₁₂, 6 => H₂₃, 7 => H₃₄) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[5], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[6], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[7], Γₛ⁰[4], V=[1]) + ), + ∫(Gₛ) +) + +bags4size8 = StrDecomp(Gₛ, Γₛ) + +# 8 bags size 8 + +#bag 1 +H₁ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 1,2 +H₁₂ = @acset Graph begin + V = 1 +end + +#bag 2 +H₂ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 2,3 +H₂₃ = @acset Graph begin + V = 1 +end + +#bag 3 +H₃ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 3,4 +H₃₄ = @acset Graph begin + V = 1 +end + +#bag 4 +H₄ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 4,5 +H₄₅ = @acset Graph begin + V = 1 +end + +#bag 5 +H₅ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 5,6 +H₅₆ = @acset Graph begin + V = 1 +end + +#bag 6 +H₆ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 6,7 +H₆₇ = @acset Graph begin + V = 1 +end + +#bag 7 +H₇ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +#adhesion 7,8 +H₇₈ = @acset Graph begin + V = 1 +end + +#bag 8 +H₈ = @acset Graph begin + V = 8 + E = 11 + src = [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7] + tgt = [2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8] +end + +Gₛ = @acset Graph begin + V = 8 + E = 7 + src = [1, 1, 1, 1, 1, 1, 1] + tgt = [2, 3, 4, 5, 6, 7, 8] +end + +#transformations +Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₃, 4 => H₄, 5 => H₅, 6 => H₆, 7 => H₇, 8 => H₈, 9 => H₁₂, 10 => H₂₃, 11 => H₃₄, + 12 => H₄₅, 13 => H₅₆, 14 => H₆₇, 15 => H₇₈) +Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[9], Γₛ⁰[1], V=[1]), + 2 => ACSetTransformation(Γₛ⁰[9], Γₛ⁰[2], V=[1]), + 3 => ACSetTransformation(Γₛ⁰[10], Γₛ⁰[2], V=[1]), + 4 => ACSetTransformation(Γₛ⁰[10], Γₛ⁰[3], V=[1]), + 5 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[3], V=[1]), + 6 => ACSetTransformation(Γₛ⁰[11], Γₛ⁰[4], V=[1]), + 7 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[4], V=[1]), + 8 => ACSetTransformation(Γₛ⁰[12], Γₛ⁰[5], V=[1]), + 9 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[5], V=[1]), + 10 => ACSetTransformation(Γₛ⁰[13], Γₛ⁰[6], V=[1]), + 11 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[6], V=[1]), + 12 => ACSetTransformation(Γₛ⁰[14], Γₛ⁰[7], V=[1]), + 13 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[7], V=[1]), + 14 => ACSetTransformation(Γₛ⁰[15], Γₛ⁰[8], V=[1]) + ), + ∫(Gₛ) +) + +bags8size8 = StrDecomp(Gₛ, Γₛ) + +# benchmarks on size 4 bags +decide_sheaf_tree_shape(skeletalColoring(2), bags2size8)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags3size8)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags4size8)[1] == false +decide_sheaf_tree_shape(skeletalColoring(2), bags8size8)[1] == false +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags2size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags3size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags4size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(2), bags8size8)[1] +@benchmark colorability_test(2, bags2size8) +@benchmark colorability_test(2, bags3size8) +@benchmark colorability_test(2, bags4size8) +@benchmark colorability_test(2, bags8size8) + +decide_sheaf_tree_shape(skeletalColoring(3), bags2size8)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags3size8)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags4size8)[1] == true +decide_sheaf_tree_shape(skeletalColoring(3), bags8size8)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags2size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags3size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags4size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(3), bags8size8)[1] +@benchmark colorability_test(3, bags2size8) +@benchmark colorability_test(3, bags3size8) +@benchmark colorability_test(3, bags4size8) +@benchmark colorability_test(3, bags8size8) + +decide_sheaf_tree_shape(skeletalColoring(4), bags2size8)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags3size8)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags4size8)[1] == true +decide_sheaf_tree_shape(skeletalColoring(4), bags8size8)[1] == true +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags2size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags3size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags4size8)[1] +@benchmark decide_sheaf_tree_shape(skeletalColoring(4), bags8size8)[1] +@benchmark colorability_test(4, bags2size8) +@benchmark colorability_test(4, bags3size8) +@benchmark colorability_test(4, bags4size8) +@benchmark colorability_test(4, bags8size8) \ No newline at end of file diff --git a/benchmarks/Project.toml b/benchmarks/Project.toml new file mode 100644 index 0000000..edd9e18 --- /dev/null +++ b/benchmarks/Project.toml @@ -0,0 +1,25 @@ +[deps] +AMD = "14f7f29c-3bd6-536c-9a0b-7339e30b5a3e" +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" +CuthillMcKee = "17f17636-5e38-52e3-a803-7ae3aaaf3da9" +DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" +Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" +PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b" +PkgBenchmark = "32113eaa-f34f-5b0d-bd6c-c81e245fc73d" +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" \ No newline at end of file diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 0000000..9a7d90f --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,3 @@ +# StructuredDecompositions.jl benchmarks + +This directory benchmarks various parts of StructuredDecompositions.jl. \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl index cffe617..8020754 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -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", ] ) diff --git a/docs/src/api.md b/docs/src/api.md index d2dbd55..131b9ad 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -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] ``` diff --git a/docs/src/index.md b/docs/src/index.md index 5e8cc12..3480477 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -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 +``` diff --git a/docs/src/pages/decidingsheaves.md b/docs/src/pages/decidingsheaves.md new file mode 100644 index 0000000..867d797 --- /dev/null +++ b/docs/src/pages/decidingsheaves.md @@ -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ₛ, Γₛ) +``` \ No newline at end of file diff --git a/docs/src/pages/decompositions.md b/docs/src/pages/decompositions.md new file mode 100644 index 0000000..ca5f07b --- /dev/null +++ b/docs/src/pages/decompositions.md @@ -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 +``` \ No newline at end of file diff --git a/docs/src/pages/functor_utils.md b/docs/src/pages/functor_utils.md new file mode 100644 index 0000000..9560064 --- /dev/null +++ b/docs/src/pages/functor_utils.md @@ -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 +``` \ No newline at end of file diff --git a/docs/src/pages/junction_trees.md b/docs/src/pages/junction_trees.md new file mode 100644 index 0000000..aa20651 --- /dev/null +++ b/docs/src/pages/junction_trees.md @@ -0,0 +1 @@ +# [Junction Trees] (@id JunctionTrees) diff --git a/docs/src/pages/nested_uwds.md b/docs/src/pages/nested_uwds.md new file mode 100644 index 0000000..478bae0 --- /dev/null +++ b/docs/src/pages/nested_uwds.md @@ -0,0 +1 @@ +# [Nested UWDs] (@id NestedUWDs) diff --git a/src/DecidingSheaves.jl b/src/DecidingSheaves.jl index a9ad760..0a0a1a3 100644 --- a/src/DecidingSheaves.jl +++ b/src/DecidingSheaves.jl @@ -14,14 +14,14 @@ 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 by the span obtained by projecting the pullback of de (i.e. taking images) """ -function adhesion_filter(tup::Tuple, d::StructuredDecomposition) +function old_adhesion_filter(tup::Tuple, d::StructuredDecomposition) if d.decomp_type == Decomposition error("expecting ", CoDecomposition, " given ", Decomposition) end @@ -30,16 +30,16 @@ function adhesion_filter(tup::Tuple, d::StructuredDecomposition) # the pullback cone dx₁ <-l₁-- p --l₂ --> dx₂ with legs l₁ and l₂ p_cone = pullback(d_csp) p_legs = legs(p_cone) - #for each leg lᵢ : p → xᵢ of the pullback cone, - #compute its image ιᵢ : im lᵢ → dxᵢ + # for each leg lᵢ : p → xᵢ of the pullback cone, + # compute its image ιᵢ : im lᵢ → dxᵢ imgs = map( f -> legs(image(f))[1], p_legs) - #now get the new desired cospan; - #i.e. im l₁ --ι₁--> dx₁ --l₁--> de <--l₂--dx₂ <--ι₂-- im l₂ + # now get the new desired cospan; + # i.e. im l₁ --ι₁--> dx₁ --l₁--> de <--l₂--dx₂ <--ι₂-- im l₂ new_d_csp = map(t -> compose(t...), zip(imgs, d_csp)) - #get the domain of d + # get the domain of d d_dom = dom(d.diagram) - #now make the new decomposition, call it δ - #start with the object map δ₀ + # now make the new decomposition, call it δ + # start with the object map δ₀ function ob_replace(x) if x == dom(d_dom, csp[1]) dom(new_d_csp[1]) @@ -50,7 +50,7 @@ function adhesion_filter(tup::Tuple, d::StructuredDecomposition) end end δ₀ = Dict( x => ob_replace(x) for x ∈ ob_generators(d_dom) ) - #now do the same thing with the morphism map + # now do the same thing with the morphism map function mor_replace(f) if f == csp[1] return new_d_csp[1] @@ -64,9 +64,9 @@ function adhesion_filter(tup::Tuple, d::StructuredDecomposition) StrDecomp(d.decomp_shape, FinDomFunctor(δ₀, δ₁, d.domain), d.decomp_type) end -#for some reason PartialFunctions is giving me an error here -#and we have to explicitly Curry adhesion_filter.. -adhesion_filter(tup::Tuple) = d -> adhesion_filter(tup, d) +# for some reason PartialFunctions is giving me an error here on old_adhesion_filter +# and we have to explicitly Curry adhesion_filter.. +old_adhesion_filter(tup::Tuple) = d -> old_adhesion_filter(tup, d) """Solve the decision problem encoded by a sheaf. The algorithm is as follows: @@ -78,10 +78,91 @@ The algorithm is as follows: "no" if there is an empty bag; "yes" otherwise. """ -function decide_sheaf_tree_shape(f, d::StructuredDecomposition, solution_space_decomp::StructuredDecomposition = 𝐃(f, d, CoDecomposition)) - witness = foldl(∘, map(adhesion_filter, adhesionSpans(solution_space_decomp, true)))(solution_space_decomp) +function old_decide_sheaf_tree_shape(f, d::StructuredDecomposition, solution_space_decomp::StructuredDecomposition = 𝐃(f, d, CoDecomposition)) + witness = foldl(∘, map(old_adhesion_filter, adhesionSpans(solution_space_decomp, true)))(solution_space_decomp) (foldr(&, map( !isempty, bags(witness))), witness) end +# now updated functions + +function adhesion_filter(tup::Tuple, d::StructuredDecomposition) + if d.decomp_type == Decomposition + error("expecting ", CoDecomposition, " given ", Decomposition) + end + + #unpack + # d_csp is the cospan dx₁ -> de <- dx₂ corresp to some edge e = x₁x₂ in shape(d) + (csp, d_csp) = tup + + # the pullback cone dx₁ <-l₁-- p --l₂ --> dx₂ with legs l₁ and l₂ + p_cone = pullback(d_csp) + + if isempty(p_cone) + d_dom = FinSet(0) + else + # for each leg lᵢ : p → xᵢ of the pullback cone, + # compute its image ιᵢ : im lᵢ → dxᵢ + p_legs = legs(p_cone) + imgs = map(f->legs(image(f))[1], p_legs) + new_d_csp = map(t->compose(t...), zip(imgs, d_csp)) + end + + # get the domain of d + d_dom = dom(d.diagram) + + + # now make the new decomposition, call it δ + # us ob_replace and mor_replace + + function ob_replace(x) + if x == dom(d_dom, csp[1]) + dom(new_d_csp[1]) + elseif x == dom(d_dom, csp[2]) + dom(new_d_csp[2]) + else + ob_map(d,x) + end + end + + function mor_replace(f) + if f == csp[1] + return new_d_csp[1] + elseif f == csp[2] + return new_d_csp[2] + else + return hom_map(d,f) + end + end + + # start with the object map δ₀ + δ₀ = Dict( x => ob_replace(x) for x ∈ ob_generators(d_dom)) + # now do the same thing with the morphism map + δ₁ = Dict( f => mor_replace(f) for f ∈ hom_generators(d_dom)) + + StrDecomp(d.decomp_shape, FinDomFunctor(δ₀, δ₁, d.domain), d.decomp_type) +end + +"""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 composites on edges, + project back down to bags + answer (providing a witness) + "no" if there is an empty bag; + "yes" otherwise. +""" +function decide_sheaf_tree_shape(f, d::StructuredDecomposition, solution_space_decomp::StructuredDecomposition = 𝐃(f, d, CoDecomposition)) + witness = solution_space_decomp + adhesion_spans = adhesionSpans(solution_space_decomp, true) + for adhesion in adhesion_spans + witness = adhesion_filter(adhesion, witness) + if any(isempty, bags(witness)) + return (false, witness) + end + end + return (true, witness) +end end \ No newline at end of file diff --git a/src/Decompositions.jl b/src/Decompositions.jl index 1ceb835..ef6b6db 100644 --- a/src/Decompositions.jl +++ b/src/Decompositions.jl @@ -34,9 +34,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 @@ -73,7 +73,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 @@ -89,9 +89,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) @@ -112,7 +112,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) @@ -129,19 +129,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) @@ -150,13 +150,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) diff --git a/test/DecidingSheaves.jl b/test/DecidingSheaves.jl index 949d6af..d5141a0 100644 --- a/test/DecidingSheaves.jl +++ b/test/DecidingSheaves.jl @@ -13,56 +13,13 @@ using Catlab.ACSetInterface using Catlab.CategoricalAlgebra using Catlab.Graphics -############################ -# EXAMPLE INSTANCE str decomp -############################ - -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 - -Γₛ⁰ = 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_decomp = StrDecomp(Gₛ, Γₛ) - """ An example: graph colorings """ #an H-coloring is a hom onto H struct Coloring n #the target graph - func #the function mappgin opens to lists of homs from G to K_n + func #the function mapping opens to lists of homs from G to K_n end #construct an n-coloring @@ -80,12 +37,565 @@ 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] -is_homomorphic(ob(colimit(my_decomp)), K(2)) +# TODO: Remove me? +# is_homomorphic(ob(colimit(my_decomp1)), K(2)) + +@testset "Test 1" begin + + ############################ + # EXAMPLE INSTANCE 1 str decomp + ############################ + # 7 node cycle + + #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 + + #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ₛ, Γₛ) + + @show "Decomp1" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp1)[1] == false + #evaluates if decomp1 2 coloring is possible + + @show "Decomp1 colorability_test" + + @test all(colorability_test(n, my_decomp1) for n ∈ range(1,3)) + #evaluate possible 1 thorugh 3 colorings + +end + +@testset "Test 2" begin + + ############################ + # EXAMPLE INSTANCE 2 str decomp + ############################ + # Triangle + one extension from a vertex + + #bag 1 + H₁ = @acset Graph begin + V = 3 + E = 3 + src = [1, 2, 3] + tgt = [2, 3, 1] + end + + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 2 + end + + #bag 2 + H₂ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1, 3]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[2, 1]), + ), + ∫(Gₛ) + ) + + my_decomp2 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp2" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp2)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp2)[1] == true + #evaluate if decomp2 2 and 3 colorings are possible + + @show "Decomp2 colorability_test" + + @test all(colorability_test(n, my_decomp2) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings + +end + +@testset "Test 3" begin + + ############################ + # EXAMPLE INSTANCE 3 str decomp + ############################ + # windmill shape + # 4 triangles connected at single vertex + + #bag 1 + H₁ = @acset Graph begin + V = 5 + E = 6 + src = [1, 2, 1, 4, 5, 4] + tgt = [2, 3, 3, 3, 3, 5] + end + + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 1 + end + + #bag 2 + H₂ = @acset Graph begin + V = 5 + E = 6 + src = [1, 2, 1, 4, 5, 4] + tgt = [2, 3, 3, 3, 3, 5] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[3]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[3]), + ), + ∫(Gₛ) + ) + + my_decomp3 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp3" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp3)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp3)[1] == true + #evaluate if decomp3 2 and 3 colorings are possible + + @show "Decomp3 colorability_test" + + @test all(colorability_test(n, my_decomp3) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings + +end + +@testset "Test 4" begin + + ############################ + # EXAMPLE INSTANCE 4 str decomp + ############################ + # star inside of pentagon + # connections at each star vertex to a pentagon vertex + + #bag 1 + H₁ = @acset Graph begin + V = 10 + E = 10 + src = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 1, 6, 7, 8, 9, 10] + end + + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 5 + end + + #bag 2 + H₂ = @acset Graph begin + V = 5 + E = 5 + src = [1, 1, 2, 2, 3] + tgt = [3, 4, 4, 5, 5] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[6, 7, 8, 9, 10]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 2, 3, 4, 5]), + ), + ∫(Gₛ) + ) + + my_decomp4 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp4" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp4)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp4)[1] == true + #evaluate if decomp4 2 and 3 colorings are possible + + @show "Decomp4 colorability_test" + + @test all(colorability_test(n, my_decomp4) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings + +end + +@testset "Test 5" begin -@test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp)[1] == false + ############################ + # EXAMPLE INSTANCE 5 str decomp + ############################ + # 2 by 2 square inside of 3 by 3 square + # connections at each vertex of 2 by 2 to middle + # vertices of 3 by 3 square -@test all(colorability_test(n, my_decomp) for n ∈ range(1,10)) + #bag 1 + H₁ = @acset Graph begin + V = 12 + E = 12 + src = [5, 6, 7, 8, 9, 10, 11, 12, 6, 8, 10, 12] + tgt = [6, 7, 8, 9, 10, 11, 12, 5, 1, 2, 3, 4] + end + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 4 + end + #bag 2 + H₂ = @acset Graph begin + V = 4 + E = 4 + src = [1, 2, 3, 4] + tgt = [2, 3, 4, 1] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1, 2, 3, 4]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 2, 3, 4]), + ), + ∫(Gₛ) + ) + + my_decomp5 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp5" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp5)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp5)[1] == true + #evaluate if decomp5 2 and 3 colorings are possible + + @show "Decomp5 colorability_test" + + @test all(colorability_test(n, my_decomp5) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings + +end + +@testset "Test 6" begin + + ############################ + # EXAMPLE INSTANCE 6 str decomp + ############################ + # K4 inside of 3 by 3 square + # connections at each vertex of K4 to middle + # vertices of 3 by 3 square + + #bag 1 + H₁ = @acset Graph begin + V = 12 + E = 12 + src = [5, 6, 7, 8, 9, 10, 11, 12, 6, 8, 10, 12] + tgt = [6, 7, 8, 9, 10, 11, 12, 5, 1, 2, 3, 4] + end + + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 4 + end + + #bag 2 + H₂ = @acset Graph begin + V = 4 + E = 6 + src = [1, 2, 3, 4, 1, 2] + tgt = [2, 3, 4, 1, 3, 4] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1, 2, 3, 4]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 2, 3, 4]), + ), + ∫(Gₛ) + ) + + my_decomp6 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp6" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp6)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp6)[1] == false + #evaluate if decomp6 2 and 3 colorings are possible + + @show "Decomp6 colorability_test" + + @test all(colorability_test(n, my_decomp6) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings +end + +@testset "Test 7" begin + + ############################ + # EXAMPLE INSTANCE 7 str decomp + ############################ + # slightly different variation of 6 + # connections are now to corners not center + + #bag 1 + H₁ = @acset Graph begin + V = 12 + E = 12 + src = [5, 6, 7, 8, 9, 10, 11, 12, 5, 7, 9, 11] + tgt = [6, 7, 8, 9, 10, 11, 12, 5, 1, 2, 3, 4] + end + + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 4 + end + + #bag 2 + H₂ = @acset Graph begin + V = 4 + E = 6 + src = [1, 2, 3, 4, 1, 2] + tgt = [2, 3, 4, 1, 3, 4] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[1, 2, 3, 4]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 2, 3, 4]), + ), + ∫(Gₛ) + ) + + my_decomp7 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp7" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp7)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp7)[1] == false + #evaluate if decomp7 2 and 3 colorings are possible + + @show "Decomp7 colorability_test" + + @test all(colorability_test(n, my_decomp7) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings + +end + +@testset "Test 8" begin + + ############################ + # EXAMPLE INSTANCE 8 str decomp + ############################ + # large box with incomplete 3x3 lattice inside + # incomplete lattice missing a corner + + #bag 1 + H₁ = @acset Graph begin + V = 11 + E = 11 + src = [1, 2, 3, 4, 5, 1, 2, 2, 3, 4, 5] + tgt = [2, 3, 4, 5, 1, 6, 7, 8, 9, 10, 11] + end + + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 6 + end + + #bag 2 + H₂ = @acset Graph begin + V = 8 + E = 10 + src = [1, 1, 2, 3, 3, 4, 4, 5, 6, 7] + tgt = [2, 3, 4, 4, 6, 5, 7, 8, 7, 8] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[6, 7, 8, 9, 10, 11]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 2, 5, 8, 7, 6]), + ), + ∫(Gₛ) + ) + + my_decomp8 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp8" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp8)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp8)[1] == true + #evaluate if decomp8 2 and 3 colorings are possible + + @show "Decomp8 colorability_test" + + @test all(colorability_test(n, my_decomp8) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings + +end + +@testset "Test 9" begin + + ############################ + # EXAMPLE INSTANCE 9 str decomp + ############################ + # circle (defined by 3 vertices) with inscribed triangle 1 + # triangle 1 inscribed with triangle 2 + # triangle 2 inscribed with upsidedown triangle 3 + + + #bag 1 + H₁ = @acset Graph begin + V = 12 + E = 15 + src = [1, 1, 2, 3, 3, 4, 5, 5, 6, 1, 2, 3, 4, 5, 6] + tgt = [2, 3, 3, 4, 5, 5, 6, 1, 1, 7, 8, 9, 10, 11, 12] + end + + #adhesion 1,2 + H₁₂ = @acset Graph begin + V = 6 + end + + #bag 2 + H₂ = @acset Graph begin + V = 6 + E = 9 + src = [1, 1, 2, 2, 2, 3, 4, 4, 5] + tgt = [2, 6, 3, 4, 6, 4, 5, 6, 6] + end + + Gₛ = @acset Graph begin + V = 2 + E = 1 + src = [1] + tgt = [2] + end + + #transformations + Γₛ⁰ = Dict(1 => H₁, 2 => H₂, 3 => H₁₂) + Γₛ = FinDomFunctor( + Γₛ⁰, + Dict( + 1 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[1], V=[7, 8, 9, 10, 11, 12]), + 2 => ACSetTransformation(Γₛ⁰[3], Γₛ⁰[2], V=[1, 2, 3, 4, 5, 6]), + ), + ∫(Gₛ) + ) + + my_decomp9 = StrDecomp(Gₛ, Γₛ) + + @show "Decomp9" + + @test decide_sheaf_tree_shape(skeletalColoring(2), my_decomp9)[1] == false + @test decide_sheaf_tree_shape(skeletalColoring(3), my_decomp9)[1] == true + #evaluate if decomp9 2 and 3 colorings are possible + + @show "Decomp9 colorability_test" + + @test all(colorability_test(n, my_decomp9) for n ∈ range(1, 3)) + #evaluate possible 1 through 3 colorings + +end end \ No newline at end of file