@@ -7,7 +7,7 @@ export ACSetTransformation, CSetTransformation, StructACSetTransformation,
77 ACSetHomomorphismAlgorithm, BacktrackingSearch, HomomorphismQuery,
88 components, type_components, force,
99 naturality_failures, show_naturality_failures, is_natural,
10- homomorphism, homomorphisms, is_homomorphic,
10+ homomorphism, homomorphisms, debug_homomorphisms, is_homomorphic,
1111 isomorphism, isomorphisms, is_isomorphic,
1212 @acset_transformation , @acset_transformations ,
1313 subobject_graph, partial_overlaps, maximum_common_subobject
@@ -766,7 +766,7 @@ homomorphism(X::ACSet, Y::ACSet; alg=BacktrackingSearch(), kw...) =
766766function homomorphism (X:: ACSet , Y:: ACSet , alg:: BacktrackingSearch ; kw... )
767767 result = nothing
768768 backtracking_search (X, Y; kw... ) do α
769- result = α ; return true
769+ result = get_hom (α) ; return true
770770 end
771771 result
772772end
@@ -782,11 +782,19 @@ homomorphisms(X::ACSet, Y::ACSet; alg=BacktrackingSearch(), kw...) =
782782function homomorphisms (X:: ACSet , Y:: ACSet , alg:: BacktrackingSearch ; kw... )
783783 results = []
784784 backtracking_search (X, Y; kw... ) do α
785- push! (results, map_components (deepcopy, α )); return false
785+ push! (results, map_components (deepcopy, get_hom (α) )); return false
786786 end
787787 results
788788end
789789
790+ function debug_homomorphisms (X:: ACSet , Y:: ACSet ; kw... )
791+ results = []
792+ m = backtracking_search (X, Y; debug= true , kw... ) do α
793+ push! (results, map_components (deepcopy, get_hom (α))); return false
794+ end
795+ results => m. debug
796+ end
797+
790798""" Is the first attributed ``C``-set homomorphic to the second?
791799
792800This function generally reduces to [`homomorphism`](@ref) but certain algorithms
@@ -844,6 +852,53 @@ partial_assignments(x::AbstractVector; is_attr=false) =
844852in_hom (S, c) = [dom (S,f) => f for f in hom (S) if codom (S,f) == c]
845853out_hom (S, c) = [f => codom (S,f) for f in hom (S) if dom (S,f) == c]
846854
855+ """ Keep track of progress through backtracking homomorphism search."""
856+ mutable struct BacktrackingTree
857+ node:: Union{Nothing,Pair{Symbol,Int}}
858+ success:: Bool
859+ asgn:: NamedTuple
860+ children:: OrderedDict{Int,BacktrackingTree}
861+ BacktrackingTree () = new (nothing , false , (;), OrderedDict {Int,BacktrackingTree} ())
862+ end
863+ """ A backtracking tree plus a pointer to a node in the tree"""
864+ struct BacktrackingTreePt
865+ t:: BacktrackingTree
866+ curr:: Vector{Int}
867+ BacktrackingTreePt () = new (BacktrackingTree (),Int[])
868+ end
869+ function Base. push! (tc:: BacktrackingTreePt , c:: Symbol , x:: Int , y:: Int , asgn)
870+ t = tc. t[tc. curr]
871+ t. node = c => x
872+ t. children[y] = BacktrackingTree ()
873+ t. children[y]. asgn = deepcopy (asgn)
874+ push! (tc. curr, y)
875+ return true
876+ end
877+ function Base. delete! (tc:: BacktrackingTreePt , c:: Symbol , x:: Int , y:: Int )
878+ t = tc. t[tc. curr[1 : end - 1 ]]
879+ t. node == (c=> x) || error (" Bad remove $c #$x ->$y " )
880+ pop! (tc. curr)
881+ end
882+ function success (tc:: BacktrackingTreePt )
883+ tc. t[tc. curr]. success = true
884+ end
885+ function Base. show (io:: IO , t:: BacktrackingTree )
886+ if ! isnothing (t. node)
887+ print (io," {" ); print (io, t. node[1 ]); print (io, t. node[2 ]); print (io," }" );
888+ end
889+ print (io, " [" )
890+ for (k,v) in collect (t. children)
891+ print (io, k); print (io, v); print (io, " ," )
892+ end
893+ if ! isempty (t. children) print (io," \b " ) end
894+ print (io," ]" )
895+ end
896+ function Base. getindex (t:: BacktrackingTree , curr:: Vector{Int} )
897+ for c in curr
898+ t = t. children[c]
899+ end
900+ t
901+ end
847902
848903""" Internal state for backtracking search for ACSet homomorphisms.
849904
@@ -862,11 +917,27 @@ struct BacktrackingState{
862917 dom:: Dom
863918 codom:: Codom
864919 type_components:: LooseFun
920+ debug:: Union{Nothing,BacktrackingTreePt}
921+ end
922+
923+ """ Extract an ACSetTransformation from BacktrackingState"""
924+ function get_hom (state:: BacktrackingState )
925+ if any (!= (identity), state. type_components)
926+ return LooseACSetTransformation (
927+ state. assignment, state. type_components, state. dom, state. codom)
928+ else
929+ S = acset_schema (state. dom)
930+ od = Dict {Symbol,Vector{Int}} (k=> (state. assignment[k]) for k in objects (S))
931+ ad = Dict (k=> last .(state. assignment[k]) for k in attrtypes (S))
932+ comps = merge (NamedTuple (od),NamedTuple (ad))
933+ return ACSetTransformation (comps, state. dom, state. codom)
934+ end
865935end
866936
937+
867938function backtracking_search (f, X:: ACSet , Y:: ACSet ;
868939 monic= false , iso= false , random= false ,
869- type_components= (;), initial= (;), error_failures= false )
940+ type_components= (;), initial= (;), error_failures= false , debug = false )
870941 S, Sy = acset_schema .([X,Y])
871942 S == Sy || error (" Schemas must match for morphism search" )
872943 Ob = Tuple (objects (S))
@@ -922,7 +993,8 @@ function backtracking_search(f, X::ACSet, Y::ACSet;
922993 loosefuns = NamedTuple {Attr} (
923994 isnothing (type_components) ? identity : get (type_components, c, identity) for c in Attr)
924995 state = BacktrackingState (assignment, assignment_depth,
925- inv_assignment, X, Y, loosefuns)
996+ inv_assignment, X, Y, loosefuns,
997+ debug ? BacktrackingTreePt () : nothing )
926998
927999 # Make any initial assignments, failing immediately if inconsistent.
9281000 for (c, c_assignments) in pairs (initial)
@@ -937,39 +1009,31 @@ function backtracking_search(f, X::ACSet, Y::ACSet;
9371009 end
9381010 end
9391011 # Start the main recursion for backtracking search.
940- backtracking_search (f, state, 1 ; random= random)
1012+ backtracking_search (f, state, 1 ; random= random, toplevel = true )
9411013end
9421014
9431015function backtracking_search (f, state:: BacktrackingState , depth:: Int ;
944- random= false )
1016+ random= false , toplevel = false )
9451017 # Choose the next unassigned element.
9461018 mrv, mrv_elem = find_mrv_elem (state, depth)
9471019 if isnothing (mrv_elem)
948- # No unassigned elements remain, so we have a complete assignment.
949- if any (!= (identity), state. type_components)
950- return f (LooseACSetTransformation (
951- state. assignment, state. type_components, state. dom, state. codom))
952- else
953- S = acset_schema (state. dom)
954- od = Dict {Symbol,Vector{Int}} (k=> (state. assignment[k]) for k in objects (S))
955- ad = Dict (k=> last .(state. assignment[k]) for k in attrtypes (S))
956- comps = merge (NamedTuple (od),NamedTuple (ad))
957- return f (ACSetTransformation (comps, state. dom, state. codom))
958- end
1020+ isnothing (state. debug) || success (state. debug)
1021+ return f (state)
9591022 elseif mrv == 0
9601023 # An element has no allowable assignment, so we must backtrack.
9611024 return false
9621025 end
963- c, x = mrv_elem
1026+ c, x, ys = mrv_elem
9641027
9651028 # Attempt all assignments of the chosen element.
966- Y = state. codom
967- for y in (random ? shuffle : identity)(parts (Y, c))
1029+ for y in (random ? shuffle : identity)(ys)
9681030 (assign_elem! (state, depth, c, x, y)
1031+ && (isnothing (state. debug) ? true : push! (state. debug, c, x, y, state. assignment))
9691032 && backtracking_search (f, state, depth + 1 )) && return true
9701033 unassign_elem! (state, depth, c, x)
1034+ isnothing (state. debug) || delete! (state. debug, c, x, state. assignment[c][x])
9711035 end
972- return false
1036+ return toplevel ? state : false # return state to recover debug tree
9731037end
9741038
9751039""" Find an unassigned element having the minimum remaining values (MRV).
@@ -980,9 +1044,12 @@ function find_mrv_elem(state::BacktrackingState, depth)
9801044 Y = state. codom
9811045 for c in ob (S), (x, y) in enumerate (state. assignment[c])
9821046 y == 0 || continue
983- n = count (can_assign_elem (state, depth, c, x, y) for y in parts (Y, c))
1047+ ys = filter (parts (Y,c)) do y
1048+ can_assign_elem (state, depth, c, x, y)
1049+ end
1050+ n = length (ys)
9841051 if n < mrv
985- mrv, mrv_elem = n, (c, x)
1052+ mrv, mrv_elem = n, (c, x, ys )
9861053 end
9871054 end
9881055 (mrv, mrv_elem)
0 commit comments