Skip to content

Commit

Permalink
added default option to get_prop (#41)
Browse files Browse the repository at this point in the history
* added default option to get_prop

* throw error when accessing non existent edge

* get_prop with default using get with default
  • Loading branch information
henrik-wolf authored Dec 12, 2023
1 parent 42a2f26 commit 0cf7d6a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 39 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ Dict{Symbol,Any} with 2 entries:
julia> get_prop(mg, 2, :name)
"John"

# set a default value to return in case the property does not exist
julia> get_prop(mg, 2, :nonexistent_prop, "default value")
"default value"

# delete a specific property
julia> rem_prop!(mg, 1, :name)
Dict{Symbol,Any} with 1 entry:
Expand Down
57 changes: 35 additions & 22 deletions src/MetaGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ has_vertex(g::AbstractMetaGraph, x...) = has_vertex(g.graph, x...)
inneighbors(g::AbstractMetaGraph, v::Integer) = inneighbors(g.graph, v)
outneighbors(g::AbstractMetaGraph, v::Integer) = fadj(g.graph, v)

issubset(g::T, h::T) where T <: AbstractMetaGraph = issubset(g.graph, h.graph)
issubset(g::T, h::T) where {T<:AbstractMetaGraph} = issubset(g.graph, h.graph)

"""
add_edge!(g, u, v, s, val)
Expand Down Expand Up @@ -135,10 +135,10 @@ function rem_vertex!(g::AbstractMetaGraph, v::Integer)
lasteoutprops = Dict(n => props(g, lastv, n) for n in outneighbors(g, lastv))
lasteinprops = Dict(n => props(g, n, lastv) for n in inneighbors(g, lastv))
for ind in g.indices
if haskey(props(g,lastv),ind)
if haskey(props(g, lastv), ind)
pop!(g.metaindex[ind], get_prop(g, lastv, ind))
end
if haskey(props(g,v),ind)
if haskey(props(g, v), ind)
v != lastv && pop!(g.metaindex[ind], get_prop(g, v, ind))
end
end
Expand Down Expand Up @@ -191,7 +191,7 @@ function rem_vertex!(g::AbstractMetaGraph, v::Integer)
return true
end

struct MetaWeights{T <: Integer,U <: Real} <: AbstractMatrix{U}
struct MetaWeights{T<:Integer,U<:Real} <: AbstractMatrix{U}
n::T
weightfield::Symbol
defaultweight::U
Expand All @@ -203,7 +203,7 @@ show(io::IO, z::MIME"text/plain", x::MetaWeights) = show(io, x)

MetaWeights(g::AbstractMetaGraph) = MetaWeights{eltype(g),eltype(g.defaultweight)}(nv(g), g.weightfield, g.defaultweight, g.eprops, is_directed(g))

function getindex(w::MetaWeights{T,U}, u::Integer, v::Integer)::U where T <: Integer where U <: Real
function getindex(w::MetaWeights{T,U}, u::Integer, v::Integer)::U where {T<:Integer} where {U<:Real}
_e = Edge(u, v)
e = !w.directed && !Graphs.is_ordered(_e) ? reverse(_e) : _e
!haskey(w.eprops, e) && return w.defaultweight
Expand Down Expand Up @@ -253,20 +253,33 @@ props(g::AbstractMetaGraph, v::Integer) = get(PropDict, g.vprops, v)
props(g::AbstractMetaGraph, u::Integer, v::Integer) = props(g, Edge(u, v))

"""
get_prop(g, prop)
get_prop(g, v, prop)
get_prop(g, e, prop)
get_prop(g, s, d, prop)
get_prop(g, prop::Symbol)
get_prop(g, prop::Symbol, default)
get_prop(g, v, prop::Symbol)
get_prop(g, v, prop::Symbol, default)
get_prop(g, e, prop::Symbol)
get_prop(g, e, prop::Symbol, default)
get_prop(g, s, d, prop::Symbol)
get_prop(g, s, d, prop::Symbol, default)
Return the property `prop` defined for graph `g`, vertex `v`, or edge `e`
(optionally referenced by source vertex `s` and destination vertex `d`).
If property is not defined, return an error.
Use the version with `default`, to return a default value if the property is not defined. Otherwise, it will return an error.
"""
get_prop(g::AbstractMetaGraph, prop::Symbol) = props(g)[prop]
get_prop(g::AbstractMetaGraph, prop::Symbol, default) = get(props(g), prop, default)

get_prop(g::AbstractMetaGraph, v::Integer, prop::Symbol) = props(g, v)[prop]
get_prop(g::AbstractMetaGraph, v::Integer, prop::Symbol, default) = get(props(g, v), prop, default)

get_prop(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol) = props(g, e)[prop]
get_prop(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol, default) = get(props(g, e), prop, default)

get_prop(g::AbstractMetaGraph, u::Integer, v::Integer, prop::Symbol) = get_prop(g, Edge(u, v), prop)
get_prop(g::AbstractMetaGraph, u::Integer, v::Integer, prop::Symbol, default) = get_prop(g, Edge(u, v), prop, default)


"""
has_prop(g, prop)
Expand Down Expand Up @@ -300,15 +313,15 @@ end

function set_props!(g::AbstractMetaGraph, v::Integer, d::Dict)
if has_vertex(g, v)
for (prop,val) in d
for (prop, val) in d
index_available(g, v, prop, val) || error("':$prop' index already contains $val")
end
if !_hasdict(g, v)
g.vprops[v] = d
else
merge!(g.vprops[v], d)
end
for prop in intersect(keys(d), g.indices)
for prop in intersect(keys(d), g.indices)
g.metaindex[prop][d[prop]] = v
end
return true
Expand All @@ -317,7 +330,7 @@ function set_props!(g::AbstractMetaGraph, v::Integer, d::Dict)
end
# set_props!(g::AbstractMetaGraph, e::SimpleEdge, d::Dict) is dependent on directedness.

set_props!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, d::Dict) where T = set_props!(g, Edge(T(u), T(v)), d)
set_props!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, d::Dict) where {T} = set_props!(g, Edge(T(u), T(v)), d)

"""
set_prop!(g, prop, val)
Expand All @@ -339,7 +352,7 @@ set_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol, val) = begin
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)
set_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol, val) where {T} = set_prop!(g, Edge(T(u), T(v)), prop, val)

"""
rem_prop!(g, prop)
Expand All @@ -355,7 +368,7 @@ rem_prop!(g::AbstractMetaGraph, prop::Symbol) = delete!(g.gprops, prop)
rem_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol) = delete!(g.vprops[v], prop)
rem_prop!(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol) = delete!(g.eprops[e], prop)

rem_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol) where T = rem_prop!(g, Edge(T(u), T(v)), prop)
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)
Expand Down Expand Up @@ -416,7 +429,7 @@ function set_indexing_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol, val:
haskey(g.metaindex[prop], val) && error("':$prop' index already contains $val")

if !haskey(g.vprops, v)
push!(g.vprops, v=>Dict{Symbol,Any}())
push!(g.vprops, v => Dict{Symbol,Any}())
end
if haskey(g.vprops[v], prop)
delete!(g.metaindex[prop], g.vprops[v][prop])
Expand All @@ -438,7 +451,7 @@ clear_props!(g::AbstractMetaGraph, v::Integer) = _hasdict(g, v) && delete!(g.vpr
clear_props!(g::AbstractMetaGraph, e::SimpleEdge) = _hasdict(g, e) && delete!(g.eprops, e)
clear_props!(g::AbstractMetaGraph) = g.gprops = PropDict()

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

"""
weightfield!(g, prop)
Expand Down Expand Up @@ -514,7 +527,7 @@ filter_vertices(g::AbstractMetaGraph, prop::Symbol) =
filter_vertices(g::AbstractMetaGraph, prop::Symbol, val) =
filter_vertices(g, (g, x) -> has_prop(g, x, prop) && get_prop(g, x, prop) == val)

function _copy_props!(oldg::T, newg::T, vmap) where T <: AbstractMetaGraph
function _copy_props!(oldg::T, newg::T, vmap) where {T<:AbstractMetaGraph}
for (newv, oldv) in enumerate(vmap)
p = props(oldg, oldv)
if !isempty(p)
Expand All @@ -540,21 +553,21 @@ function _copy_props!(oldg::T, newg::T, vmap) where T <: AbstractMetaGraph
return nothing
end

function induced_subgraph(g::T, v::AbstractVector{U}) where T <: AbstractMetaGraph where U <: Integer
function induced_subgraph(g::T, v::AbstractVector{U}) where {T<:AbstractMetaGraph} where {U<:Integer}
inducedgraph, vmap = induced_subgraph(g.graph, v)
newg = T(inducedgraph)
_copy_props!(g, newg, vmap)
return newg, vmap
end

function induced_subgraph(g::T, v::AbstractVector{U}) where T <: AbstractMetaGraph where U <: SimpleEdge
function induced_subgraph(g::T, v::AbstractVector{U}) where {T<:AbstractMetaGraph} where {U<:SimpleEdge}
inducedgraph, vmap = induced_subgraph(g.graph, v)
newg = T(inducedgraph)
_copy_props!(g, newg, vmap)
return newg, vmap
end

induced_subgraph(g::T, filt::Iterators.Filter) where T <: AbstractMetaGraph =
induced_subgraph(g::T, filt::Iterators.Filter) where {T<:AbstractMetaGraph} =
induced_subgraph(g, collect(filt))

# TODO - would be nice to be able to apply a function to properties. Not sure
Expand All @@ -563,7 +576,7 @@ induced_subgraph(g::T, filt::Iterators.Filter) where T <: AbstractMetaGraph =

==(x::AbstractMetaGraph, y::AbstractMetaGraph) = x.graph == y.graph

copy(g::T) where T <: AbstractMetaGraph = deepcopy(g)
copy(g::T) where {T<:AbstractMetaGraph} = deepcopy(g)

include("metadigraph.jl")
include("metagraph.jl")
Expand Down
85 changes: 68 additions & 17 deletions test/metagraphs.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using MetaGraphs
import Graphs: SimpleGraphs
import Base64:
stringmime
stringmime


@testset "MetaGraphs" begin
Expand All @@ -21,7 +21,7 @@ import Base64:
mg = MetaGraph()
@test add_vertex!(mg, :color, "red") && get_prop(mg, nv(mg), :color) == "red"
@test add_vertex!(mg, Dict(:color => "red", :prop2 => "prop2")) && props(mg, nv(mg)) == Dict(:color => "red", :prop2 => "prop2")
@test add_edge!(mg, 1, 2, :color, "blue") && get_prop(mg, 1, 2, :color) == "blue"
@test add_edge!(mg, 1, 2, :color, "blue") && get_prop(mg, 1, 2, :color) == "blue"
@test add_vertex!(mg) && add_edge!(mg, 1, 3, Dict(:color => "red", :prop2 => "prop2")) && props(mg, 1, 3) == Dict(:color => "red", :prop2 => "prop2")

for g in testgraphs(gx)
Expand Down Expand Up @@ -78,6 +78,31 @@ import Base64:
U = @inferred(weighttype(mg))
@test @inferred(nv(MetaGraph{T,U}(6))) == 6

# get_prop with default argument
# vertices
set_prop!(mg, nv(mg), :testprop, "exists")
@test get_prop(mg, nv(mg), :testprop, "nonexistent") == "exists"
@test get_prop(mg, nv(mg), :testprop_nonexist, "nonexistent") == "nonexistent"
@test get_prop(mg, nv(mg) + 100, :testprop_nonexist, "nonexistent") == "nonexistent"

# edges
set_prop!(mg, 1, 2, :testedgeprop, "5 meters")
@test get_prop(mg, 1, 2, :testedgeprop, "0 meters") == "5 meters"
@test get_prop(mg, 1, 2, :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, 2, 4, :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, 2, 1, :testedgeprop, "0 meters") == "5 meters"
@test get_prop(mg, 2, 1, :testedgeprop_nonexist, "0 meters") == "0 meters"

@test get_prop(mg, Edge(1, 2), :testedgeprop, "0 meters") == "5 meters"
@test get_prop(mg, Edge(1, 2), :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, Edge(2, 4), :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, Edge(2, 1), :testedgeprop, "0 meters") == "5 meters"
@test get_prop(mg, Edge(2, 1), :testedgeprop_nonexist, "0 meters") == "0 meters"

# graph
set_prop!(mg, :testgraphpprop, "linegraph")
@test get_prop(mg, :testgraphpprop, "circlegraph") == "linegraph"
@test get_prop(mg, :testgraphpprop_nonexist, "circlegraph") == "circlegraph"
end

for g in testdigraphs(dgx)
Expand Down Expand Up @@ -135,6 +160,32 @@ import Base64:
T = @inferred(eltype(mg))
U = @inferred(weighttype(mg))
@test @inferred(nv(MetaDiGraph{T,U}(6))) == 6

# get_prop with default argument
# vertices
set_prop!(mg, nv(mg), :testprop, "exists")
@test get_prop(mg, nv(mg), :testprop, "nonexistent") == "exists"
@test get_prop(mg, nv(mg), :testprop_nonexist, "nonexistent") == "nonexistent"
@test get_prop(mg, nv(mg) + 100, :testprop_nonexist, "nonexistent") == "nonexistent"

# edges
set_prop!(mg, 1, 2, :testedgeprop, "5 meters")
@test get_prop(mg, 1, 2, :testedgeprop, "0 meters") == "5 meters"
@test get_prop(mg, 1, 2, :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, 2, 4, :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, 2, 1, :testedgeprop, "0 meters") == "0 meters"
@test get_prop(mg, 2, 1, :testedgeprop_nonexist, "0 meters") == "0 meters"

@test get_prop(mg, Edge(1, 2), :testedgeprop, "0 meters") == "5 meters"
@test get_prop(mg, Edge(1, 2), :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, Edge(2, 4), :testedgeprop_nonexist, "0 meters") == "0 meters"
@test get_prop(mg, Edge(2, 1), :testedgeprop, "0 meters") == "0 meters"
@test get_prop(mg, Edge(2, 1), :testedgeprop_nonexist, "0 meters") == "0 meters"

# graph
set_prop!(mg, :testgraphpprop, "linegraph")
@test get_prop(mg, :testgraphpprop, "circlegraph") == "linegraph"
@test get_prop(mg, :testgraphpprop_nonexist, "circlegraph") == "circlegraph"
end

for gbig in [SimpleGraph(0xff), SimpleDiGraph(0xff)]
Expand Down Expand Up @@ -299,7 +350,7 @@ import Base64:
@test weightfield!(mg, :weight) == :weight
@test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 2, 3]

@test set_props!(mg, 1, 2, Dict(:color => :blue, :action => "knows"))
@test set_props!(mg, 1, 2, Dict(:color => :blue, :action => "knows"))
@test length(props(mg, 1, 2)) == 3
@test rem_edge!(mg, 1, 2)
@test length(props(mg, 1, 2)) == 0
Expand Down Expand Up @@ -352,12 +403,12 @@ import Base64:
for v in vertices(mga)
set_prop!(mga, v, :name, string(v))
end
set_indexing_prop!(mga,:name)
set_indexing_prop!(mga, :name)
@test get_prop(mga, 1, :name) == "1"
@test get_prop(mga, 5, :name) == "5"
@test rem_vertex!(mga, 1)
@test get_prop(mga, 1, :name) == "5"
@test mga["5",:name] == 1
@test mga["5", :name] == 1
@test isempty(props(mga, 5))

# test for #22
Expand All @@ -373,19 +424,19 @@ import Base64:

# test for #72 - Multiple indicies that are not all used
let
test_graph = x-> begin
g=MetaGraph()
set_indexing_prop!(g,:IndexA)
set_indexing_prop!(g,:IndexB)
add_vertex!(g,:IndexA,"A")
x && add_vertex!(g,:IndexA,"B")
x && set_indexing_prop!(g,nv(g),:IndexB,"B")
add_vertex!(g,:IndexB,"C")
test_graph = x -> begin
g = MetaGraph()
set_indexing_prop!(g, :IndexA)
set_indexing_prop!(g, :IndexB)
add_vertex!(g, :IndexA, "A")
x && add_vertex!(g, :IndexA, "B")
x && set_indexing_prop!(g, nv(g), :IndexB, "B")
add_vertex!(g, :IndexB, "C")
g
end
mga=test_graph(true)
rem_vertex!(mga,2)
@test mga==test_graph(false)
mga = test_graph(true)
rem_vertex!(mga, 2)
@test mga == test_graph(false)
end

mga = MetaDiGraph(path_digraph(4))
Expand Down Expand Up @@ -482,7 +533,7 @@ end
@test MetaGraphs.index_available(dG, 7, :name, "dgnode_8-anothername") == true

@test_throws ErrorException set_props!(G, 11, Dict(:name => "gnode_3", :other_name => "something11"))
@test_throws ErrorException set_props!(dG,11, Dict(:name => "dgnode_3", :other_name => "something11"))
@test_throws ErrorException set_props!(dG, 11, Dict(:name => "dgnode_3", :other_name => "something11"))
@test_throws KeyError get_prop(G, 11, :other_name)
@test_throws KeyError get_prop(dG, 11, :other_name)

Expand Down

0 comments on commit 0cf7d6a

Please sign in to comment.