@@ -358,74 +358,118 @@ function toDot(dfg::GraphsDFG)
358358 return String (data)
359359end
360360
361- function findShortestPathDijkstra (
361+ # API design NOTE:
362+ # Do not create new Verbs or Nouns for metric vs. topological pathfinding. findPaths is the universal router... findPaths(..., metric)
363+ # for now we only look at topological paths.
364+
365+ function findPaths (:: typeof (all_simple_paths), dfg, from:: Symbol , to:: Symbol ; kwargs... )
366+ gpaths = Graphs. all_simple_paths (dfg. g, dfg. g. labels[from], dfg. g. labels[to]; kwargs... )
367+ return map (p -> (path = map (i -> dfg. g. labels[i], p), dist = length (p) - 1 ), gpaths)
368+ end
369+
370+ function findPaths (
371+ :: typeof (yen_k_shortest_paths),
362372 dfg:: GraphsDFG ,
363373 from:: Symbol ,
364- to:: Symbol ;
365- labelFilterVariables:: Union{Function, Nothing} = nothing ,
366- labelFilterFactors:: Union{Function, Nothing} = nothing ,
367- tagsFilterVariables:: Union{Function, Nothing} = nothing ,
368- tagsFilterFactors:: Union{Function, Nothing} = nothing ,
369- typeFilterVariables:: Union{Function, Nothing} = nothing ,
370- typeFilterFactors:: Union{Function, Nothing} = nothing ,
371- solvableFilter:: Union{Function, Nothing} = nothing ,
372- initialized:: Union{Nothing, Bool} = nothing ,
374+ to:: Symbol ,
375+ k:: Int ;
376+ distmx = weights (dfg. g),
377+ kwargs... ,
373378)
374- duplicate =
375- ! isnothing (labelFilterVariables) ||
376- ! isnothing (labelFilterFactors) ||
377- ! isnothing (tagsFilterVariables) ||
378- ! isnothing (tagsFilterFactors) ||
379- ! isnothing (typeFilterVariables) ||
380- ! isnothing (typeFilterFactors) ||
381- ! isnothing (initialized) ||
382- ! isnothing (solvableFilter)
383-
384- dfg_ = if duplicate
385- # use copy if filter is being applied
386- varList = ls (
387- dfg;
388- labelFilter = labelFilterVariables,
389- tagsFilter = tagsFilterVariables,
390- typeFilter = typeFilterVariables,
391- solvableFilter,
392- )
393- fctList = lsf (
394- dfg;
395- labelFilter = labelFilterFactors,
396- tagsFilter = tagsFilterFactors,
397- typeFilter = typeFilterFactors,
398- solvableFilter,
399- )
379+ (; paths, dists) = Graphs. yen_k_shortest_paths (
380+ dfg. g,
381+ dfg. g. labels[from],
382+ dfg. g. labels[to],
383+ distmx,
384+ k;
385+ kwargs... ,
386+ )
387+ return map (zip (paths, dists)) do (path, dist)
388+ return (path = map (i -> dfg. g. labels[i], path), dist = dist)
389+ end
390+ end
400391
401- varList = if initialized != = nothing
402- initmask = isInitialized .(dfg, varList) .== initialized
403- varList[initmask]
392+ # note with default heuristic this is just dijkstra's algorithm
393+ function findPaths (
394+ :: typeof (a_star),
395+ dfg:: GraphsDFG ,
396+ from:: Symbol ,
397+ to:: Symbol ;
398+ distmx:: AbstractMatrix{T} = weights (dfg. g),
399+ heuristic = nothing ,
400+ ) where {T}
401+ # TODO make it easier to use label in the heuristic
402+ heuristic = something (heuristic, (n) -> zero (T))
403+ edgepath = Graphs. a_star (dfg. g, dfg. g. labels[from], dfg. g. labels[to], distmx, heuristic)
404+
405+ if isempty (edgepath)
406+ return @NamedTuple {path:: Vector{Symbol} , dist:: T }[]
407+ end
408+
409+ path = [dfg. g. labels[edgepath[1 ]. src]]
410+ dist = zero (T)
411+ for (; dst, src) in edgepath
412+ push! (path, dfg. g. labels[dst])
413+ dist += distmx[src, dst]
414+ end
415+
416+ return [(path = path, dist = dist)]
417+ end
418+
419+ # TODO Move findPaths and findPath to AbstractDFG services as default implementations.
420+ function findPaths (
421+ dfg:: AbstractDFG ,
422+ from:: Symbol ,
423+ to:: Symbol ,
424+ k:: Int ;
425+ variableLabels:: Union{Nothing, Vector{Symbol}} = nothing ,
426+ factorLabels:: Union{Nothing, Vector{Symbol}} = nothing ,
427+ kwargs... ,
428+ )
429+ # If the user provided restricted lists, build the subgraph automatically
430+ active_dfg =
431+ if isa (dfg, GraphsDFG) && isnothing (variableLabels) && isnothing (factorLabels)
432+ dfg
404433 else
405- varList
434+ vlabels = something (variableLabels, listVariables (dfg))
435+ flabels = something (factorLabels, listFactors (dfg))
436+ labels = vcat (vlabels, flabels)
437+ DFG. getSubgraph (
438+ GraphsDFG{NoSolverParams, VariableSkeleton, FactorSkeleton},
439+ dfg,
440+ labels,
441+ )
406442 end
407- DFG. deepcopyGraph (typeof (dfg), dfg, varList, fctList)
443+ ! hasVariable (active_dfg, from) &&
444+ ! hasFactor (active_dfg, from) &&
445+ throw (DFG. LabelNotFoundError (from))
446+ ! hasVariable (active_dfg, to) &&
447+ ! hasFactor (active_dfg, to) &&
448+ throw (DFG. LabelNotFoundError (to))
449+
450+ # optimization for k=1 since A* is more efficient than Yen's for single shortest path
451+ if k == 1
452+ return findPaths (a_star, active_dfg, from, to; kwargs... )
408453 else
409- # no filter can be used directly
410- dfg
411- end
412-
413- if ! (hasVariable (dfg_, from) || hasFactor (dfg_, from)) ||
414- ! (hasVariable (dfg_, to) || hasFactor (dfg_, to))
415- # assume filters excluded either `to` or `from` and hence no shortest path
416- return Symbol[]
454+ return findPaths (yen_k_shortest_paths, active_dfg, from, to, k; kwargs... )
417455 end
418- # GraphsDFG internally uses Integers
419- frI = dfg_. g. labels[from]
420- toI = dfg_. g. labels[to]
456+ end
421457
422- # get shortest path from graph provider
423- path_state = Graphs. dijkstra_shortest_paths (dfg_. g. graph, [frI;])
424- path = Graphs. enumerate_paths (path_state, toI)
425- dijkpath = map (x -> dfg_. g. labels[x], path)
458+ function findPath (
459+ dfg:: AbstractDFG ,
460+ from:: Symbol ,
461+ to:: Symbol ;
462+ variableLabels:: Union{Nothing, Vector{Symbol}} = nothing ,
463+ factorLabels:: Union{Nothing, Vector{Symbol}} = nothing ,
464+ kwargs... ,
465+ )
466+ paths = findPaths (dfg, from, to, 1 ; variableLabels, factorLabels, kwargs... )
426467
427- # return the list of symbols
428- return dijkpath
468+ if isempty (paths)
469+ return nothing
470+ else
471+ return first (paths)
472+ end
429473end
430474
431475export bfs_tree
0 commit comments