Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 62 additions & 37 deletions src/Deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ end
# - Orphaned factors (where the subgraph does not contain all the related variables) are not included.
# Related:
# - [`copyGraph!`](@ref)
# - [`buildSubgraph`](@ref)
# - [`getSubgraph`](@ref)
# - [`listNeighborhood`](@ref)
# - [`deepcopyGraph`](@ref)
# """
Expand All @@ -455,7 +455,7 @@ NOTE: `copyGraphMetadata` is deprecated – use agent/graph Bloblets instead.
Related:
- [`deepcopyGraph`](@ref)
- [`deepcopyGraph!`](@ref)
- [`buildSubgraph`](@ref)
- [`getSubgraph`](@ref)
- [`listNeighborhood`](@ref)
- [`mergeGraph!`](@ref)
"""
Expand Down Expand Up @@ -528,7 +528,7 @@ Copy nodes from one graph into another graph by making deepcopies.
see [`copyGraph!`](@ref) for more detail.
Related:
- [`deepcopyGraph`](@ref)
- [`buildSubgraph`](@ref)
- [`getSubgraph`](@ref)
- [`listNeighborhood`](@ref)
- [`mergeGraph!`](@ref)
"""
Expand Down Expand Up @@ -556,7 +556,7 @@ Copy nodes from one graph into a new graph by making deepcopies.
see [`copyGraph!`](@ref) for more detail.
Related:
- [`deepcopyGraph!`](@ref)
- [`buildSubgraph`](@ref)
- [`getSubgraph`](@ref)
- [`listNeighborhood`](@ref)
- [`mergeGraph!`](@ref)
"""
Expand Down Expand Up @@ -634,40 +634,9 @@ end
# - A better noun is maybe Path or simply listFactors with a fancy filter, something like:
# - [list/get]Path(dfg, from, to; algorithm...)
# the `search` verb can also come ito play, but it is more for knn search type functions.
"""
$SIGNATURES

Relatively naive function counting linearly from-to

DevNotes
- Convert to using Graphs shortest path methods instead.
"""
#
function findFactorsBetweenNaive(
dfg::AbstractDFG,
from::Symbol,
to::Symbol,
assertSingles::Bool = false,
)
#
@info "findFactorsBetweenNaive is naive linear number method -- improvements welcome"
SRT = getVariableLabelNumber(from)
STP = getVariableLabelNumber(to)
prefix = string(from)[1]
@assert prefix == string(to)[1] "from-to prefixes must match, one is $prefix, other $(string(to)[1])"
prev = from
fctlist = Symbol[]
for num = (SRT + 1):STP
next = Symbol(prefix, num)
fct = intersect(ls(dfg, prev), ls(dfg, next))
if assertSingles
@assert length(fct) == 1 "assertSingles=true, won't return multiple factors joining variables at this time"
end
union!(fctlist, fct)
prev = next
end

return fctlist
function findFactorsBetweenNaive(args...)
return error("findFactorsBetweenNaive is obsolete, use DFG.getPath[s] instead.")
Comment thread
Affie marked this conversation as resolved.
Outdated
end

#TODO deprecate `is` is the correct verb, but rather isHomogeneous(path::Path) the form is isAdjective
Expand All @@ -690,3 +659,59 @@ function isPathFactorsHomogeneous(dfg::AbstractDFG, from::Symbol, to::Symbol)
utyp = unique(types)
return (length(utyp) == 1), utyp
end

# deprecated use filter and path seperately.
Comment thread
Affie marked this conversation as resolved.
Outdated
function findShortestPathDijkstra(
dfg::GraphsDFG,
from::Symbol,
to::Symbol;
labelFilterVariables::Union{Function, Nothing} = nothing,
labelFilterFactors::Union{Function, Nothing} = nothing,
tagsFilterVariables::Union{Function, Nothing} = nothing,
tagsFilterFactors::Union{Function, Nothing} = nothing,
typeFilterVariables::Union{Function, Nothing} = nothing,
typeFilterFactors::Union{Function, Nothing} = nothing,
solvableFilter::Union{Function, Nothing} = nothing,
initialized::Union{Nothing, Bool} = nothing,
)
Base.depwarn(
"findShortestPathDijkstra is deprecated, use getPath with `variableLabels`/`factorLabels` kwargs instead.",
:findShortestPathDijkstra,
)
any_active_filters = any(
.!isnothing.([labelFilterVariables, labelFilterFactors, tagsFilterVariables, tagsFilterFactors, typeFilterVariables, typeFilterFactors, initialized, solvableFilter]),
)

if any_active_filters
varList = listVariables(
dfg;
labelFilter = labelFilterVariables,
tagsFilter = tagsFilterVariables,
typeFilter = typeFilterVariables,
solvableFilter,
)
fctList = listFactors(
dfg;
labelFilter = labelFilterFactors,
tagsFilter = tagsFilterFactors,
typeFilter = typeFilterFactors,
solvableFilter,
)

varList = if initialized !== nothing
initmask = isInitialized.(dfg, varList) .== initialized
varList[initmask]
else
varList
end
restrict_labels = vcat(varList, fctList)
subdfg = DFG.getSubgraph(
GraphsDFG{NoSolverParams, VariableSkeleton, FactorSkeleton},
dfg,
restrict_labels,
)
return getPath(subdfg, from, to).path
else
return getPath(dfg, from, to).path
end
Comment thread
Affie marked this conversation as resolved.
end
4 changes: 3 additions & 1 deletion src/DistributedFactorGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,8 @@ const unstable_functions::Vector{Symbol} = [
:FactorSummary,
:listNeighborhood,
:listNeighbors,
:getPath,
:getPaths,
:InMemoryBlobstore,
:exists,
:compare,
Expand All @@ -442,7 +444,7 @@ const unstable_functions::Vector{Symbol} = [
:findClosestTimestamp,
:findVariablesNearTimestamp,
:findShortestPathDijkstra,
:findFactorsBetweenNaive,
:findFactorsBetweenNaive, # TODO not really used
:getAgentLabel, #TODO check and mark as public
:getGraphLabel, #TODO check and mark as public
:getDescription,
Expand Down
4 changes: 3 additions & 1 deletion src/GraphsDFG/GraphsDFG.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ import ...DistributedFactorGraphs:
lsf,
isConnected,
listNeighbors,
buildSubgraph,
getPaths,
getPath,
getSubgraph,
getBiadjacencyMatrix,
toDot,
toDotFile,
Expand Down
162 changes: 103 additions & 59 deletions src/GraphsDFG/services/GraphsDFG.jl
Original file line number Diff line number Diff line change
Expand Up @@ -358,74 +358,118 @@ function toDot(dfg::GraphsDFG)
return String(data)
end

function findShortestPathDijkstra(
#API design NOTE:
# Do not create new Verbs or Nouns for metric vs. topological pathfinding. getPaths is the universal router... getPaths(..., metric)
# for now we only look at topological paths.

function getPaths(::typeof(all_simple_paths), dfg, from::Symbol, to::Symbol; kwargs...)
gpaths = Graphs.all_simple_paths(dfg.g, dfg.g.labels[from], dfg.g.labels[to]; kwargs...)
return map(p -> (path = map(i -> dfg.g.labels[i], p), dist = length(p) - 1), gpaths)
end

function getPaths(
Comment thread
Affie marked this conversation as resolved.
Outdated
::typeof(yen_k_shortest_paths),
dfg::GraphsDFG,
from::Symbol,
to::Symbol;
labelFilterVariables::Union{Function, Nothing} = nothing,
labelFilterFactors::Union{Function, Nothing} = nothing,
tagsFilterVariables::Union{Function, Nothing} = nothing,
tagsFilterFactors::Union{Function, Nothing} = nothing,
typeFilterVariables::Union{Function, Nothing} = nothing,
typeFilterFactors::Union{Function, Nothing} = nothing,
solvableFilter::Union{Function, Nothing} = nothing,
initialized::Union{Nothing, Bool} = nothing,
to::Symbol,
k::Int;
distmx = weights(dfg.g),
kwargs...,
)
duplicate =
!isnothing(labelFilterVariables) ||
!isnothing(labelFilterFactors) ||
!isnothing(tagsFilterVariables) ||
!isnothing(tagsFilterFactors) ||
!isnothing(typeFilterVariables) ||
!isnothing(typeFilterFactors) ||
!isnothing(initialized) ||
!isnothing(solvableFilter)

dfg_ = if duplicate
# use copy if filter is being applied
varList = ls(
dfg;
labelFilter = labelFilterVariables,
tagsFilter = tagsFilterVariables,
typeFilter = typeFilterVariables,
solvableFilter,
)
fctList = lsf(
dfg;
labelFilter = labelFilterFactors,
tagsFilter = tagsFilterFactors,
typeFilter = typeFilterFactors,
solvableFilter,
)
(; paths, dists) = Graphs.yen_k_shortest_paths(
dfg.g,
dfg.g.labels[from],
dfg.g.labels[to],
distmx,
k;
kwargs...,
)
return map(zip(paths, dists)) do (path, dist)
return (path = map(i -> dfg.g.labels[i], path), dist = dist)
end
end

varList = if initialized !== nothing
initmask = isInitialized.(dfg, varList) .== initialized
varList[initmask]
else
varList
end
DFG.deepcopyGraph(typeof(dfg), dfg, varList, fctList)
else
# no filter can be used directly
dfg
# note with default heuristic this is just dijkstra's algorithm
function getPaths(
::typeof(a_star),
dfg::GraphsDFG,
from::Symbol,
to::Symbol,
::Int;
distmx::AbstractMatrix{T} = weights(dfg.g),
heuristic = nothing,
) where {T}
Comment thread
Affie marked this conversation as resolved.
Outdated
#TODO make it easier to use label in the heuristic
heuristic = something(heuristic, (n) -> zero(T))
edgepath = Graphs.a_star(dfg.g, dfg.g.labels[from], dfg.g.labels[to], distmx, heuristic)

if isempty(edgepath)
return @NamedTuple{path::Vector{Symbol}, dist::T}[]
end

if !(hasVariable(dfg_, from) || hasFactor(dfg_, from)) ||
!(hasVariable(dfg_, to) || hasFactor(dfg_, to))
# assume filters excluded either `to` or `from` and hence no shortest path
return Symbol[]
path = [dfg.g.labels[edgepath[1].src]]
dist = zero(T)
for (; dst, src) in edgepath
push!(path, dfg.g.labels[dst])
dist += distmx[src, dst]
end
# GraphsDFG internally uses Integers
frI = dfg_.g.labels[from]
toI = dfg_.g.labels[to]

# get shortest path from graph provider
path_state = Graphs.dijkstra_shortest_paths(dfg_.g.graph, [frI;])
path = Graphs.enumerate_paths(path_state, toI)
dijkpath = map(x -> dfg_.g.labels[x], path)
return [(path = path, dist = dist)]
end

#TODO Move getPaths and getPath to AbstractDFG services as default implementations.
function getPaths(
dfg::AbstractDFG,
from::Symbol,
to::Symbol,
k::Int;
algorithm = k == 1 ? a_star : yen_k_shortest_paths,
variableLabels::Union{Nothing, Vector{Symbol}} = nothing,
factorLabels::Union{Nothing, Vector{Symbol}} = nothing,
kwargs...,
)
# If the user provided restricted lists, build the subgraph automatically
active_dfg =
if isa(dfg, GraphsDFG) && isnothing(variableLabels) && isnothing(factorLabels)
dfg
else
vlabels = something(variableLabels, listVariables(dfg))
flabels = something(factorLabels, listFactors(dfg))
labels = vcat(vlabels, flabels)
DFG.getSubgraph(
GraphsDFG{NoSolverParams, VariableSkeleton, FactorSkeleton},
dfg,
labels,
)
end
!hasVariable(active_dfg, from) &&
!hasFactor(active_dfg, from) &&
throw(DFG.LabelNotFoundError(from))
!hasVariable(active_dfg, to) &&
!hasFactor(active_dfg, to) &&
throw(DFG.LabelNotFoundError(to))

return getPaths(algorithm, active_dfg, from, to, k; kwargs...)
end
Comment thread
Affie marked this conversation as resolved.

function getPath(
dfg::AbstractDFG,
from::Symbol,
to::Symbol;
variableLabels::Union{Nothing, Vector{Symbol}} = nothing,
factorLabels::Union{Nothing, Vector{Symbol}} = nothing,
kwargs...,
)
paths = getPaths(dfg, from, to, 1; variableLabels, factorLabels, kwargs...)

# Adhere strictly to the "getSingular = Error" rule
if isempty(paths)
error(
"No path found between :$(from) and :$(to). If a disconnected graph is expected, use `getPaths` instead.",
)
end

# return the list of symbols
return dijkpath
return first(paths)
end

export bfs_tree
Expand Down
Loading
Loading