Skip to content

Commit

Permalink
valid constraint for homsearch
Browse files Browse the repository at this point in the history
  • Loading branch information
Kris Brown committed Dec 8, 2023
1 parent 0437247 commit a6732c5
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 2 deletions.
20 changes: 18 additions & 2 deletions src/categorical_algebra/HomSearch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ default, a backtracking search algorithm is used ([`BacktrackingSearch`](@ref)).
Use the keyword argument error_failures = true to get errors explaining
any immediate inconsistencies in specified initial data.
The keyword `valid` accepts a Dict{Ob->Dict{Int->Union{Nothing, Set{Int}}}}
For each part of the domain, we have the option to give a constraint stating
which parts of the codomain are allowable values for assignment. E.g.
`valid=Dict(:E => Dict(2=>Set([2,4,6])))` would only find matches which assigned
edge#2 to edge #2, #4, or #6 in the codomain.
See also: [`homomorphisms`](@ref), [`isomorphism`](@ref).
"""
homomorphism(X::ACSet, Y::ACSet; alg=BacktrackingSearch(), kw...) =
Expand Down Expand Up @@ -170,17 +176,19 @@ been bound.
struct BacktrackingState{
Dom <: ACSet, Codom <: ACSet,
Assign <: NamedTuple, PartialAssign <: NamedTuple, LooseFun <: NamedTuple,
Valid <: NamedTuple
}
assignment::Assign
assignment_depth::Assign
inv_assignment::PartialAssign
dom::Dom
codom::Codom
type_components::LooseFun
valid_range::Valid
end

function backtracking_search(f, X::ACSet, Y::ACSet;
monic=false, iso=false, random=false,
monic=false, iso=false, random=false, valid=(;),
type_components=(;), initial=(;), error_failures=false)
S, Sy = acset_schema.([X,Y])
S == Sy || error("Schemas must match for morphism search")
Expand Down Expand Up @@ -225,6 +233,10 @@ function backtracking_search(f, X::ACSet, Y::ACSet;
error(sprint(show_naturality_failures, uns))
end

valid_nt = NamedTuple{Ob}(let dic = get(valid, c, Dict());
Union{Set{Int},Nothing}[haskey(dic,p) ? Set(dic[p]) : nothing for p in parts(X,c)]
end for c in Ob)

# Initialize state variables for search.
assignment = merge(
NamedTuple{Ob}(zeros(Int, nparts(X, c)) for c in Ob),
Expand All @@ -237,7 +249,7 @@ function backtracking_search(f, X::ACSet, Y::ACSet;
loosefuns = NamedTuple{Attr}(
isnothing(type_components) ? identity : get(type_components, c, identity) for c in Attr)
state = BacktrackingState(assignment, assignment_depth,
inv_assignment, X, Y, loosefuns)
inv_assignment, X, Y, loosefuns, valid_nt)

# Make any initial assignments, failing immediately if inconsistent.
for (c, c_assignments) in pairs(initial)
Expand Down Expand Up @@ -337,6 +349,10 @@ assign_elem!(state::BacktrackingState{<:DynamicACSet}, depth, c, x, y) =
return false
end

if !isnothing(state.valid_range[c][x]) && y state.valid_range[c][x]
return false
end

# Check attributes first to fail as quickly as possible.
X, Y = state.dom, state.codom
@ct_ctrl for (f, _, d) in attrs(S; from=c)
Expand Down
4 changes: 4 additions & 0 deletions test/categorical_algebra/HomSearch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ add_edge!(g2, 1, 2) # double arrow
@test length(homomorphisms(g2, g1, monic=[:E])) == 2 # two for 2->3
@test length(homomorphisms(g2, g1, iso=[:E])) == 0

# valid constraint
@test length(homomorphisms(g2, g1; valid=(V=Dict([1 => [1,3]]),))) == 3
@test length(homomorphisms(g2, g1; valid=(E=Dict([1 => [1,3]]),))) == 2

# Loose
s1 = SetAttr{Int}()
add_part!(s1, :X, f=1)
Expand Down

0 comments on commit a6732c5

Please sign in to comment.