Skip to content

Commit d708217

Browse files
authored
Merge pull request #373 from JuliaRobotics/maint/20Q1/refactCopy300
Refactor graph copy and subgraph functions
2 parents 86c7ae6 + c85774a commit d708217

File tree

14 files changed

+530
-409
lines changed

14 files changed

+530
-409
lines changed

attic/sandbox.jl

Lines changed: 0 additions & 68 deletions
This file was deleted.

src/DFGPlots/DFGPlots.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ More information at [GraphPlot.jl](https://github.com/JuliaGraphs/GraphPlot.jl)
8888
function dfgplot(dfg::AbstractDFG, p::DFGPlotProps = DFGPlotProps())
8989
# TODO implement convert functions
9090
ldfg = LightDFG{NoSolverParams}()
91-
DistributedFactorGraphs._copyIntoGraph!(dfg, ldfg, union(listVariables(dfg), listFactors(dfg)), true, copyGraphMetadata=false)
91+
copyGraph!(ldfg, dfg, union(listVariables(dfg), listFactors(dfg)), copyGraphMetadata=false)
9292
dfgplot(ldfg, p)
9393
end
9494

src/Deprecated.jl

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,199 @@ Base.setproperty!(x::DFGFactor,f::Symbol, val) = begin
9595
end
9696

9797

98-
#
98+
#NOTE deprecate in favor of constructors because its not lossless: https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Conversion-vs.-Construction-1
99+
function Base.convert(::Type{DFGVariableSummary}, v::DFGVariable)
100+
Base.depwarn("convert to type DFGVariableSummary is deprecated use the constructor", :convert)
101+
return DFGVariableSummary(v)
102+
end
103+
104+
function Base.convert(::Type{SkeletonDFGVariable}, v::VariableDataLevel1)
105+
Base.depwarn("convert to type SkeletonDFGVariable is deprecated use the constructor", :convert)
106+
return SkeletonDFGVariable(v)
107+
end
108+
109+
function Base.convert(::Type{DFGFactorSummary}, f::DFGFactor)
110+
Base.depwarn("convert to type DFGFactorSummary is deprecated use the constructor", :convert)
111+
return DFGFactorSummary(f)
112+
end
113+
114+
function Base.convert(::Type{SkeletonDFGFactor}, f::FactorDataLevel1)
115+
Base.depwarn("convert to type SkeletonDFGFactor is deprecated use the constructor", :convert)
116+
return SkeletonDFGFactor(f)
117+
end
118+
119+
##==============================================================================
120+
## WIP on consolidated subgraph functions, aim to remove in 0.8
121+
##==============================================================================
122+
# Deprecate in favor of buildSubgraph, mergeGraph
123+
export getSubgraph, getSubgraphAroundNode
124+
export buildSubgraphFromLabels!
125+
126+
"""
127+
$(SIGNATURES)
128+
Common function for copying nodes from one graph into another graph.
129+
This is overridden in specialized implementations for performance.
130+
NOTE: copyGraphMetadata not supported yet.
131+
"""
132+
function _copyIntoGraph!(sourceDFG::G, destDFG::H, variableFactorLabels::Vector{Symbol}, includeOrphanFactors::Bool=false; copyGraphMetadata::Bool=false)::Nothing where {G <: AbstractDFG, H <: AbstractDFG}
133+
# Split into variables and factors
134+
Base.depwarn("_copyIntoGraph! is deprecated use copyGraph/deepcopyGraph[!]", :_copyIntoGraph!)
135+
136+
includeOrphanFactors && (@error "Adding orphaned factors is not supported")
137+
138+
sourceVariables = map(vId->getVariable(sourceDFG, vId), intersect(listVariables(sourceDFG), variableFactorLabels))
139+
sourceFactors = map(fId->getFactor(sourceDFG, fId), intersect(listFactors(sourceDFG), variableFactorLabels))
140+
if length(sourceVariables) + length(sourceFactors) != length(variableFactorLabels)
141+
rem = symdiff(map(v->v.label, sourceVariables), variableFactorLabels)
142+
rem = symdiff(map(f->f.label, sourceFactors), variableFactorLabels)
143+
error("Cannot copy because cannot find the following nodes in the source graph: $rem")
144+
end
145+
146+
# Now we have to add all variables first,
147+
for variable in sourceVariables
148+
if !exists(destDFG, variable)
149+
addVariable!(destDFG, deepcopy(variable))
150+
end
151+
end
152+
# And then all factors to the destDFG.
153+
for factor in sourceFactors
154+
# Get the original factor variables (we need them to create it)
155+
sourceFactorVariableIds = getNeighbors(sourceDFG, factor)
156+
# Find the labels and associated variables in our new subgraph
157+
factVariableIds = Symbol[]
158+
for variable in sourceFactorVariableIds
159+
if exists(destDFG, variable)
160+
push!(factVariableIds, variable)
161+
end
162+
end
163+
# Only if we have all of them should we add it (otherwise strange things may happen on evaluation)
164+
if includeOrphanFactors || length(factVariableIds) == length(sourceFactorVariableIds)
165+
if !exists(destDFG, factor)
166+
addFactor!(destDFG, deepcopy(factor))
167+
end
168+
end
169+
end
170+
171+
if copyGraphMetadata
172+
setUserData(destDFG, getUserData(sourceDFG))
173+
setRobotData(destDFG, getRobotData(sourceDFG))
174+
setSessionData(destDFG, getSessionData(sourceDFG))
175+
end
176+
return nothing
177+
end
178+
179+
"""
180+
$(SIGNATURES)
181+
Retrieve a deep subgraph copy around a given variable or factor.
182+
Optionally provide a distance to specify the number of edges should be followed.
183+
Optionally provide an existing subgraph addToDFG, the extracted nodes will be copied into this graph. By default a new subgraph will be created.
184+
Note: By default orphaned factors (where the subgraph does not contain all the related variables) are not returned. Set includeOrphanFactors to return the orphans irrespective of whether the subgraph contains all the variables.
185+
Note: Always returns the node at the center, but filters around it if solvable is set.
186+
"""
187+
function getSubgraphAroundNode(dfg::AbstractDFG, node::DFGNode, distance::Int=1, includeOrphanFactors::Bool=false, addToDFG::AbstractDFG=_getDuplicatedEmptyDFG(dfg); solvable::Int=0)::AbstractDFG
188+
189+
Base.depwarn("getSubgraphAroundNode is deprecated use buildSubgraph", :getSubgraphAroundNode)
190+
191+
if !exists(dfg, node.label)
192+
error("Variable/factor with label '$(node.label)' does not exist in the factor graph")
193+
end
194+
195+
neighbors = getNeighborhood(dfg, node.label, distance)
196+
197+
# for some reason: always returns the node at the center with || (nlbl == node.label)
198+
solvable != 0 && filter!(nlbl -> (getSolvable(dfg, nlbl) >= solvable) || (nlbl == node.label), neighbors)
199+
200+
# Copy the section of graph we want
201+
_copyIntoGraph!(dfg, addToDFG, neighbors, includeOrphanFactors)
202+
return addToDFG
203+
end
204+
205+
"""
206+
$(SIGNATURES)
207+
Get a deep subgraph copy from the DFG given a list of variables and factors.
208+
Optionally provide an existing subgraph addToDFG, the extracted nodes will be copied into this graph. By default a new subgraph will be created.
209+
Note: By default orphaned factors (where the subgraph does not contain all the related variables) are not returned. Set includeOrphanFactors to return the orphans irrespective of whether the subgraph contains all the variables.
210+
"""
211+
function getSubgraph(dfg::G,
212+
variableFactorLabels::Vector{Symbol},
213+
includeOrphanFactors::Bool=false,
214+
addToDFG::H=_getDuplicatedEmptyDFG(dfg))::H where {G <: AbstractDFG, H <: AbstractDFG}
215+
216+
Base.depwarn("getSubgraph is deprecated use buildSubgraph", :getSubgraph)
217+
218+
for label in variableFactorLabels
219+
if !exists(dfg, label)
220+
error("Variable/factor with label '$(label)' does not exist in the factor graph")
221+
end
222+
end
223+
224+
_copyIntoGraph!(dfg, addToDFG, variableFactorLabels, includeOrphanFactors)
225+
return addToDFG
226+
end
227+
228+
229+
# TODO needsahome: home should be in IIF, calling just deepcopyGraph, or copyGraph
230+
# Into, Labels, Subgraph are all implied from the parameters.
231+
# can alies names but like Sam suggested only on copy is needed.
232+
233+
234+
"""
235+
$SIGNATURES
236+
Construct a new factor graph object as a subgraph of `dfg <: AbstractDFG` based on the
237+
variable labels `syms::Vector{Symbols}`.
238+
239+
SamC: Can we not just use _copyIntoGraph! for this? Looks like a small refactor to make it work.
240+
Will paste in as-is for now and we can figure it out as we go.
241+
DF: Absolutely agree that subgraph functions should use `DFG._copyIntoGraph!` as a single dependency in the code base. There have been a repeated new rewrites of IIF.buildSubGraphFromLabels (basic wrapper is fine) but nominal should be to NOT duplicate DFG functionality in IIF -- rather use/improve the existing features in DFG. FYI, I have repeatedly refactored this function over and over to use DFG properly but somehow this (add/delete/Variable/Factor) version keeps coming back without using `_copyIntoGraph`!!??? See latest effort commented out below `buildSubgraphFromLabels!_SPECIAL`...
242+
243+
Notes
244+
- Slighly messy internals, but gets the job done -- some room for performance improvement.
245+
- Defaults to GraphDFG, but likely to change to LightDFG in future.
246+
- since DFG v0.6 LightDFG is the default.
247+
248+
DevNotes
249+
- TODO: still needs to be consolidated with `DFG._copyIntoGraph`
250+
251+
Related
252+
253+
listVariables, _copyIntoGraph!
254+
"""
255+
function buildSubgraphFromLabels!(dfg::G,
256+
syms::Vector{Symbol};
257+
subfg::AbstractDFG=(G <: InMemoryDFGTypes ? G : GraphsDFG)(params=getSolverParams(dfg)),
258+
solvable::Int=0,
259+
allowedFactors::Union{Nothing, Vector{Symbol}}=nothing )::AbstractDFG where G <: AbstractDFG
260+
#
261+
Base.depwarn("buildSubgraphFromLabels! is deprecated use copyGraph, buildSubgraph or buildCliqueSubgraph!(IIF)", :buildSubgraphFromLabels!)
262+
# add a little too many variables (since we need the factors)
263+
for sym in syms
264+
if solvable <= getSolvable(dfg, sym)
265+
getSubgraphAroundNode(dfg, getVariable(dfg, sym), 2, false, subfg, solvable=solvable)
266+
end
267+
end
268+
269+
# remove excessive variables that were copied by neighbors distance 2
270+
currVars = listVariables(subfg)
271+
toDelVars = setdiff(currVars, syms)
272+
for dv in toDelVars
273+
# delete any neighboring factors first
274+
for fc in lsf(subfg, dv)
275+
deleteFactor!(subfg, fc)
276+
end
277+
278+
# and the variable itself
279+
deleteVariable!(subfg, dv)
280+
end
281+
282+
# delete any factors not in the allowed list
283+
if allowedFactors != nothing
284+
delFcts = setdiff(lsf(subfg), allowedFactors)
285+
for dfct in delFcts
286+
deleteFactor!(subfg, dfct)
287+
end
288+
end
289+
290+
# orphaned variables are allowed, but not orphaned factors
291+
292+
return subfg
293+
end

src/DistributedFactorGraphs.jl

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ export packVariableNodeData, unpackVariableNodeData
161161

162162
export getSolvedCount, isSolved, setSolvedCount!, isInitialized
163163

164-
export getNeighborhood, getSubgraph, getSubgraphAroundNode, getNeighbors, _getDuplicatedEmptyDFG
165-
164+
export getNeighborhood, getNeighbors, _getDuplicatedEmptyDFG
165+
export copyGraph!, deepcopyGraph, deepcopyGraph!, buildSubgraph, mergeGraph!
166166
# Big Data
167167
##------------------------------------------------------------------------------
168168
export addBigDataEntry!, getBigDataEntry, updateBigDataEntry!, deleteBigDataEntry!, getBigDataEntries, getBigDataKeys
@@ -224,13 +224,10 @@ export
224224
compareFactorGraphs
225225

226226

227-
## Deprecated.jl should be listed there
227+
## Deprecated exports should be listed in Deprecated.jl if possible, otherwise here
228228

229229

230230
## needsahome.jl
231-
232-
export buildSubgraphFromLabels!
233-
234231
import Base: print
235232
export printFactor, printVariable, print
236233

src/GraphsDFG/services/GraphsDFG.jl

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function getFactors(dfg::GraphsDFG, regexFilter::Union{Nothing, Regex}=nothing;
208208
if regexFilter != nothing
209209
factors = filter(f -> occursin(regexFilter, String(f.label)), factors)
210210
end
211-
211+
212212
if length(tags) > 0
213213
mask = map(v -> length(intersect(v.tags, tags)) > 0, factors )
214214
return factors[mask]
@@ -255,42 +255,6 @@ function getNeighbors(dfg::GraphsDFG, label::Symbol; solvable::Int=0)::Vector{Sy
255255
return map(n -> n.dfgNode.label, neighbors)
256256
end
257257

258-
#NOTE Replaced by abstract function in services/AbstractDFG.jl
259-
# """
260-
# $(SIGNATURES)
261-
# Retrieve a deep subgraph copy around a given variable or factor.
262-
# Optionally provide a distance to specify the number of edges should be followed.
263-
# Optionally provide an existing subgraph addToDFG, the extracted nodes will be copied into this graph. By default a new subgraph will be created.
264-
# Note: By default orphaned factors (where the subgraph does not contain all the related variables) are not returned. Set includeOrphanFactors to return the orphans irrespective of whether the subgraph contains all the variables.
265-
# """
266-
# function getSubgraphAroundNode(dfg::GraphsDFG{P}, node::T, distance::Int64=1, includeOrphanFactors::Bool=false, addToDFG::GraphsDFG=GraphsDFG{P}(); solvable::Int=0)::GraphsDFG where {P <: AbstractParams, T <: DFGNode}
267-
# if !haskey(dfg.labelDict, node.label)
268-
# error("Variable/factor with label '$(node.label)' does not exist in the factor graph")
269-
# end
270-
#
271-
# # Build a list of all unique neighbors inside 'distance'
272-
# neighborList = Dict{Symbol, Any}()
273-
# push!(neighborList, node.label => dfg.g.vertices[dfg.labelDict[node.label]])
274-
# curList = Dict{Symbol, Any}(node.label => dfg.g.vertices[dfg.labelDict[node.label]])
275-
# for dist in 1:distance
276-
# newNeighbors = Dict{Symbol, Any}()
277-
# for (key, node) in curList
278-
# neighbors = in_neighbors(node, dfg.g) #Don't use out_neighbors! It enforces directiveness even if we don't want it
279-
# for neighbor in neighbors
280-
# if !haskey(neighborList, neighbor.dfgNode.label) && (isSolvable(neighbor.dfgNode) >= solvable)
281-
# push!(neighborList, neighbor.dfgNode.label => neighbor)
282-
# push!(newNeighbors, neighbor.dfgNode.label => neighbor)
283-
# end
284-
# end
285-
# end
286-
# curList = newNeighbors
287-
# end
288-
#
289-
# # Copy the section of graph we want
290-
# _copyIntoGraph!(dfg, addToDFG, collect(keys(neighborList)), includeOrphanFactors)
291-
# return addToDFG
292-
# end
293-
294258
"""
295259
$(SIGNATURES)
296260
Produces a dot-format of the graph for visualization.

src/LightDFG/LightDFG.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ module LightDFGs
33
using LightGraphs
44
using DocStringExtensions
55

6-
import ...DistributedFactorGraphs: AbstractDFG, DFGNode, AbstractDFGVariable, AbstractDFGFactor, DFGSummary, AbstractParams, NoSolverParams, DFGVariable, DFGFactor
6+
using ...DistributedFactorGraphs
7+
8+
# import ...DistributedFactorGraphs: AbstractDFG, DFGNode, AbstractDFGVariable, AbstractDFGFactor, DFGSummary, AbstractParams, NoSolverParams, DFGVariable, DFGFactor
79

810
# import DFG functions to extend
911
import ...DistributedFactorGraphs: setSolverParams!,
@@ -38,8 +40,8 @@ import ...DistributedFactorGraphs: setSolverParams!,
3840
isFullyConnected,
3941
hasOrphans,
4042
getNeighbors,
41-
getSubgraphAroundNode,
42-
getSubgraph,
43+
buildSubgraph,
44+
copyGraph!,
4345
getBiadjacencyMatrix,
4446
_getDuplicatedEmptyDFG,
4547
toDot,

src/LightDFG/entities/LightDFG.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,14 @@ LightDFG(description::String,
6666
sessionData::Dict{Symbol, String},
6767
solverParams::AbstractParams) =
6868
LightDFG(FactorGraph{Int,DFGVariable,DFGFactor}(), description, userId, robotId, sessionId, userData, robotData, sessionData, Symbol[], solverParams)
69+
70+
71+
LightDFG{T,V,F}(description::String,
72+
userId::String,
73+
robotId::String,
74+
sessionId::String,
75+
userData::Dict{Symbol, String},
76+
robotData::Dict{Symbol, String},
77+
sessionData::Dict{Symbol, String},
78+
solverParams::T) where {T <: AbstractParams, V <:AbstractDFGVariable, F<:AbstractDFGFactor} =
79+
LightDFG(FactorGraph{Int,V,F}(), description, userId, robotId, sessionId, userData, robotData, sessionData, Symbol[], solverParams)

0 commit comments

Comments
 (0)