Skip to content

Commit

Permalink
Overlap iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
Kris Brown committed Jun 30, 2023
1 parent b05c01d commit fd4427c
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 30 deletions.
82 changes: 56 additions & 26 deletions src/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1734,6 +1734,28 @@ function Base.iterate(Sub::SubobjectIterator, state=SubobjectIteratorState())
end


struct OverlapIterator
top::ACSet
others::Vector{ACSet}
function OverlapIterator(Xs::Vector{T}) where T<:ACSet
t, o... = Xs[sortperm(total_parts.(Xs))]
new(t, o)
end
end

function Base.collect(itr::OverlapIterator)
res = []; for sp in itr push!(res, sp) end; res
end

mutable struct OverlapIteratorState
curr_subobj::Union{Nothing,ACSetTransformation}
subobject_state::Iterators.Stateful{SubobjectIterator}
seen::Set{Multispan}
maps::Union{Nothing,Iterators.Stateful{<:Iterators.ProductIterator}}
OverlapIteratorState(x::ACSet) =
new(nothing, Iterators.Stateful(SubobjectIterator(x)), Set{Multispan}(), nothing)
end

# Could be cleaner/faster if we used CSetAutomorphisms to handle symmetries
"""
Given a list of ACSets X₁...Xₙ, find all multispans A ⇉ X ordered by decreasing
Expand All @@ -1747,29 +1769,35 @@ we need to cache a lot of work because we consider each such subobject
independently. This is the maps from A into all the other objects as well as the
automorphisms of A.
"""
function overlap_maps(Xs::Vector{T}) where T<:ACSet
!isempty(Xs) || error("Vector must not be empty")
Xs = Xs[sortperm(total_parts.(Xs))] # put the smallest X first
res = OrderedDict()
for subobj in hom.(SubobjectIterator(Xs[1]))
function Base.iterate(Sub::OverlapIterator, state=nothing)
state = isnothing(state) ? OverlapIteratorState(Sub.top) : state
# if we are not computing overlaps from a particular subobj,
if isnothing(state.curr_subobj) # pick the next subobj
isnothing(state.maps) || error("Inconsistent overlapiterator state")
if isempty(state.subobject_state)
return nothing
else
state.curr_subobj = hom(popfirst!(state.subobject_state))
return Base.iterate(Sub, state)
end
elseif isnothing(state.maps) # compute all the maps out of curr subobj
subobj = state.curr_subobj
abs_subobj = abstract_attributes(dom(subobj)) subobj
Y = dom(abs_subobj)
# don't repeat work if already computed syms/maps for something iso to Y
seen = false
for (Y′, Y′maps) in collect(res)
σ = isomorphism(Y′, Y)
for res in state.seen
σ = isomorphism(Y, apex(res))

Check warning on line 1789 in src/categorical_algebra/CSets.jl

View check run for this annotation

Codecov / codecov/patch

src/categorical_algebra/CSets.jl#L1788-L1789

Added lines #L1788 - L1789 were not covered by tests
if !isnothing(σ)
push!(Y′maps[1], σ abs_subobj)
seen = true
break
state.subobj = nothing

Check warning on line 1791 in src/categorical_algebra/CSets.jl

View check run for this annotation

Codecov / codecov/patch

src/categorical_algebra/CSets.jl#L1791

Added line #L1791 was not covered by tests
return (Multispan(map(m->σm, res)), state)
end
end
if seen continue end
maps = Vector{ACSetTransformation}[[abs_subobj]]
# Compute the automorphisms so that we can remove spurious symmetries
syms = isomorphisms(Y, Y)
# Get monic maps from Y into each of the objects. The first comes for free
maps = Vector{ACSetTransformation}[[abs_subobj]]
for X in Xs[2:end]
for X in Sub.others
fs = homomorphisms(Y, X; monic=true)
real_fs = Set() # quotient fs via automorphisms of Y
for f in fs
Expand All @@ -1783,20 +1811,21 @@ function overlap_maps(Xs::Vector{T}) where T<:ACSet
push!(maps,collect(real_fs))
end
end
if length(maps) == length(Xs)
res[Y] = maps
if length(maps) == length(Sub.others) + 1
state.maps = Iterators.Stateful(Iterators.product(maps...))
else
state.curr_subobj = nothing
end
return Base.iterate(Sub, state)
elseif isempty(state.maps)
state.maps = nothing; state.curr_subobj = nothing
return Base.iterate(Sub, state)
else
return (Multispan(collect(popfirst!(state.maps))), state)
end
return res
end

function partial_overlaps(Xs::Vector{T}) where T<:ACSet
res = []
for (_,v) in collect(overlap_maps(Xs))
append!(res, [Multispan(collect(ms)) for ms in Iterators.product(v...)])
end
return res
end
partial_overlaps(Xs::Vector{T}) where T<:ACSet = OverlapIterator(Xs)
partial_overlaps(Xs::ACSet...) = Xs |> collect |> partial_overlaps

""" Compute the Maximimum Common C-Sets from a vector of C-Sets.
Expand All @@ -1812,14 +1841,15 @@ If there are attributes, we ignore these and use variables in the apex of the
overlap.
"""
function maximum_common_subobject(Xs::Vector{T}) where T <: ACSet
it = overlap_maps(Xs)
it = partial_overlaps(Xs)
osize = -1
res = OrderedDict()
for (apx, overlap) in it
for overlap in it
apx = apex(overlap)
size = total_parts(apx)
osize = osize == -1 ? size : osize
if size < osize return res end
res[apx]=overlap
res[apx] = overlap
end
return res
end
Expand Down
14 changes: 10 additions & 4 deletions test/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -657,9 +657,15 @@ subRG, sos = subobject_graph(path_graph(ReflexiveGraph, 2))
@test is_isomorphic(subG, subRG)

# Partial overlaps
G = path_graph(Graph, 2)
os = partial_overlaps(G,G)
@test length(os) == 7 # ⊤, ••, 4x •, ⊥
G,H = path_graph.(Graph, 2:3)
os = collect(partial_overlaps(G,G))
@test length(os) == 7 # ⊤, ••, 4× •, ⊥

po = partial_overlaps([G,H])
@test length(collect(po))==12 # 2×⊤, 3ו•, 6× •, ⊥
@test all(m -> apex(m) == G, Iterators.take(po, 2)) # first two are •→•
@test all(m -> apex(m) == Graph(2),
Iterators.take(Iterators.drop(po, 2), 3)) # next three are • •

# Maximum Common C-Set
######################
Expand All @@ -674,7 +680,7 @@ end
g2 = @acset WeightedGraph{Bool} begin
V=3; E=3; src=[1,2,3]; tgt=[2,3,3]; weight=[true,false,false]
end
(apx1, ((L1,),(R1,))), (apx2, ((L2,),(R2,))) = collect(maximum_common_subobject(g1, g2))
(apx1,(L1,R1)), (apx2,(L2,R2)) = collect(maximum_common_subobject(g1, g2))
apex1 = @acset WeightedGraph{Bool} begin
V=3; E=2; Weight=2; src=[1,2]; tgt=[2,3]; weight=AttrVar.(1:2)
end
Expand Down

0 comments on commit fd4427c

Please sign in to comment.