Skip to content

Commit 6d49afc

Browse files
committed
WIP getVariableSolverData, Cleanup, API updates on user/robot/session data, toDot for plotting,
1 parent f1679c8 commit 6d49afc

File tree

5 files changed

+196
-36
lines changed

5 files changed

+196
-36
lines changed

src/DistributedFactorGraphs.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ export MeanMaxPPE
8787
export setSerializationModule!, getSerializationModule
8888
export getLabelDict, getDescription, setDescription, getAddHistory, getSolverParams, setSolverParams
8989
export getUserData, setUserData, getRobotData, setRobotData, getSessionData, setSessionData
90-
# Not sure these are going to work everywhere
91-
export pushUserData!, pushRobotData!, pushSessionData!, popUserData!, popRobotData!, popSessionData!
90+
91+
# Not sure these are going to work everywhere, TODO implement in cloud?
92+
export updateUserData!, updateRobotData!, updateSessionData!, deleteUserData!, deleteRobotData!, deleteSessionData!
93+
export emptyUserData!, emptyRobotData!, emptySessionData!
94+
9295

9396
export exists, addVariable!, addFactor!, getVariable, getFactor, updateVariable!, updateFactor!, deleteVariable!, deleteFactor!
9497
export getVariables, getVariableIds, getFactors, getFactorIds, ls, lsf

src/LightDFG/LightDFG.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ import ...DistributedFactorGraphs: setSolverParams,
4242
getSubgraph,
4343
getIncidenceMatrix,
4444
getIncidenceMatrixSparse,
45-
_getDuplicatedEmptyDFG
45+
_getDuplicatedEmptyDFG,
46+
toDot,
47+
toDotFile
4648

4749
include("FactorGraphs/FactorGraphs.jl")
4850
using .FactorGraphs

src/LightDFG/services/LightDFG.jl

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ exists(dfg::LightDFG, nId::Symbol) = haskey(dfg.g.labels, nId)
4242
exists(dfg::LightDFG, node::DFGNode) = exists(dfg, node.label)
4343

4444
function isVariable(dfg::LightDFG{P,V,F}, sym::Symbol) where {P <: AbstractParams, V <: AbstractDFGVariable, F <: AbstractDFGFactor}
45-
return haskey(dfg.g.variables, sym)
45+
return haskey(dfg.g.variables, sym)
4646
end
4747

4848
function isFactor(dfg::LightDFG{P,V,F}, sym::Symbol) where {P <: AbstractParams, V <: AbstractDFGVariable, F <: AbstractDFGFactor}
49-
return haskey(dfg.g.factors, sym)
49+
return haskey(dfg.g.factors, sym)
5050
end
5151

5252

@@ -113,17 +113,34 @@ function addFactor!(dfg::LightDFG{<:AbstractParams, <:AbstractDFGVariable, F}, f
113113
return FactorGraphs.addFactor!(dfg.g, variableLabels, factor)
114114
end
115115

116+
#TODO Sam, I would say if we handle the update vsd corectly this function is only needed in the cloud.
117+
# In memory types are just passing pointers along so no perfomrance issue.
116118
function getVariable(dfg::LightDFG, label::Symbol; solveKey::Union{Nothing, Symbol})::Union{Nothing, AbstractDFGVariable}
117-
!(label in dfg.g.variables) && return nothing
118-
solveKey == nothing && return dfg.g.variables[label]
119-
# Requesting a single solvekey
120-
if solveKey in dfg.g.variables[label]
121-
v = copy(dfg.g.variables[label]) # Shallow copy
122-
v.solverDataDict = [solveKey=>v.solverDataDict[solveKey]]
123-
return v
124-
else
125-
return nothing
126-
end
119+
120+
if !haskey(dfg.g.variables, label)
121+
@warn "Variable '$label' does not exists in the factor graph"
122+
return nothing
123+
end
124+
125+
var = dfg.g.variables[label]
126+
127+
if solveKey != nothing && !haskey(var.solverDataDict, solveKey)
128+
@warn "Solvekey '$solveKey' does not exists in the variable"
129+
return nothing
130+
end
131+
132+
return var
133+
134+
# !(label in dfg.g.variables) && return nothing
135+
# solveKey == nothing && return dfg.g.variables[label]
136+
# # Requesting a single solvekey
137+
# if solveKey in dfg.g.variables[label]
138+
# v = copy(dfg.g.variables[label]) # Shallow copy
139+
# v.solverDataDict = [solveKey=>v.solverDataDict[solveKey]]
140+
# return v
141+
# else
142+
# return nothing
143+
# end
127144
end
128145

129146
function getFactor(dfg::LightDFG, label::Symbol)::AbstractDFGFactor
@@ -360,7 +377,9 @@ function getIncidenceMatrix(dfg::LightDFG; solvable::Int=0)::Matrix{Union{Nothin
360377
return adjMat
361378
end
362379

363-
function getIncidenceMatrixSparse(dfg::LightDFG; solvable::Int=0)::Tuple{LightGraphs.SparseMatrixCSC, Vector{Symbol}, Vector{Symbol}}
380+
#TODO This is just way too strange to call a function getIncidenceMatrix that calls adjacency_matrix internally,
381+
# So I'm going with Biadjacency Matrix https://en.wikipedia.org/wiki/Adjacency_matrix#Of_a_bipartite_graph
382+
function getBiadjacencyMatrixSparse(dfg::LightDFG; solvable::Int=0)::Tuple{LightGraphs.SparseMatrixCSC, Vector{Symbol}, Vector{Symbol}}
364383
varLabels = getVariableIds(dfg, solvable=solvable)
365384
factLabels = getFactorIds(dfg, solvable=solvable)
366385
varIndex = [dfg.g.labels[s] for s in varLabels]
@@ -372,6 +391,16 @@ function getIncidenceMatrixSparse(dfg::LightDFG; solvable::Int=0)::Tuple{LightGr
372391
return adjvf, varLabels, factLabels
373392
end
374393

394+
function getAdjacencyMatrixSparse(dfg::LightDFG; solvable::Int=0)
395+
@warn "Deprecated function, please use getBiadjacencyMatrixSparse as this will be removed in v0.6.1"
396+
return getBiadjacencyMatrixSparse(dfg, solvable)
397+
end
398+
399+
# this would be an incidence matrix
400+
function getIncidenceMatrixSparse(dfg::LightDFG)
401+
return incidence_matrix(dfg.g)
402+
end
403+
375404
"""
376405
$(SIGNATURES)
377406
Gets an empty and unique LightDFG derived from an existing DFG.
@@ -383,3 +412,40 @@ function _getDuplicatedEmptyDFG(dfg::LightDFG{P,V,F})::LightDFG where {P <: Abst
383412
newDfg.description ="(Copy of) $(dfg.description)"
384413
return newDfg
385414
end
415+
416+
417+
#TODO JT test.
418+
"""
419+
$(SIGNATURES)
420+
A replacement for to_dot that saves only hardcoded factor graph plotting attributes.
421+
"""
422+
function savedot_attributes(io::IO, dfg::LightDFG)
423+
write(io, "graph G {\n")
424+
425+
for vl in getVariableIds(dfg)
426+
write(io, "$vl [color=red, shape=ellipse];\n")
427+
end
428+
for fl in getFactorIds(dfg)
429+
write(io, "$fl [color=blue, shape=box];\n")
430+
end
431+
432+
for e in edges(dfg.g)
433+
write(io, "$(dfg.g.labels[src(e)]) -- $(dfg.g.labels[dst(e)])\n")
434+
end
435+
write(io, "}\n")
436+
end
437+
438+
function toDotFile(dfg::LightDFG, fileName::String="/tmp/dfg.dot")::Nothing
439+
open(fileName, "w") do fid
440+
savedot_attributes(fid, dfg)
441+
end
442+
return nothing
443+
end
444+
445+
function toDot(dfg::LightDFG)::String
446+
m = PipeBuffer()
447+
savedot_attributes(m, dfg)
448+
data = take!(m)
449+
close(m)
450+
return String(data)
451+
end

src/entities/DFGFactor.jl

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,37 +54,34 @@ mutable struct DFGFactor{T, S} <: AbstractDFGFactor
5454
"""Solvable flag for the factor.
5555
Accessors: `getSolvable`, `setSolvable!`
5656
TODO: Switch to `DFGNodeParams`"""
57-
solvable::Int
57+
solvable::Int #TODO we can go with DFGNodeParams or Reference the solvable field with getproperty overload
5858
"""Mutable parameters for the variable. We suggest using accessors to get to this data.
5959
Accessors: `getSolvable`, `setSolvable!`"""
6060
_dfgNodeParams::DFGNodeParams
6161
"""Internal cache of the ordering of the neighbor variables. Rather use getNeighbors to get the list as this is an internal value.
6262
Accessors: `getVariableOrder`"""
6363
_variableOrderSymbols::Vector{Symbol}
64-
DFGFactor{T, S}(label::Symbol, _internalId::Int64=0, timestamp::DateTime=now()) where {T, S} = new{T, S}(label, timestamp, Symbol[], GenericFunctionNodeData{T, S}(), 0, 0, Symbol[])
65-
DFGFactor{T, S}(label::Symbol,
66-
timestamp::DateTime,
67-
tags::Vector{Symbol},
68-
solverData::GenericFunctionNodeData{T, S},
69-
solvable::Int,
70-
_dfgNodeParams::DFGNodeParams,
71-
_variableOrderSymbols::Vector{Symbol}) where {T, S} = new(label, timestamp, tags, solverData, solvable, _dfgNodeParams, _variableOrderSymbols)
7264
end
7365

7466
"""
7567
$(SIGNATURES)
7668
7769
Construct a DFG factor given a label.
7870
"""
71+
#TODO _internalId?
72+
DFGFactor{T, S}(label::Symbol, _internalId::Int64=0, timestamp::DateTime=now()) where {T, S}
73+
= DFGFactor(label, timestamp, Symbol[], GenericFunctionNodeData{T, S}(), 0, DFGNodeParams(0, _internalId), Symbol[])
74+
7975
DFGFactor(label::Symbol;
76+
timestamp::DateTime=now(),
8077
tags::Vector{Symbol}=Symbol[],
8178
data::GenericFunctionNodeData{T, S}=GenericFunctionNodeData{T, S}(),
8279
solvable::Int=0,
83-
timestamp::DateTime=now(),
8480
_internalId::Int64=0,
8581
_variableOrderSymbols::Vector{Symbol}=Symbol[]) where {T, S} =
8682
DFGFactor{T,S}(label,timestamp,tags,data,solvable,DFGNodeParams(solvable, _internalId),_variableOrderSymbols)
8783

84+
8885
# Simply for convenience - don't export
8986
const PackedFunctionNodeData{T} = GenericFunctionNodeData{T, <: AbstractString}
9087
PackedFunctionNodeData(x1, x2, x3, x4, x5::S, x6::T, x7::String="", x8::Vector{Int}=Int[], x9::Int=0) where {T <: PackedInferenceType, S <: AbstractString} = GenericFunctionNodeData(x1, x2, x3, x4, x5, x6, x7, x8, x9)

src/services/AbstractDFG.jl

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,23 @@ function setSessionData(dfg::AbstractDFG, data::Dict{Symbol, String})::Bool
9494
return true
9595
end
9696

97-
pushUserData!(dfg::AbstractDFG, pair::Pair{Symbol,String}) = push!(dfg.userData, pair)
98-
pushRobotData!(dfg::AbstractDFG, pair::Pair{Symbol,String}) = push!(dfg.userData, pair)
99-
pushSessionData!(dfg::AbstractDFG, pair::Pair{Symbol,String}) = push!(dfg.userData, pair)
97+
#NOTE with API standardization this should become something like:
98+
# JT, I however do not feel we should force it, as I prever dot notation
99+
getUserData(dfg::AbstractDFG, key::Symbol)::Dict{Symbol, String} = dfg.UserData[key]
100+
getRobotData(dfg::AbstractDFG, key::Symbol)::Dict{Symbol, String} = dfg.RobotData[key]
101+
getSessionData(dfg::AbstractDFG, key::Symbol)::Dict{Symbol, String} = dfg.SessionData[key]
100102

101-
popUserData!(dfg::AbstractDFG, key::Symbol) = pop!(dfg.userData, key)
102-
popRobotData!(dfg::AbstractDFG, key::Symbol) = pop!(dfg.userData, key)
103-
popSessionData!(dfg::AbstractDFG, key::Symbol) = pop!(dfg.userData, key)
103+
updateUserData!(dfg::AbstractDFG, pair::Pair{Symbol,String}) = push!(dfg.userData, pair)
104+
updateRobotData!(dfg::AbstractDFG, pair::Pair{Symbol,String}) = push!(dfg.userData, pair)
105+
updateSessionData!(dfg::AbstractDFG, pair::Pair{Symbol,String}) = push!(dfg.userData, pair)
106+
107+
deleteUserData!(dfg::AbstractDFG, key::Symbol) = pop!(dfg.userData, key)
108+
deleteRobotData!(dfg::AbstractDFG, key::Symbol) = pop!(dfg.userData, key)
109+
deleteSessionData!(dfg::AbstractDFG, key::Symbol) = pop!(dfg.userData, key)
110+
111+
emptyUserData!(dfg::AbstractDFG) = empty!(dfg.userData)
112+
emptyRobotData!(dfg::AbstractDFG) = empty!(dfg.userData)
113+
emptySessionData!(dfg::AbstractDFG) = empty!(dfg.userData)
104114

105115
"""
106116
$(SIGNATURES)
@@ -432,6 +442,88 @@ function _copyIntoGraph!(sourceDFG::G, destDFG::H, variableFactorLabels::Vector{
432442
return nothing
433443
end
434444

445+
#TODO UNTESTED and NOT FILLED IN, just to define function signatures
446+
"""
447+
$(SIGNATURES)
448+
"""
449+
function getVariableSolverData(dfg::AbstractDFG, variablekey::Symbol, solvekey::Symbol=:default)
450+
return solverData(getVariable(dfg, variablekey), solvekey)
451+
end
452+
453+
"""
454+
$(SIGNATURES)
455+
456+
"""
457+
function addVariableSolverData!(dfg::AbstractDFG, variablekey::Symbol, vnd::VariableNodeData, solvekey::Symbol=:default)
458+
#for InMemoryDFGTypes, cloud would update here
459+
error("not implemented")
460+
461+
var = getVariable(dfg, variablekey)
462+
463+
if haskey(var.solverDataDict, solvekey)
464+
error("VariableNodeData '$(solvekey)' already exists")
465+
end
466+
467+
var.solverDataDict[solvekey] = vnd
468+
# setSolverData!(var, vnd, solvekey)
469+
470+
end
471+
472+
"""
473+
$(SIGNATURES)
474+
Add a new solver data entry from a deepcopy of the source variable solver data.
475+
"""
476+
addVariableSolverData!(dfg::AbstractDFG, sourceVariable::DFGVariable, solvekey::Symbol=:default) =
477+
addVariableSolverData!(dfg, sourceVariable.label, deepcopy(solverData(sourceVariable, solvekey)), solvekey)
478+
479+
480+
"""
481+
$(SIGNATURES)
482+
"""
483+
function updateVariableSolverData!(dfg::AbstractDFG, variablekey::Symbol, vnd::VariableNodeData, solvekey::Symbol=:default)
484+
485+
#This is basically just setSolverData
486+
var = getVariable(dfg, variablekey)
487+
488+
#for InMemoryDFGTypes, cloud would update here
489+
# var.solverDataDict[solvekey] = deepcopy(vnd)
490+
setSolverData!(var, vnd, solvekey)
491+
end
492+
493+
"""
494+
$(SIGNATURES)
495+
Update the destination variable solver data with a deepcopy of the source variable solver data
496+
"""
497+
updateVariableSolverData!(dfg::AbstractDFG, sourceVariable::DFGVariable, solvekey::Symbol=:default) =
498+
updateVariableSolverData!(dfg, sourceVariable.label, deepcopy(solverData(sourceVariable, solvekey)), solvekey)
499+
500+
function updateVariableSolverData!(dfg::AbstractDFG, sourceVariables::Vector{DFGVariable}, solvekey::Symbol=:default)
501+
#I think cloud would do this in bulk for speed
502+
for var in sourceVariables
503+
updateVariableSolverData!(dfg, solverData(var, solvekey), var.label, solvekey)
504+
end
505+
end
506+
507+
"""
508+
$(SIGNATURES)
509+
"""
510+
function deleteVariableSolverData!(dfg::AbstractDFG, variablekey::Symbol, solvekey::Symbol=:default)
511+
512+
var = getVariable(dfg, variablekey)
513+
514+
if !haskey(var.solverDataDict, solvekey)
515+
error("VariableNodeData '$(solvekey)' does not exist")
516+
end
517+
518+
vnd = pop!(var.solverDataDict, solvekey)
519+
520+
return vnd
521+
end
522+
523+
# deleteVariableSolverData!(dfg::AbstractDFG, sourceVariable::DFGVariable, solvekey::Symbol=:default) =
524+
# deleteVariableSolverData!(dfg, sourceVariable.label, solvekey)
525+
526+
435527
"""
436528
$(SIGNATURES)
437529
Merges and updates solver and estimate data for a variable (variable can be from another graph).
@@ -642,11 +734,11 @@ Checks whether it both exists in the graph and is a variable.
642734
(If you rather want a quick for type, just do node isa DFGVariable)
643735
"""
644736
function isVariable(dfg::G, sym::Symbol) where G <: AbstractDFG
645-
error("isVariable not implemented for $(typeof(dfg))")
737+
error("isVariable not implemented for $(typeof(dfg))")
646738
end
647739
# Alias - bit ridiculous but know it'll come up at some point. Does existential and type check.
648740
function isVariable(dfg::G, node::N)::Bool where {G <: AbstractDFG, N <: DFGNode}
649-
return isVariable(dfg, node.label)
741+
return isVariable(dfg, node.label)
650742
end
651743

652744
"""
@@ -657,11 +749,11 @@ Checks whether it both exists in the graph and is a factor.
657749
(If you rather want a quicker for type, just do node isa DFGFactor)
658750
"""
659751
function isFactor(dfg::G, sym::Symbol) where G <: AbstractDFG
660-
error("isFactor not implemented for $(typeof(dfg))")
752+
error("isFactor not implemented for $(typeof(dfg))")
661753
end
662754
# Alias - bit ridiculous but know it'll come up at some point. Does existential and type check.
663755
function isFactor(dfg::G, node::N)::Bool where {G <: AbstractDFG, N <: DFGNode}
664-
return isFactor(dfg, node.label)
756+
return isFactor(dfg, node.label)
665757
end
666758

667759
"""

0 commit comments

Comments
 (0)