Skip to content

Commit fa8e755

Browse files
committed
Merge branch 'feature/summaryfunction' of https://github.com/JuliaRobotics/DistributedFactorGraphs.jl.git into jt/develop
2 parents 256ec48 + 078f57c commit fa8e755

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1598
-885
lines changed

Project.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ version = "0.4.0"
44

55
[deps]
66
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
7-
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
87
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
98
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
109
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
10+
GraphPlot = "a2cc645c-3eea-5389-862e-a155d0052231"
1111
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
1212
JSON2 = "2535ab7d-5cd8-5a07-80ac-9b1792aadce3"
1313
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
@@ -29,6 +29,7 @@ julia = "0.7, 1"
2929

3030
[extras]
3131
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
32+
GraphPlot = "a2cc645c-3eea-5389-862e-a155d0052231"
3233

3334
[targets]
34-
test = ["Test"]
35+
test = ["Test", "GraphPlot"]

docs/src/func_ref.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ getSubgraphAroundNode
5858
getSubgraph
5959
```
6060

61-
### Visualization
61+
### Summaries
6262
```@docs
63-
toDot
64-
toDotFile
63+
getSummary
64+
getSummaryGraph
6565
```
6666

67-
### DataFrame Extension Functions
67+
### Visualization and Plotting
6868
```@docs
69-
getAdjacencyMatrixDataFrame
69+
toDot
70+
toDotFile
71+
dfgplot
7072
```

src/CloudGraphsDFG/CloudGraphsDFG.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ export getAddHistory, getDescription, getLabelDict
1717
export addVariable!, addFactor!
1818
export ls, lsf, getVariables, getFactors, getVariableIds, getFactorIds
1919
export getVariable, getFactor
20-
export updateVariable!, updateFactor!
20+
export updateVariable!, updateFactor!, updateVariableSolverData!
2121
export deleteVariable!, deleteFactor!
2222
export getAdjacencyMatrix
23-
export getAdjacencyMatrixDataFrame
2423
export getNeighbors
2524
export getSubgraphAroundNode
2625
export getSubgraph

src/CloudGraphsDFG/services/CloudGraphsDFG.jl

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ getDescription(dfg::CloudGraphsDFG) = dfg.description
6363
setDescription(dfg::CloudGraphsDFG, description::String) = dfg.description = description
6464
getAddHistory(dfg::CloudGraphsDFG) = dfg.addHistory
6565
getSolverParams(dfg::CloudGraphsDFG) = dfg.solverParams
66-
function setSolverParams(dfg::CloudGraphsDFG, solverParams::T) where T <: AbstractParams
67-
dfg.solverParams = solverParams
66+
function setSolverParams(dfg::CloudGraphsDFG, solverParams::T)::T where T <: AbstractParams
67+
return dfg.solverParams = solverParams
6868
end
6969

7070
"""
@@ -255,7 +255,7 @@ function getVariable(dfg::CloudGraphsDFG, variableId::Int64)::DFGVariable
255255
# props["label"] = Symbol(variable.label)
256256
timestamp = DateTime(props["timestamp"])
257257
tags = JSON2.read(props["tags"], Vector{Symbol})
258-
estimateDict = JSON2.read(props["estimateDict"], Dict{Symbol, VariableEstimate})
258+
estimateDict = JSON2.read(props["estimateDict"], Dict{Symbol, Dict{Symbol, VariableEstimate}})
259259
smallData = nothing
260260
smallData = JSON2.read(props["smallData"], Dict{String, String})
261261

@@ -389,6 +389,20 @@ function updateVariable!(dfg::CloudGraphsDFG, variable::DFGVariable)::DFGVariabl
389389
return variable
390390
end
391391

392+
"""
393+
$(SIGNATURES)
394+
Update solver and estimate data for a variable (variable can be from another graph).
395+
"""
396+
function updateVariableSolverData!(dfg::CloudGraphsDFG, sourceVariable::DFGVariable)::DFGVariable
397+
if !exists(dfg, sourceVariable)
398+
error("Source variable '$(sourceVariable.label)' doesn't exist in the graph.")
399+
end
400+
nodeId = _tryGetNeoNodeIdFromNodeLabel(dfg.neo4jInstance, dfg.userId, dfg.robotId, dfg.sessionId, sourceVariable.label)
401+
Neo4j.setnodeproperty(dfg.neo4jInstance.graph, nodeId, "estimateDict", JSON2.write(sourceVariable.estimateDict))
402+
Neo4j.setnodeproperty(dfg.neo4jInstance.graph, nodeId, "solverDataDict", JSON2.write(Dict(keys(sourceVariable.solverDataDict) .=> map(vnd -> pack(dfg, vnd), values(sourceVariable.solverDataDict)))))
403+
return sourceVariable
404+
end
405+
392406
"""
393407
$(SIGNATURES)
394408
Update a complete DFGFactor in the DFG.
@@ -704,25 +718,11 @@ Optionally provide a distance to specify the number of edges should be followed.
704718
Optionally provide an existing subgraph addToDFG, the extracted nodes will be copied into this graph. By default a new subgraph will be created.
705719
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.
706720
"""
707-
function getSubgraphAroundNode(dfg::CloudGraphsDFG, node::DFGNode, distance::Int64=1, includeOrphanFactors::Bool=false, addToDFG::Union{Nothing, CloudGraphsDFG}=nothing)::CloudGraphsDFG
721+
function getSubgraphAroundNode(dfg::CloudGraphsDFG, node::DFGNode, distance::Int64=1, includeOrphanFactors::Bool=false, addToDFG::AbstractDFG=_getDuplicatedEmptyDFG(dfg))::AbstractDFG
708722
distance < 1 && error("getSubgraphAroundNode() only works for distance > 0")
709723

710724
# Making a copy session if not specified
711-
if addToDFG == nothing
712-
addToDFG = _getDuplicatedEmptyDFG(dfg)
713-
end
714-
715-
# Thank you Neo4j for 0..* awesomeness!!
716-
neighborList = _getLabelsFromCyphonQuery(dfg.neo4jInstance, "(n:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId):$(node.label))-[FACTORGRAPH*0..$distance]-(node:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId))")
717-
718-
# Copy the section of graph we want
719-
_copyIntoGraph!(dfg, addToDFG, neighborList, includeOrphanFactors)
720-
return addToDFG
721-
end
722-
723-
function getSubgraphAroundNode(dfg::CloudGraphsDFG{<:AbstractParams}, node::DFGNode, distance::Int64, includeOrphanFactors::Bool, addToDFG::MetaGraphsDFG{AbstractParams})::AbstractDFG
724-
distance < 1 && error("getSubgraphAroundNode() only works for distance > 0")
725-
725+
#moved to parameter addToDFG::AbstractDFG=_getDuplicatedEmptyDFG(dfg)
726726

727727
# Thank you Neo4j for 0..* awesomeness!!
728728
neighborList = _getLabelsFromCyphonQuery(dfg.neo4jInstance, "(n:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId):$(node.label))-[FACTORGRAPH*0..$distance]-(node:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId))")
@@ -739,11 +739,11 @@ Get a deep subgraph copy from the DFG given a list of variables and factors.
739739
Optionally provide an existing subgraph addToDFG, the extracted nodes will be copied into this graph. By default a new subgraph will be created.
740740
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.
741741
"""
742-
function getSubgraph(dfg::CloudGraphsDFG, variableFactorLabels::Vector{Symbol}, includeOrphanFactors::Bool=false, addToDFG::Union{Nothing, CloudGraphsDFG}=nothing)::CloudGraphsDFG
742+
function getSubgraph(dfg::CloudGraphsDFG,
743+
variableFactorLabels::Vector{Symbol},
744+
includeOrphanFactors::Bool=false,
745+
addToDFG::G=_getDuplicatedEmptyDFG(dfg) )::G where {G <: AbstractDFG}
743746
# Making a copy session if not specified
744-
if addToDFG == nothing
745-
addToDFG = _getDuplicatedEmptyDFG(dfg)
746-
end
747747

748748
_copyIntoGraph!(dfg, addToDFG, variableFactorLabels, includeOrphanFactors)
749749

@@ -787,6 +787,33 @@ function getAdjacencyMatrix(dfg::CloudGraphsDFG)::Matrix{Union{Nothing, Symbol}}
787787
return adjMat
788788
end
789789

790+
function getAdjacencyMatrixSparse(dfg::CloudGraphsDFG)::Tuple{SparseMatrixCSC, Vector{Symbol}, Vector{Symbol}}
791+
varLabels = getVariableIds(dfg)
792+
factLabels = sort(getFactorIds(dfg))
793+
vDict = Dict(varLabels .=> [1:length(varLabels)...].+1)
794+
fDict = Dict(factLabels .=> [1:length(factLabels)...].+1)
795+
796+
adjMat = spzeros(Int, length(factLabels)+1, length(varLabels)+1)
797+
798+
# Now ask for all relationships for this session graph
799+
loadtx = transaction(dfg.neo4jInstance.connection)
800+
query = "START n=node(*) MATCH (n:VARIABLE:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId))-[r:FACTORGRAPH]-(m:FACTOR:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId)) RETURN n.label as variable, m.label as factor;"
801+
nodes = loadtx(query; submit=true)
802+
# Have to finish the transaction
803+
commit(loadtx)
804+
if length(nodes.errors) > 0
805+
error(string(nodes.errors))
806+
end
807+
# Add in the relationships
808+
varRel = Symbol.(map(node -> node["row"][1], nodes.results[1]["data"]))
809+
factRel = Symbol.(map(node -> node["row"][2], nodes.results[1]["data"]))
810+
for i = 1:length(varRel)
811+
adjMat[fDict[factRel[i]], vDict[varRel[i]]] = 1
812+
end
813+
814+
return adjMat, varLabels, factLabels
815+
end
816+
790817
# """
791818
# $(SIGNATURES)
792819
# Produces a dot-format of the graph for visualization.
@@ -815,30 +842,3 @@ end
815842
# end
816843
# return nothing
817844
# end
818-
#
819-
# function __init__()
820-
# @require DataFrames="a93c6f00-e57d-5684-b7b6-d8193f3e46c0" begin
821-
# if isdefined(Main, :DataFrames)
822-
# """
823-
# $(SIGNATURES)
824-
# Get an adjacency matrix for the DFG as a DataFrame.
825-
# Rows are all factors, columns are all variables, and each cell contains either nothing or the symbol of the relating factor.
826-
# The first column is the factor headings.
827-
# """
828-
# function getAdjacencyMatrixDataFrame(dfg::CloudGraphsDFG)::Main.DataFrames.DataFrame
829-
# varLabels = sort(map(v->v.label, getVariables(dfg)))
830-
# factLabels = sort(map(f->f.label, getFactors(dfg)))
831-
# adjDf = DataFrames.DataFrame(:Factor => Union{Missing, Symbol}[])
832-
# for varLabel in varLabels
833-
# adjDf[varLabel] = Union{Missing, Symbol}[]
834-
# end
835-
# for (i, factLabel) in enumerate(factLabels)
836-
# push!(adjDf, [factLabel, DataFrames.missings(length(varLabels))...])
837-
# factVars = getNeighbors(dfg, getFactor(dfg, factLabel))
838-
# map(vLabel -> adjDf[vLabel][i] = factLabel, factVars)
839-
# end
840-
# return adjDf
841-
# end
842-
# end
843-
# end
844-
# end

src/Common.jl

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11

2-
export sortVarNested
2+
export sortVarNested, sortDFG
33
export isPrior, lsfPriors
4-
export getData
54
export getVariableType, getSofttype
65
export getFactorType, getfnctype
76
export lsTypes, lsfTypes
87
export lsWho, lsfWho
98

9+
## Utility functions for getting type names and modules (from IncrementalInference)
10+
function _getmodule(t::T) where T
11+
T.name.module
12+
end
13+
function _getname(t::T) where T
14+
T.name.name
15+
end
16+
1017
"""
1118
$(SIGNATURES)
1219
Test if all elements of the string is a number: Ex, "123" is true, "1_2" is false.
@@ -85,10 +92,20 @@ end
8592
"""
8693
$SIGNATURES
8794
88-
Retrieve data structure stored in a node.
95+
Sort variable (factor) lists in a meaningful way, for example `[:april;:x1_3;:x1_6;]`
96+
97+
Notes
98+
- Not fool proof, but does better than native sort.
99+
100+
Example
101+
102+
`sortDFG(ls(dfg))`
103+
104+
Related
105+
106+
ls, lsf
89107
"""
90-
getData(v::DFGFactor)::GenericFunctionNodeData = v.data
91-
getData(v::DFGVariable; solveKey::Symbol=:default)::VariableNodeData = v.solverDataDict[solveKey]
108+
sortDFG(vars::Vector{Symbol})::Vector{Symbol} = sortVarNested(vars)
92109

93110
"""
94111
$SIGNATURES
@@ -232,10 +249,38 @@ function lsTypes(dfg::G)::Dict{Symbol, Vector{String}} where G <: AbstractDFG
232249
end
233250

234251

252+
function ls(dfg::G, ::Type{T}; solveKey::Symbol=:default) where {G <: AbstractDFG, T <: InferenceVariable}
253+
xx = getVariables(dfg)
254+
mask = getVariableType.(xx, solveKey=solveKey) .|> typeof .== T
255+
vxx = view(xx, mask)
256+
map(x->x.label, vxx)
257+
end
258+
259+
260+
function ls(dfg::G, ::Type{T}) where {G <: AbstractDFG, T <: FunctorInferenceType}
261+
xx = getFactors(dfg)
262+
names = getfield.(typeof.(getFactorType.(xx)), :name) .|> Symbol
263+
vxx = view(xx, names .== Symbol(T))
264+
map(x->x.label, vxx)
265+
end
266+
267+
function lsf(dfg::G, ::Type{T}) where {G <: AbstractDFG, T <: FunctorInferenceType}
268+
ls(dfg, T)
269+
end
270+
271+
235272
"""
236273
$(SIGNATURES)
237274
Gives back all factor labels that fit the bill:
238275
lsWho(dfg, :Pose3)
276+
277+
Dev Notes
278+
- Cloud versions will benefit from less data transfer
279+
- `ls(dfg::C, ::T) where {C <: CloudDFG, T <: ..}`
280+
281+
Related
282+
283+
ls, lsf, lsfPriors
239284
"""
240285
function lsWho(dfg::AbstractDFG, type::Symbol; solveKey::Symbol=:default)::Vector{Symbol}
241286
vars = getVariables(dfg)
@@ -252,6 +297,14 @@ end
252297
$(SIGNATURES)
253298
Gives back all factor labels that fit the bill:
254299
lsfWho(dfg, :Point2Point2)
300+
301+
Dev Notes
302+
- Cloud versions will benefit from less data transfer
303+
- `ls(dfg::C, ::T) where {C <: CloudDFG, T <: ..}`
304+
305+
Related
306+
307+
ls, lsf, lsfPriors
255308
"""
256309
function lsfWho(dfg::AbstractDFG, type::Symbol)::Vector{Symbol}
257310
facs = getFactors(dfg)

src/DFGPlots/DFGPlots.jl

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ using Colors
44
using LightGraphs
55
using MetaGraphs
66
using GraphPlot
7+
using DocStringExtensions
78
import GraphPlot: gplot
89

910
using ...DistributedFactorGraphs
@@ -28,6 +29,26 @@ DFGPlotProps() = DFGPlotProps( (var=colorant"seagreen", fac=colorant"cyan3"),
2829
true)
2930

3031

32+
33+
"""
34+
$(SIGNATURES)
35+
Plots the structure of the factor graph. GraphPlot must be imported before DistributedFactoGraphs for these functions to be available.
36+
Returns the plot context.
37+
38+
E.g.
39+
```
40+
using GraphPlot
41+
using DistributedFactorGraphs, DistributedFactorGraphs.DFGPlots
42+
# ... Make graph...
43+
# Using GraphViz plotting
44+
dfgplot(fg)
45+
# Save to PDFG
46+
using Compose
47+
draw(PDF("/tmp/graph.pdf", 16cm, 16cm), dfgplot(fg))
48+
```
49+
50+
More information at [GraphPlot.jl](https://github.com/JuliaGraphs/GraphPlot.jl)
51+
"""
3152
function dfgplot(dfg::LightDFG, p::DFGPlotProps = DFGPlotProps())
3253

3354
nodetypes = [haskey(dfg.g.variables, s) for s in dfg.g.labels]
@@ -46,6 +67,25 @@ function dfgplot(dfg::LightDFG, p::DFGPlotProps = DFGPlotProps())
4667

4768
end
4869

70+
"""
71+
$(SIGNATURES)
72+
Plots the structure of the factor graph. GraphPlot must be imported before DistributedFactoGraphs for these functions to be available.
73+
Returns the plot context.
74+
75+
E.g.
76+
```
77+
using GraphPlot
78+
using DistributedFactorGraphs, DistributedFactorGraphs.DFGPlots
79+
# ... Make graph...
80+
# Using GraphViz plotting
81+
dfgplot(fg)
82+
# Save to PDFG
83+
using Compose
84+
draw(PDF("/tmp/graph.pdf", 16cm, 16cm), dfgplot(fg))
85+
```
86+
87+
More information at [GraphPlot.jl](https://github.com/JuliaGraphs/GraphPlot.jl)
88+
"""
4989
function dfgplot(dfg::MetaGraphsDFG, p::DFGPlotProps = DFGPlotProps())
5090

5191
nodesize = [has_prop(dfg.g,i,:factor) ? p.nodesize.fac : p.nodesize.var for i=vertices(dfg.g)]
@@ -60,14 +100,32 @@ function dfgplot(dfg::MetaGraphsDFG, p::DFGPlotProps = DFGPlotProps())
60100

61101
end
62102

103+
"""
104+
$(SIGNATURES)
105+
Plots the structure of the factor graph. GraphPlot must be imported before DistributedFactoGraphs for these functions to be available.
106+
Returns the plot context.
107+
108+
E.g.
109+
```
110+
using GraphPlot
111+
using DistributedFactorGraphs, DistributedFactorGraphs.DFGPlots
112+
# ... Make graph...
113+
# Using GraphViz plotting
114+
dfgplot(fg)
115+
# Save to PDFG
116+
using Compose
117+
draw(PDF("/tmp/graph.pdf", 16cm, 16cm), dfgplot(fg))
118+
```
119+
120+
More information at [GraphPlot.jl](https://github.com/JuliaGraphs/GraphPlot.jl)
121+
"""
63122
function dfgplot(dfg::AbstractDFG, p::DFGPlotProps = DFGPlotProps())
64123
# TODO implement convert functions
65124
@warn "TODO Implement convert"
66125
ldfg = MetaGraphsDFG{AbstractParams}()
67126
DistributedFactorGraphs._copyIntoGraph!(dfg, ldfg, union(getVariableIds(dfg), getFactorIds(dfg)), true)
68127

69128
dfgplot(ldfg, p)
70-
71129
end
72130

73131
function gplot(dfg::MetaGraphsDFG; keyargs...)

0 commit comments

Comments
 (0)