Skip to content

Commit

Permalink
Rebase to include sheaf code
Browse files Browse the repository at this point in the history
  • Loading branch information
Kris Brown committed Jan 23, 2025
1 parent 30c98e8 commit 38b0f13
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 86 deletions.
6 changes: 3 additions & 3 deletions src/Catlab.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ module Catlab

using Reexport

include("theories/Theories.jl")
include("theories/module.jl")
include("ACSetsGATsInterop.jl")
include("graphs/Graphs.jl")
include("basic_sets/BasicSets.jl")
include("categorical_algebra/CategoricalAlgebra.jl")
include("basic_sets/module.jl")
include("categorical_algebra/module.jl")
include("wiring_diagrams/WiringDiagrams.jl")
include("graphics/Graphics.jl")
include("adts/ADTs.jl")
Expand Down
79 changes: 52 additions & 27 deletions src/sheaves/FVect.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
struct FinVect <: Category{FinSet, Function, SmallCatSize} end
using GATlab

@struct_hash_equal struct FinVect end

# The tests do not currently use the category structure of FinVect
@instance ThCategoryExplicitSets{FinSet, Function, AbsSet} [model::FinVect] begin
dom(f::FinSet) = error()
codom(f::FinSet) = error()
id(f::FinSet) = error()
compose(f::Function, g::Function) = error()
ob_set() = SetOb(FinSet)
hom_set() = SetOb(Function)
end

""" FVectPullback{T} where T <: Number
The contravariant free vector space functor.
The returned function f✶ restricts via precomposition.
"""
struct FVectPullback <: Functor{FinSetOpT, FinVect} end
dom(::FVectPullback) = FinSetOpT
codom(::FVectPullback) = FinVect
do_ob_map(::FVectPullback, n::FinSet) = n
do_hom_map(::FVectPullback, f::FinFunction) = (v->v[f.(dom(f))])
# as a callable functor this would be: FVectPullback = Functor(identity, f->(v->v[f.(dom(f))]), OppositeCat(FinSetCat()), FinVect())
FVectPullback = Functor(identity, f->(v->v[f.(dom(f))]),
op(Category(FinSetC())), Category(FinVect())) # end {FinSetOpT, FinVect} end

""" FreeVect{T} where T <: Number
Expand All @@ -23,45 +31,58 @@ FVectPushforward = Functor(identity, # identity on objects
sum(v[j] for j in preimage(f,i))
end),
# covariant functor from FinSetCat to FinVect
FinSetCat(), FinVect()
Category(FinSetC()), Category(FinVect())
)

const FinMat = TypeCat(MatrixDom, Matrix)
const FinMatT = typeof(FinMat)
@instance ThCategoryExplicitSets{Int, AbstractMatrix{T}, AbsSet
} [model::MatC{T}] where T begin

ob_set() = SetOb(PredicatedSet{Int}(i -> i0))
hom_set() = SetOb(AbstractMatrix{T})
end

# call a matrix on a vector multiplies by it.
function (M::SparseArrays.SparseMatrixCSC{Int64, Int64})(x::AbstractVector)
M*x
end

function pullback_matrix(f::FinFunction)
n = length(dom(f))
sparse(1:n, f.(dom(f)), ones(Int,n), dom(f).n, codom(f).n)
n = length(dom(f))
sparse(1:n, f.(dom(f)), ones(Int,n), length(dom(f)), length(codom(f)))
end

function pushforward_matrix(f::FinFunction)
pullback_matrix(f)'
end

struct FMatPullback <: Functor{FinSetOpT, FinMatT} end
dom(::FMatPullback) = FinSetOpT
codom(::FMatPullback) = FinMat
do_ob_map(::FMatPullback, n::FinSet) = MatrixDom{AbstractMatrix}(n.n)
do_hom_map(::FMatPullback, f::FinFunction) = pullback_matrix(f)
FMatPullback = Functor(n -> length(n), f->pullback_matrix(f),
Category(SkelFinSet()), Category(MatC{Number}()))


FMatPushforward = Functor(n->MatrixDom(n.n),
pushforward_matrix,
FinSetCat(), FinVect()
Category(FinSetC()), Category(FinVect())
)

""" extend(X::Sheaf{T, FVectPullback}, cover::ColimCover, sections::Vector{Vector{R}}; check=true, debug=false) where {T<:DiagramTopology, R}
extend(X::Sheaf, cover, sections; kw...) = if functor(X) == FMatPullback
extend_mat(X,cover,sections; kw...)
elseif functor(X) == FVectPullback
extend_vect(X,cover,sections; kw...)
else
error("Cannot extend $X")
end

""" extend(X::Sheaf{T}, cover::ColimCover, sections::Vector{Vector{R}}; check=true, debug=false) where {T<:DiagramTopology, R}
This method implements the extension operation for the diagram topology on FinSet for the Free Vector Space functor.
The implementation does copies the value of the ith section into the jth spot as indicated by the legs of the cocone.
"""
function extend(X::Sheaf{T, FVectPullback}, cover::ColimCover, sections::Vector{Vector{R}}; check=true, debug=false) where {T<:DiagramTopology, R}
length(sections) == length(legs(cover)) || throw(ArgumentError("There are $(length(sections)) but only $(length(legs(cover))) legs in the cover"))
v = zeros(R, apex(cover))
function extend_vect(X::Sheaf{T}, cover::ColimCover,
sections::Vector{Vector{R}}; check=true, debug=false
) where {T<:DiagramTopology, R}
length(sections) == length(legs(cover)) || throw(ArgumentError(
"There are $(length(sections)) but only $(length(legs(cover))) legs in the cover"))
v = zeros(R, length(apex(cover)))
if check
match_errors = diagnose_match(X, cover, sections; debug=debug)
length(match_errors) == 0 || throw(MatchingError(match_errors))
Expand All @@ -77,14 +98,18 @@ function extend(X::Sheaf{T, FVectPullback}, cover::ColimCover, sections::Vector{
return v
end

function extend(X::Sheaf{T, FMatPullback}, cover::ColimCover, sections::Vector{Vector{R}};check=true, debug=false) where {T<:DiagramTopology, R}
length(sections) == length(legs(cover)) || throw(ArgumentError("There are $(length(sections)) but only $(length(legs(cover))) legs in the cover"))
v = zeros(R, apex(cover))

function extend_mat(X::Sheaf{T}, cover::ColimCover,
sections::Vector{Vector{R}};check=true, debug=false
) where {T<:DiagramTopology, R}
length(sections) == length(legs(cover)) || throw(ArgumentError(
"There are $(length(sections)) but only $(length(legs(cover))) legs in the cover"))
v = zeros(R, length(apex(cover)))
if check
match_errors = diagnose_match(X, cover, sections; debug=debug)
length(match_errors) == 0 || throw(MatchingError(match_errors))
end
f = copair(legs(cover))
M = do_hom_map(functor(X), f)
f = copair[TypedCatWithCoproducts(SkelFinSet())](legs(cover))
M = hom_map(functor(X), f)
return Float64.(M) \ direct_sum(sections)
end
65 changes: 30 additions & 35 deletions src/sheaves/Sheaves.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
module Sheaves

using StructEquality
using LinearAlgebra
using SparseArrays
using ...CategoricalAlgebra
using ...CategoricalAlgebra.Categories
using ...CategoricalAlgebra.FinSets
using ...CategoricalAlgebra.Matrices
using ...BasicSets, ...CategoricalAlgebra
using ...CategoricalAlgebra.Misc.Matrices
using ...Theories
import ..CategoricalAlgebra.Categories: CatSize
import ...Theories: dom, codom, id, ob, hom
import ...CategoricalAlgebra: legs, apex
import ...CategoricalAlgebra.Categories: do_ob_map, do_hom_map

export AbstractSheaf, AbstractFunctor, AbstractCover, AbstractCoverage,
FinSetCat, FinVect, FinSetOp, FinSetOpT,
Expand All @@ -18,8 +16,6 @@ export AbstractSheaf, AbstractFunctor, AbstractCover, AbstractCoverage,
ColimCover, DiagramTopology, is_coverage,
Sheaf, diagnose_match, extend, restrict, MatchingError, MatchingFailure


abstract type SmallCatSize <: CatSize end
Base.zeros(T, n::FinSet) = zeros(T, length(n))
direct_sum(vs) = reduce(vcat, vs)

Expand All @@ -28,12 +24,7 @@ abstract type AbstractFunctor end
abstract type AbstractCover end
abstract type AbstractCoverage end

struct FinSetCat <: Category{FinSet, FinFunction, SmallCatSize} end

const FinSetOp = op(FinSetCat())
const FinSetOpT = typeof(FinSetOp)

struct Sieve{T} <: AbstractCover
@struct_hash_equal struct Sieve{T} <: AbstractCover
basis::T
end

Expand All @@ -48,17 +39,17 @@ end
legs(s::Sieve) = legs(s.basis)
apex(s::Sieve) = apex(s.basis)

struct ColimCover <: AbstractCover
colimit
@struct_hash_equal struct ColimCover <: AbstractCover
colimit::AbsColimit
end

ColimCover(d::FreeDiagram) = ColimCover(colimit(d))
ColimCover(d::FreeDiagram) = ColimCover(colimit[SkelFinSet()](getvalue(d)))

legs(s::ColimCover) = legs(s.colimit)
apex(s::ColimCover) = apex(s.colimit)
Base.enumerate(s::ColimCover) = enumerate(legs(s))

struct DiagramTopology <: AbstractCoverage end
@struct_hash_equal struct DiagramTopology <: AbstractCoverage end

function is_coverage(top::AbstractCoverage, S::AbstractCover, object)
error("To implement a GTopology, you have to be able to check if a Sieve covers an object.")
Expand All @@ -68,7 +59,7 @@ function is_coverage(::DiagramTopology, S::ColimCover, object)
apex(S) == object
end

struct Sheaf{T<:AbstractCoverage,F<:Functor} <: AbstractSheaf
@struct_hash_equal struct Sheaf{T<:AbstractCoverage,F<:Functor} <: AbstractSheaf
coverage::T
functor::F
end
Expand All @@ -78,7 +69,7 @@ functor(s::Sheaf) = s.functor

abstract type AbstractSection end

struct Section{S,D,V} <: AbstractSection
@struct_hash_equal struct Section{S,D,V} <: AbstractSection
sheaf::S
domain::D
value::V
Expand All @@ -96,27 +87,33 @@ end
""" restrict(X::AbstractSheaf, s::Data, f::Hom) where {Data, Hom}
Restrict a section along a morphism in the sheaf.
This is to apply the sheaf's functor to the morphism f and then apply that function to the data supplied.
We can assume that you can directly call that applied functor on data, because sheaves take C to **Set**.
This is to apply the sheaf's functor to the morphism f and then apply that
function to the data supplied. We can assume that you can directly call that
applied functor on data, because sheaves take C to **Set**.
"""
function restrict(X::AbstractSheaf, s::Data, f::Hom) where {Data, Hom}
do_hom_map(functor(X), f)(s)
hom_map(functor(X), f)(s)
end

""" extend(X::AbstractSheaf, cover::AbstractCover, sections::AbstractVector)
Extend a collection of sections to the unique section that restricts to the sections provided.
The `sections` vector needs to be indexed in the same order as `enumerate(cover)`.
Extend a collection of sections to the unique section that restricts to the
sections provided. The `sections` vector needs to be indexed in the same order
as `enumerate(cover)`.
"""
function extend(X::AbstractSheaf, cover::AbstractCover, sections::AbstractVector)
error("In order to define a sheaf, you must implement restrict and extend")
end
##################################
# THIS SHOULD BE DONE WITH A GAT #
##################################
# function extend(X::AbstractSheaf, cover::AbstractCover, sections::AbstractVector)
# error("In order to define a sheaf, you must implement restrict and extend")
# end

""" MatchingFailure
An type for when sections over a sheaf fail to match, that is when they don't agree on the overlaps implied by a cover.
A type for when sections over a sheaf fail to match, that is when they don't
agree on the overlaps implied by a cover.
"""
struct MatchingFailure
@struct_hash_equal struct MatchingFailure
sheaf::AbstractSheaf
hom1
hom2
Expand All @@ -133,7 +130,7 @@ Base.show(io::IO, m::MatchingFailure) = println(io, "$(typeof(m).name.name): Sec
An Exception type for when sections over a sheaf fail to match, that is when they don't agree on the overlaps implied by a cover.
This stores the list MatchingFailures encountered when walking the cover.
"""
struct MatchingError{T} <: Exception where T<:MatchingFailure
@struct_hash_equal struct MatchingError{T} <: Exception where T<:MatchingFailure
failures::Vector{T}
end

Expand All @@ -159,11 +156,9 @@ function diagnose_match(X::Sheaf, cover, sections; debug=false)
if i >= j
continue
end
P = pullback(l₁,l₂)
P = pullback[SkelFinSet()](l₁,l₂)
if debug
println("Computing intersection of opens $i,$j")
@show l₁
@show l₂
@show apex(P)
end
v₁ = restrict(X, sections[i], legs(P)[1])
Expand All @@ -176,4 +171,4 @@ end

include("FVect.jl")

end
end # module
4 changes: 1 addition & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using Test

# include("aqua.jl") # can uncomment once PR is more complete

@testset "Theories" begin
include("theories/runtests.jl") # TO SAVE TIME
end
Expand All @@ -27,7 +25,7 @@ end
end

@testset "Programs" begin
include("programs/Programs.jl")
include("programs/runtests.jl")
end

@testset "Parsers" begin
Expand Down
37 changes: 19 additions & 18 deletions test/sheaves/sheaves.jl
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
using Test
using Catlab.CategoricalAlgebra
using Catlab.CategoricalAlgebra.Categories
using Catlab.Sheaves
module TestSheaves

using Test, Catlab
using Catlab.CategoricalAlgebra.Misc.Matrices

import Catlab.CategoricalAlgebra.Categories: do_ob_map, do_hom_map
using Catlab.CategoricalAlgebra.Matrices: MatrixDom
import Catlab.Sheaves: pullback_matrix, FinSetCat

VectSheaf = Sheaf(DiagramTopology(), FVectPullback())
VectSheafMat = Sheaf(DiagramTopology(), FMatPullback())
VectSheaf = Sheaf(DiagramTopology(), FVectPullback)
VectSheafMat = Sheaf(DiagramTopology(), FMatPullback)

f = FinFunction([1,2], 3)
g = FinFunction([1,2], 3)

@test isa(FinSetCat(), Category)
@test isa(FinVect(), Category)
@test do_hom_map(FVectPullback(), FinFunction([1,2,2],4))(1:4) == [1,2,2]
@test implements(FinSetC(), ThCategory)
@test implements(FinVect(), ThCategory)

@test hom_map(FVectPullback, FinFunction([1,2,2],4))(1:4) == [1,2,2]

@test pullback_matrix(FinFunction([1,2,2], 4)) == [1 0 0 0; 0 1 0 0; 0 1 0 0]
@test do_ob_map(FMatPullback(), FinSet(3)) == MatrixDom{AbstractMatrix}(3)
@test do_hom_map(FMatPullback(), FinFunction([1,2,2], 4)) == [1 0 0 0; 0 1 0 0; 0 1 0 0]
S = ColimCover(pushout(f,g))
@test ob_map(FMatPullback, FinSetInt(3)) == 3
@test hom_map(FMatPullback, FinFunction([1,2,2], 4)) == [1 0 0 0; 0 1 0 0; 0 1 0 0]
S = ColimCover(pushout[SkelFinSet()](f,g))

extend(VectSheaf, S, [[1.0, 2,3], [1,2.0,6]])
@test_throws MatchingError extend(VectSheaf, S, [[1.0, 2,3], [1,3.0,6]])
# extend(VectSheaf, S, [[1.0, 2,3], [1,3.0,6]])

D = FreeDiagram(FinSet.([3,2,3]), # list of objects
D = FreeGraph(FinSetInt.([3,2,3]), # list of objects
[ # list of (hom, src, tgt) tuples
(FinFunction([1,2], 3), 2,1),
(FinFunction([1,2], 3), 2,3),
]
)
]; cat=SkelFinSet()
) |> FreeDiagram

K = ColimCover(D)

Expand All @@ -51,4 +50,6 @@ section_data_bad = [Float64[1,2,3],
@test_throws MatchingError extend(VectSheafMat, K, section_data_bad)

# if we disable the checks, VectSheafMat will solve a least squares problem instead of last write wins.
@test extend(VectSheafMat, K, section_data_bad, check=false) != extend(VectSheaf, K, section_data_bad, check=false)
@test extend(VectSheafMat, K, section_data_bad, check=false) != extend(VectSheaf, K, section_data_bad, check=false)

end # module

0 comments on commit 38b0f13

Please sign in to comment.