diff --git a/README.md b/README.md index 4c07e6e..9d8ce3e 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/src/MetaGraphs.jl b/src/MetaGraphs.jl index 7eddade..06c3b3a 100644 --- a/src/MetaGraphs.jl +++ b/src/MetaGraphs.jl @@ -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) @@ -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 @@ -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 @@ -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 @@ -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) @@ -300,7 +313,7 @@ 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) @@ -308,7 +321,7 @@ function set_props!(g::AbstractMetaGraph, v::Integer, d::Dict) 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 @@ -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) @@ -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) @@ -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) @@ -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]) @@ -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) @@ -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) @@ -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 @@ -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") diff --git a/test/metagraphs.jl b/test/metagraphs.jl index a6bf948..59fc799 100644 --- a/test/metagraphs.jl +++ b/test/metagraphs.jl @@ -1,7 +1,7 @@ using MetaGraphs import Graphs: SimpleGraphs import Base64: - stringmime + stringmime @testset "MetaGraphs" begin @@ -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) @@ -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) @@ -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)] @@ -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 @@ -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 @@ -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)) @@ -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)