Skip to content

Commit c639c9c

Browse files
authored
Feature/lightdfg (#85)
* Factor Graphs using LightGraphs and Dict bidirectional maps * LighDFG with FactorGraphs BiDict
1 parent 291fb7b commit c639c9c

File tree

7 files changed

+831
-3
lines changed

7 files changed

+831
-3
lines changed

src/DistributedFactorGraphs.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ include("LightGraphsDFG/LightGraphsDFG.jl")
5050
include("SymbolDFG/SymbolDFG.jl")
5151
@reexport using .SymbolDFGs
5252

53+
include("LightDFG/LightDFG.jl")
54+
@reexport using .LightDFGs
5355

5456
export saveDFG, loadDFG
5557

@@ -62,7 +64,7 @@ function __init__()
6264
Rows are all factors, columns are all variables, and each cell contains either nothing or the symbol of the relating factor.
6365
The first column is the factor headings.
6466
"""
65-
function getAdjacencyMatrixDataFrame(dfg::Union{GraphsDFG, LightGraphsDFG, SymbolDFG})::Main.DataFrames.DataFrame
67+
function getAdjacencyMatrixDataFrame(dfg::Union{GraphsDFG, LightGraphsDFG, SymbolDFG, LightDFG})::Main.DataFrames.DataFrame
6668
varLabels = sort(map(v->v.label, getVariables(dfg)))
6769
factLabels = sort(map(f->f.label, getFactors(dfg)))
6870
adjDf = DataFrames.DataFrame(:Factor => Union{Missing, Symbol}[])

src/LightDFG/FactorGraphs/BiMaps.jl

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# import Base: getindex, setindex!, firstindex, lastindex, iterate, keys, isempty
2+
struct BiDictMap{T <: Integer}
3+
int_sym::Dict{T,Symbol}
4+
sym_int::Dict{Symbol,T}
5+
end
6+
7+
BiDictMap{T}(;sizehint=100) where T<:Integer = begin
8+
int_sym = Dict{T,Symbol}()
9+
sizehint!(int_sym, sizehint)
10+
sym_int = Dict{Symbol,T}()
11+
sizehint!(sym_int, sizehint)
12+
BiDictMap{T}(int_sym, sym_int)
13+
end
14+
15+
BiDictMap(;sizehint=100) = BiDictMap{Int}(;sizehint=sizehint)
16+
17+
18+
Base.getindex(b::BiDictMap, key::Int) = b.int_sym[key]
19+
Base.getindex(b::BiDictMap, key::Symbol) = b.sym_int[key]
20+
21+
# setindex!(b, value, key) = b[key] = value
22+
function Base.setindex!(b::BiDictMap, s::Symbol, i::Int)
23+
haskey(b.sym_int, s) && delete!(b.int_sym, b[s])
24+
haskey(b.int_sym, i) && delete!(b.sym_int, b[i])
25+
26+
b.int_sym[i] = s
27+
b.sym_int[s] = i
28+
end
29+
30+
function Base.setindex!(b::BiDictMap, i::Int, s::Symbol)
31+
haskey(b.int_sym, i) && delete!(b.sym_int, b[i])
32+
haskey(b.sym_int, s) && delete!(b.int_sym, b[s])
33+
34+
b.int_sym[i] = s
35+
b.sym_int[s] = i
36+
end
37+
38+
function Base.delete!(b::BiDictMap, i::Int)
39+
s = b[i]
40+
delete!(b.int_sym, i)
41+
delete!(b.sym_int, s)
42+
return b
43+
end
44+
45+
Base.haskey(b::BiDictMap, s::Symbol) = haskey(b.sym_int, s)
46+
Base.haskey(b::BiDictMap, i::Int) = haskey(b.int_sym, i)
47+
48+
49+
Base.firstindex(v::BiDictMap) = 1
50+
Base.lastindex(v::BiDictMap) = length(v.int_sym)
51+
Base.iterate(v::BiDictMap, i=1) = (length(v.int_sym) < i ? nothing : (v.int_sym[i], i + 1))
52+
Base.keys(v::BiDictMap) = Base.OneTo(length(v.int_sym))
53+
Base.isempty(v::BiDictMap) = (length(v.int_sym) == 0)
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
module FactorGraphs
2+
using LightGraphs
3+
4+
import Base:
5+
eltype, show, ==, Pair,
6+
Tuple, copy, length, size,
7+
issubset, zero, getindex
8+
# import Random:
9+
# randstring, seed!
10+
11+
import LightGraphs:
12+
AbstractGraph, src, dst, edgetype, nv,
13+
ne, vertices, edges, is_directed,
14+
add_vertex!, add_edge!, rem_vertex!, rem_edge!,
15+
has_vertex, has_edge, inneighbors, outneighbors,
16+
weights, indegree, outdegree, degree,
17+
induced_subgraph,
18+
loadgraph, savegraph, AbstractGraphFormat,
19+
reverse
20+
21+
import LightGraphs.SimpleGraphs:
22+
AbstractSimpleGraph, SimpleGraph, SimpleDiGraph,
23+
SimpleEdge, fadj, badj
24+
25+
export
26+
FactorGraph,
27+
# addVariable!,
28+
# addFactor!,
29+
LightBayesGraph,
30+
filter_edges,
31+
filter_vertices,
32+
reverse
33+
34+
import DistributedFactorGraphs: DFGNode
35+
const AbstractNodeType = DFGNode
36+
37+
include("BiMaps.jl")
38+
39+
struct FactorGraph{T <: Integer,V <: AbstractNodeType, F <: AbstractNodeType} <: AbstractGraph{T}
40+
graph::SimpleGraph{T}
41+
labels::BiDictMap{T}
42+
variables::Dict{Symbol,V}
43+
factors::Dict{Symbol,F}
44+
end
45+
46+
function FactorGraph{T, V, F}(nv::Int=100, nf::Int=100) where {T <: Integer, V <: AbstractNodeType, F <: AbstractNodeType}
47+
fadjlist = Vector{Vector{T}}()
48+
sizehint!(fadjlist, nv + nf)
49+
g = SimpleGraph{T}(0, fadjlist)
50+
labels = BiDictMap{T}(sizehint=nv+nf)
51+
variables = Dict{Symbol,V}()
52+
sizehint!(variables, nv)
53+
factors = Dict{Symbol,F}()
54+
sizehint!(factors, nf)
55+
return FactorGraph{T, V, F}(g, labels, variables, factors)
56+
end
57+
58+
# fg = FactorGraph{Int, AbstractNodeType, AbstractNodeType}()
59+
60+
FactorGraph() = FactorGraph{Int, AbstractNodeType, AbstractNodeType}()
61+
FactorGraph{V,F}() where {V <: AbstractNodeType, F <: AbstractNodeType} = FactorGraph{Int, V, F}()
62+
63+
64+
function show(io::IO, g::FactorGraph)
65+
dir = is_directed(g) ? "directed" : "undirected"
66+
print(io, "{$(nv(g)), $(ne(g))} $dir $(eltype(g)) $(typeof(g))")
67+
end
68+
69+
@inline fadj(g::FactorGraph, x...) = fadj(g.graph, x...)
70+
@inline badj(g::FactorGraph, x...) = badj(g.graph, x...)
71+
72+
73+
eltype(g::FactorGraph) = eltype(g.graph)
74+
edgetype(g::FactorGraph) = edgetype(g.graph)
75+
nv(g::FactorGraph) = nv(g.graph)
76+
vertices(g::FactorGraph) = vertices(g.graph)
77+
78+
ne(g::FactorGraph) = ne(g.graph)
79+
edges(g::FactorGraph) = edges(g.graph)
80+
81+
has_vertex(g::FactorGraph, x...) = has_vertex(g.graph, x...)
82+
@inline has_edge(g::FactorGraph, x...) = has_edge(g.graph, x...)
83+
84+
inneighbors(g::FactorGraph, v::Integer) = inneighbors(g.graph, v)
85+
outneighbors(g::FactorGraph, v::Integer) = fadj(g.graph, v)
86+
87+
is_directed(::Type{FactorGraph}) = false
88+
is_directed(::Type{FactorGraph{T,V,F}}) where {T,V,F} = false
89+
is_directed(g::FactorGraph) = false
90+
91+
zero(g::FactorGraph{T,V,F}) where {T,V,F} = FactorGraph{T,V,F}(0,0)
92+
93+
# TODO issubset(g::T, h::T) where T <: FactorGraph = issubset(g.graph, h.graph)
94+
95+
"""
96+
add_edge!(g, u, v)
97+
Add an edge `(u, v)` to FactorGraph `g`.
98+
return true if the edge has been added, false otherwise
99+
"""
100+
@inline add_edge!(g::FactorGraph, x...) = add_edge!(g.graph, x...)
101+
102+
@inline rem_edge!(g::FactorGraph, x...) = rem_edge!(g.graph, x...)
103+
104+
105+
function addVariable!(g::FactorGraph{T, V, F}, variable::V) where {T, V, F}
106+
107+
label = variable.label
108+
109+
haskey(g.labels, label) && (@error "Label already in fg"; return false) #TODO debug error or exception?
110+
111+
add_vertex!(g.graph) || return false
112+
113+
g.labels[nv(g.graph)] = label
114+
115+
push!(g.variables, label=>variable)
116+
117+
return true
118+
end
119+
120+
function addFactor!(g::FactorGraph{T, V, F}, variableLabels::Vector{Symbol}, factor::F)::Bool where {T, V, F}
121+
122+
haskey(g.labels, factor.label) && (@error "Label $(factor.label) already in fg"; return false)
123+
124+
for vlabel in variableLabels
125+
!haskey(g.labels, vlabel) && (@error "Variable '$(vlabel)' not found in graph when creating Factor '$(factor.label)'"; return false) #TODO debug error or exception?
126+
end
127+
128+
add_vertex!(g.graph) || return false
129+
130+
g.labels[nv(g.graph)] = factor.label
131+
132+
push!(g.factors, factor.label=>factor)
133+
134+
# add the edges
135+
for vlabel in variableLabels
136+
add_edge!(g, g.labels[vlabel], nv(g.graph)) || return false
137+
end
138+
139+
return true
140+
end
141+
142+
143+
144+
function rem_vertex!(g::FactorGraph{T,V,F}, v::Integer) where {T,V,F}
145+
v in vertices(g) || return false
146+
lastv = nv(g)
147+
148+
rem_vertex!(g.graph, v) || return false
149+
150+
label = g.labels[v]
151+
delete!(g.variables, label)
152+
delete!(g.factors, label)
153+
154+
if v != lastv
155+
g.labels[v] = g.labels[lastv] #lastSym
156+
else
157+
delete!(g.labels, v)
158+
end
159+
160+
return true
161+
end
162+
163+
164+
165+
end

src/LightDFG/LightDFG.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module LightDFGs
2+
3+
using LightGraphs
4+
using DocStringExtensions
5+
6+
import ...DistributedFactorGraphs: AbstractDFG, DFGNode, AbstractParams, NoSolverParams, DFGVariable, DFGFactor
7+
8+
# import DFG functions to exstend
9+
import ...DistributedFactorGraphs: setSolverParams,
10+
getInnerGraph,
11+
getFactor,
12+
setDescription,
13+
getLabelDict,
14+
addVariable!,
15+
getVariable,
16+
getAddHistory,
17+
addFactor!,
18+
getSolverParams,
19+
exists,
20+
getDescription,
21+
updateVariable!,
22+
updateFactor!,
23+
deleteVariable!,
24+
deleteFactor!,
25+
getVariables,
26+
getVariableIds,
27+
ls,
28+
getFactors,
29+
getFactorIds,
30+
lsf,
31+
isFullyConnected,
32+
hasOrphans,
33+
getNeighbors,
34+
getSubgraphAroundNode,
35+
getSubgraph,
36+
getAdjacencyMatrix
37+
38+
include("FactorGraphs/FactorGraphs.jl")
39+
using .FactorGraphs
40+
41+
# export SymbolEdge, is_directed, has_edge
42+
# Imports
43+
include("entities/LightDFG.jl")
44+
include("services/LightDFG.jl")
45+
46+
# Exports
47+
export LightDFG
48+
49+
export exists
50+
export getLabelDict, getDescription, setDescription, getInnerGraph, getAddHistory, getSolverParams, setSolverParams
51+
#
52+
export getAddHistory, getDescription, getLabelDict
53+
export addVariable!, addFactor!
54+
export ls, lsf, getVariables, getFactors, getVariableIds, getFactorIds
55+
export getVariable, getFactor
56+
export updateVariable!, updateFactor!
57+
export deleteVariable!, deleteFactor!
58+
export getAdjacencyMatrix
59+
export getAdjacencyMatrixDataFrame
60+
export getNeighbors
61+
export getSubgraphAroundNode
62+
export getSubgraph
63+
export isFullyConnected, hasOrphans
64+
export toDot, toDotFile
65+
66+
end

src/LightDFG/entities/LightDFG.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
mutable struct LightDFG{T <: AbstractParams, V <: DFGNode, F <:DFGNode} <: AbstractDFG
3+
g::FactorGraph{Int, V, F}
4+
description::String
5+
userId::String
6+
robotId::String
7+
sessionId::String
8+
#NOTE does not exist
9+
# nodeCounter::Int64
10+
#NOTE does not exist
11+
# labelDict::Dict{Symbol, Int64}
12+
addHistory::Vector{Symbol} #TODO: Discuss more - is this an audit trail?
13+
solverParams::T # Solver parameters
14+
end
15+
16+
#TODO? do we not want props such as userId, robotId, sessionId, etc...
17+
function LightDFG{T,V,F}(g::FactorGraph{Int,V,F}=FactorGraph{Int,V,F}();
18+
description::String="LightGraphs.jl implementation",
19+
userId::String="User ID",
20+
robotId::String="Robot ID",
21+
sessionId::String="Session ID",
22+
params::T=NoSolverParams()) where {T <: AbstractParams, V <:DFGNode, F<:DFGNode}
23+
24+
LightDFG{T,V,F}(g, description, userId, robotId, sessionId, Symbol[], params)
25+
end
26+
27+
LightDFG{T}(g::FactorGraph{Int,DFGVariable,DFGFactor}=FactorGraph{Int,DFGVariable,DFGFactor}(); kwargs...) where T <: AbstractParams = LightDFG{T,DFGVariable,DFGFactor}(g, kwargs...)
28+
29+
Base.propertynames(x::LightDFG, private::Bool=false) =
30+
(:g, :description, :userId, :robotId, :sessionId, :nodeCounter, :labelDict, :addHistory, :solverParams)
31+
# (private ? fieldnames(typeof(x)) : ())...)
32+
33+
Base.getproperty(x::LightDFG,f::Symbol) = begin
34+
if f == :nodeCounter
35+
@error "Depreciated? returning number of nodes"
36+
nv(x.g)
37+
elseif f == :labelDict
38+
@error "Depreciated? Concider using exists(dfg,label) instead. Returing internals copy"
39+
copy(x.g.labels.sym_int)
40+
else
41+
getfield(x,f)
42+
end
43+
end

0 commit comments

Comments
 (0)