diff --git a/src/categorical_algebra/CSets.jl b/src/categorical_algebra/CSets.jl index db8bb8012..c2f75ae5f 100644 --- a/src/categorical_algebra/CSets.jl +++ b/src/categorical_algebra/CSets.jl @@ -413,13 +413,36 @@ end map_components(f, α::LooseACSetTransformation) = LooseACSetTransformation(map(f, components(α)), α.type_components, dom(α), codom(α)) -function is_natural(α::ACSetTransformation{S}) where {S} +""" +Check naturality condition for a purported ACSetTransformation, α: X→Y. +For each hom in the schema, e.g. h: m → n, the following square must commute: + αₘ + Xₘ --> Yₘ +Xₕ ↓ ✓ ↓ Yₕ + Xₙ --> Yₙ + αₙ + +If `return_failures` is true, return a list of violations, with elements such as: + (h, index i in Xₘ, Yₕ(αₘ(i)), αₙ(Xₕ(i))) + +This is type-unstable, so it should not be used in performance-sensitive code. +""" +function is_natural(α::ACSetTransformation{S}; return_failures::Bool=false) where {S} X, Y = dom(α), codom(α) + unnatural = [] for (f, c, d) in arrows(S) Xf, Yf, α_c, α_d = subpart(X,f), subpart(Y,f), α[c], α[d] - all(i -> Yf[α_c(i)] == α_d(Xf[i]), eachindex(Xf)) || return false + for i in eachindex(Xf) + if Yf[α_c(i)] != α_d(Xf[i]) + if return_failures + push!(unnatural, (f, i, Yf[α_c(i)], α_d(Xf[i]))) + else + return false + end + end + end end - return true + return return_failures ? unnatural : true end function is_monic(α::TightACSetTransformation{S}) where {S} diff --git a/test/categorical_algebra/CSets.jl b/test/categorical_algebra/CSets.jl index a97553fce..f2c045c10 100644 --- a/test/categorical_algebra/CSets.jl +++ b/test/categorical_algebra/CSets.jl @@ -76,6 +76,9 @@ g, h = path_graph(Graph, 4), cycle_graph(Graph, 2) @test !is_natural(β) β = CSetTransformation((V=[2,1], E=[2,1]), h, h) @test is_natural(β) +β = CSetTransformation((V=[2,1], E=[2,2]), h, h) +@test is_natural(β;return_failures=true) == [(:src,2,2,1),(:tgt,2,1,2)] + # Category of C-sets. @test dom(α) === g @@ -256,6 +259,8 @@ h = path_graph(WeightedGraph{Float64}, 4, E=(weight=[1.,2.,3.],)) @test !is_natural(β) # Preserves weight but not graph homomorphism β = ACSetTransformation((V=[1,2], E=[1]), g, h) @test !is_natural(β) # Graph homomorphism but does not preserve weight +β = ACSetTransformation((V=[1,3], E=[1]), g, h) +@test is_natural(β; return_failures=true) == [(:tgt,1,2,3), (:weight,1,1.,2.)] # Loose morphisms. α = ACSetTransformation(g, h, V=[1,2], E=[1], Weight=x->x/2)