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/GraphColoringConstantBagsExpandingGraph.jl b/benchmarks/GraphColoringConstantBagsExpandingGraph.jl index 2eb2a39..7e800bc 100644 --- a/benchmarks/GraphColoringConstantBagsExpandingGraph.jl +++ b/benchmarks/GraphColoringConstantBagsExpandingGraph.jl @@ -220,7 +220,7 @@ end my_decomp2 = StrDecomp(Gₛ, Γₛ) -# We now consider the 2 bag, 12 vertices graph +# We now consider the 2 bag, 16 vertices graph #bag 1 H₁ = @acset Graph begin diff --git a/benchmarks/GraphColoringFixedGraph.jl b/benchmarks/GraphColoringFixedGraph.jl index 85e97b8..235d317 100644 --- a/benchmarks/GraphColoringFixedGraph.jl +++ b/benchmarks/GraphColoringFixedGraph.jl @@ -45,6 +45,149 @@ function colorability_test(n, the_test_case) 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 @@ -578,4 +721,33 @@ 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] \ No newline at end of file +@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