Skip to content
This repository has been archived by the owner on Oct 8, 2021. It is now read-only.

Commit

Permalink
Merge pull request #13 from kescobo/dictindex
Browse files Browse the repository at this point in the history
[WIP] Adding Index Dictionary
  • Loading branch information
sbromberger authored Dec 28, 2017
2 parents 50d2363 + 5e34a14 commit 558c7bb
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 8 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,26 @@ julia> enumerate_paths(dijkstra_shortest_paths(mg, 1), 3)
1
2
3
# use vertex values as indices
julia> G = MetaGraph(100)
{100, 0} undirected Int64 metagraph with Float64 weights defined by :weight (default weight 1.0)
julia> for i in 1:100
set_prop!(G, i, :name, "node$i")
end
julia> set_indexing_prop!(G, :name)
Set(Symbol[:name])
# nodes can now be found by the value in the index
julia> G["node4", :name]
4
# You can also find the value of an index by the vertex number (note, this behavior will dominate if index values are also integers)
julia> G[4, :name]
"node4"
julia> set_prop!(G, 3, :name, "name3")
ERROR: ':name' is an indexing property, use `set_indexing_prop!()` instead
```
86 changes: 83 additions & 3 deletions src/MetaGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ export
defaultweight,
filter_edges,
filter_vertices,
MGFormat
MGFormat,
set_indexing_prop!

const PropDict = Dict{Symbol,Any}
const MetaDict = Dict{Symbol,Dict{Any,Integer}}

abstract type AbstractMetaGraph{T} <: AbstractGraph{T} end

function show(io::IO, g::AbstractMetaGraph)
Expand Down Expand Up @@ -101,6 +104,7 @@ end
rem_edge!(g.graph, x...)
end


"""
add_vertex!(g)
add_vertex!(g, s, v)
Expand Down Expand Up @@ -150,6 +154,23 @@ function getindex(w::MetaWeights{T,U}, u::Integer, v::Integer)::U where T <: Int
return U(get(w.eprops[e], w.weightfield, w.defaultweight))
end

function getindex(g::AbstractMetaGraph, prop::Symbol)
!haskey(g.metaindex, prop) && error("':$prop' is not an index")
return g.metaindex[prop]
end

function getindex(g::AbstractMetaGraph, indx::Any, prop::Symbol)
haskey(g.metaindex, prop) || error("':$prop' is not an index")
typeof(indx) <: eltype(keys(g.metaindex[prop])) || error("Index type does not match keys of metaindex '$prop'")
!haskey(g.metaindex[prop], indx) && error("No node with prop $prop and key $indx")
return g.metaindex[prop][indx]
end

function getindex(g::AbstractMetaGraph, indx::Integer, prop::Symbol)
haskey(g.metaindex, prop) || error("':$prop' is not an index")
return props(g,indx)[prop]
end

size(d::MetaWeights) = (d.n, d.n)

weights(g::AbstractMetaGraph) = MetaWeights(g)
Expand Down Expand Up @@ -213,7 +234,9 @@ edge `e` (optionally referenced by source vertex `s` and destination vertex `d`)
"""
set_props!(g::AbstractMetaGraph, d::Dict) = merge!(g.gprops, d)
set_props!(g::AbstractMetaGraph, v::Integer, d::Dict) =
if !_hasdict(g, v)
if length(intersect(keys(d), g.indices)) != 0
error("The following properties are indexing_props and cannot be updated: $(intersect(keys(d), g.indices))")
elseif !_hasdict(g, v)
g.vprops[v] = d
else
merge!(g.vprops[v], d)
Expand All @@ -232,7 +255,12 @@ Set (replace) property `prop` with value `val` in graph `g`, vertex `v`, or
edge `e` (optionally referenced by source vertex `s` and destination vertex `d`).
"""
set_prop!(g::AbstractMetaGraph, prop::Symbol, val) = set_props!(g, Dict(prop => val))
set_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol, val) = set_props!(g, v, Dict(prop => val))
set_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol, val) =
if in(prop, g.indices)
error("':$prop' is an indexing property, use `set_indexing_prop!()` instead")
else
set_props!(g, v, Dict(prop => val))
end
set_prop!(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol, val) = set_props!(g, e, Dict(prop => val))

set_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol, val) where T = set_prop!(g, Edge(T(u), T(v)), prop, val)
Expand All @@ -254,6 +282,58 @@ rem_prop!(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol) = delete!(g.eprops[

rem_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol) where T = rem_prop!(g, Edge(T(u), T(v)), prop)

"""
default_index_value(v, prop, index_values; exclude=nothing)
Provides a default index value for a vertex if no value currently exists. The default is a string: "\$prop\$i" where `prop` is the property name and `i` is the vertex number. If some other vertex already has this name, a randomized string is generated (though the way it is generated is deterministic).
"""
function default_index_value(v::Integer, prop::Symbol, index_values::Set{Any}; exclude=nothing)
val = string(prop) * string(v)
if in(val, index_values) || val == exclude
srand(v+hash(prop))
val = randstring()
warn("'$(string(prop))$v' is already in index, setting ':$prop' for vertex $v to $val")
end
return val
end

"""
set_indexing_prop!(g, prop)
set_indexing_prop!(g, v, prop, val)
Make property `prop` into an indexing property. If any values for this property
are already set, each vertex must have unique values. Optionally, set the index
`val` for vertex `v`. Any vertices without values will be set to a default
("(prop)(v)").
"""
function set_indexing_prop!(g::AbstractMetaGraph, prop::Symbol; exclude=nothing)
in(prop, g.indices) && return g.indices
index_values = [g.vprops[v][prop] for v in keys(g.vprops) if haskey(g.vprops[v], prop)]
length(index_values) != length(union(index_values)) && error("Cannot make $prop an index, duplicate values detected")
index_values = Set(index_values)

g.metaindex[prop] = Dict{Any, Integer}()
for v in 1:size(g)[1]
if !haskey(g.vprops, v) || !haskey(g.vprops[v], prop)
val = default_index_value(v, prop, index_values, exclude=exclude)
set_prop!(g, v, prop, val)
end
g.metaindex[prop][g.vprops[v][prop]] = v
end
push!(g.indices, prop)
return g.indices
end

function set_indexing_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol, val::Any)
!in(prop, g.indices) && set_indexing_prop!(g, prop, exclude=val)
(haskey(g.metaindex[prop], val) && g.vprops[v][prop] == val) && return g.indices
haskey(g.metaindex[prop], val) && error("':$prop' index already contains $val")

delete!(g.metaindex[prop], g.vprops[v][prop])
g.metaindex[prop][val] = v
g.vprops[v][prop] = val
return g.indices
end
"""
clear_props!(g)
clear_props!(g, v)
Expand Down
10 changes: 8 additions & 2 deletions src/metadigraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mutable struct MetaDiGraph{T<:Integer,U<:Real} <: AbstractMetaGraph{T}
gprops::PropDict
weightfield::Symbol
defaultweight::U
metaindex::MetaDict
indices::Set{Symbol}
end

function MetaDiGraph(x, weightfield::Symbol, defaultweight::U) where U <: Real
Expand All @@ -13,8 +15,12 @@ function MetaDiGraph(x, weightfield::Symbol, defaultweight::U) where U <: Real
vprops = Dict{T,PropDict}()
eprops = Dict{SimpleEdge{T},PropDict}()
gprops = PropDict()
metaindex = MetaDict()
idxs = Set{Symbol}()

MetaDiGraph(g, vprops, eprops, gprops, weightfield, defaultweight)
MetaDiGraph(g, vprops, eprops, gprops,
weightfield, defaultweight,
metaindex, idxs)
end

MetaDiGraph() = MetaDiGraph(SimpleDiGraph())
Expand Down Expand Up @@ -60,4 +66,4 @@ function set_props!(g::MetaDiGraph, e::SimpleEdge, d::Dict)
end
end

zero(g::MetaDiGraph{T,U}) where T where U = MetaDiGraph{T,U}(SimpleDiGraph{T}())
zero(g::MetaDiGraph{T,U}) where T where U = MetaDiGraph{T,U}(SimpleDiGraph{T}())
6 changes: 5 additions & 1 deletion src/metagraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mutable struct MetaGraph{T<:Integer,U<:Real} <: AbstractMetaGraph{T}
gprops::PropDict
weightfield::Symbol
defaultweight::U
metaindex::MetaDict
indices::Set{Symbol}
end

function MetaGraph(x, weightfield::Symbol, defaultweight::U) where U <: Real
Expand All @@ -13,7 +15,9 @@ function MetaGraph(x, weightfield::Symbol, defaultweight::U) where U <: Real
vprops = Dict{T,PropDict}()
eprops = Dict{SimpleEdge{T},PropDict}()
gprops = PropDict()
MetaGraph(g, vprops, eprops, gprops, weightfield, defaultweight)
metaindex = MetaDict()
idxs = Set{Symbol}()
MetaGraph(g, vprops, eprops, gprops, weightfield, defaultweight, metaindex, idxs)
end

MetaGraph() = MetaGraph(SimpleGraph())
Expand Down
60 changes: 59 additions & 1 deletion test/metagraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -305,5 +305,63 @@ importall MetaGraphs
@test rem_vertex!(mga, 1)
@test get_prop(mga, 1, :name) == "5"
@test isempty(props(mga, 5))


end

@testset "MetaIndexing" begin
G = MetaGraph(100)
dG = MetaDiGraph(100)

for i in 1:100
set_prop!(G, i, :name, "gnode_$i")
set_prop!(dG, i, :name, "dgnode_$i")
end

for i in 1:100
set_prop!(G, i, :not_unique, "$(i%5)")
set_prop!(dG, i, :not_unique, "$(i%5)")
end

@test set_indexing_prop!(G, :name) == Set{Symbol}([:name])
@test set_indexing_prop!(dG, :name) == Set{Symbol}([:name])
@test_throws ErrorException set_indexing_prop!(G, :not_unique)
@test_throws ErrorException set_indexing_prop!(dG, :not_unique)

@test G["gnode_3", :name] == 3
@test dG["dgnode_99", :name] == 99

@test_throws ErrorException set_indexing_prop!(G, 4, :name, "gnode_3")
@test_throws ErrorException set_indexing_prop!(dG, 4, :name, "dgnode_3")
@test_throws ErrorException set_prop!(G, 3, :name, "name3")
@test_throws ErrorException set_prop!(dG, 3, :name, "name3")
@test_throws ErrorException set_props!(G, 5, Dict(:name=>"name", :other_name=>"something"))
@test_throws ErrorException set_props!(dG, 5, Dict(:name=>"name", :other_name=>"something"))

set_indexing_prop!(G, 50, :name, "another name")
set_indexing_prop!(G, 50, :name, "another name")

set_indexing_prop!(G, 43, :foo, "foo1")
set_indexing_prop!(dG, 43, :foo, "foo1")
@test G[1, :foo] != "foo1"
@test dG[1, :foo] != "foo1"

set_indexing_prop!(G, 42, :foo, "bar")
set_indexing_prop!(dG, 42, :foo, "bar")

@test G["foo10", :foo] == 10
@test G[12, :foo] == "foo12"
@test G["bar", :foo] == 42
@test G[42, :foo] == "bar"
@test isa(G[:foo], Dict{Any, Integer})
@test isa(dG[:foo], Dict{Any, Integer})


@test dG["foo30", :foo] == 30
@test dG[79, :foo] == "foo79"
@test dG["bar", :foo] == 42
@test dG[42, :foo] == "bar"

@test_throws ErrorException G[:not_a_key]
@test_throws ErrorException dG[:not_a_key]

end
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ tests = [
tp = joinpath(testdir, "$(t).jl")
include(tp)
end
end
end

0 comments on commit 558c7bb

Please sign in to comment.