Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added default option to get_prop #41

Merged
merged 3 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,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"
gdalle marked this conversation as resolved.
Show resolved Hide resolved

# 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
Loading