Skip to content
4 changes: 3 additions & 1 deletion src/SimpleValueGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const LG = LightGraphs

import LightGraphs:
nv, ne, is_directed,
eltype, vertices, add_vertex!, has_vertex,
eltype, vertices, add_vertex!, rem_vertex!, has_vertex,
edgetype, edges, src, dst, reverse,
weights,
add_edge!, rem_edge!, has_edge,
Expand Down Expand Up @@ -88,6 +88,7 @@ export
has_edge,

add_vertex!,
rem_vertex!,
has_vertex,

inneighbors,
Expand Down Expand Up @@ -123,6 +124,7 @@ include("AbstractTuples.jl")
include("utils.jl")

include("abstractvaluegraph.jl")
include("graphwrappers.jl")
include("valueedge.jl")
include("valuegraph.jl")
include("matrices.jl")
Expand Down
242 changes: 242 additions & 0 deletions src/graphwrappers.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@

"""
wrapped_graph(g::MyGraphWrapper)

Method to implement for creating graph wrapper types.
Should return the graph that is wrapped inside `g`.

### See also
[`wrapped_graph_type`](@ref), [`@wrap_graph!`](@ref)
"""
function wrapped_graph(::AbstractValGraph) end

"""
wrapped_graph_type{::Type{<:MyGraphWrapper}}

Method to implement for creating graph wrapper types.
Should return the type of the graph that is wrapped inside
a graph wrapper.

### See also
[`wrapped_graph`](@ref), [`@wrap_graph!`](@ref)

"""
function wrapped_graph_type(::Type) end

_DEFAULT_INCLUDE = [
:nv,
:is_directed,
:has_edge,
:get_graphval,
:get_vertexval,
:get_edgeval,
:set_graphval!,
:set_vertexval!,
:set_edgeval!,
:add_vertex!,
:rem_vertex!,
:add_edge!,
:rem_edge!,
]

_EXTRA_INCLUDE = [
:ne,
:outneighbors,
:inneighbors,
:outedgevals,
:inedgevals,
:edges
]

"""
@wrap_graph! MyGraphWrapper include=[] exclude=[]

Generate default method implementations for graph wrappers.
These default implementations simply call the wrapped graph. For that, one
needs to implement `wrapped_graph` and `wrapped_graph_type` for the custom graph wrapper.

Methods given as a `Vector` to the keyword argument `exclude` are not created.
By default, methods for the following functions are generated:
$(join(map(s -> '`' * string(s) * '`', _DEFAULT_INCLUDE), ", ")).

There are additional methods that can be generated but are not generated by default, as
these methods all have a default implementation in terms of other functions. To generate
these additional methods, specify them in a `Vector` to the keyword argument `include`.
The following methods can be additionally generated:
$(join(map(s -> '`' * string(s) * '`', _EXTRA_INCLUDE), ", ")).

Note: To completely implement the `SimpleValueGraphs` interface for some graph one
would also have to implement `zero`. This macro currently cannot do that, as this
would knowledge of how to construct a new graph wrapper. Therefore one has to implement this
method separately.

# Examples
```julia
# generate methods for ne, that are not generated by default and
# do not generate methods for get_graphval and set_graphval! that would
# be generated by default
@wrap_graph! MyGraphWrapper include=[ne] exclude=[get_graphval, set_graphval!]
```
### See also
[`wrapped_graph`](@ref), [`wrapped_graph_type`](@ref)
"""
macro wrap_graph!(GT, args...)

functions_to_generate = copy(_DEFAULT_INCLUDE)

# TODO maybe we can somehow assert that GT is something like a type
# for now we just use an `typeintersect`. If GT is not an `AbstractValGraph`
# this will simply result in a `Union{}` type
GT = :(typeintersect(SimpleValueGraphs.AbstractValGraph, $GT))

for arg in args
if !(arg isa Expr) ||
arg.head != :(=) ||
length(arg.args) != 2 ||
arg.args[1] ∉ (:include, :exclude) ||
!(arg.args[2] isa Expr) ||
arg.args[2].head != :vect

error("Argument must be of the form `include=[function names...]` or exclude=[function names...]`")
end
# TODO we could also fail if there are unknown functions specified in one of the lists
if arg.args[1] == :include
include_list = intersect(arg.args[2].args, _EXTRA_INCLUDE)
union!(functions_to_generate, include_list)
else
exclude_list = arg.args[2].args
setdiff!(functions_to_generate, exclude_list)
end

end

return Expr(:block, map(f -> _generate_wrapped_function!(Val(f), esc(GT)), functions_to_generate)...)
end

function _generate_wrapped_function!(::Val{:nv}, GT)

return :(SimpleValueGraphs.nv(g::$GT) = nv(wrapped_graph(g)))
end

function _generate_wrapped_function!(::Val{:is_directed}, GT)

return :(SimpleValueGraphs.is_directed(G::Type{<:$GT}) = is_directed(wrapped_graph_type(G)))
end

function _generate_wrapped_function!(::Val{:has_edge}, GT)

return :(SimpleValueGraphs.has_edge(g::$GT, s::Integer, d::Integer) = has_edge(wrapped_graph(g), s, d))
end

function _generate_wrapped_function!(::Val{:add_vertex!}, GT)

return :(SimpleValueGraphs.add_vertex!(g::$GT, vals) = add_vertex!(wrapped_graph(g), vals))
end

function _generate_wrapped_function!(::Val{:rem_vertex!}, GT)

return :(SimpleValueGraphs.rem_vertex!(g::$GT, v::Integer) = rem_vertex!(wrapped_graph(g), v))
end

function _generate_wrapped_function!(::Val{:add_edge!}, GT)

return :(SimpleValueGraphs.add_edge!(g::$GT, s::Integer, d::Integer, vals) = add_edge!(wrapped_graph(g), s, d, vals))
end

function _generate_wrapped_function!(::Val{:rem_edge!}, GT)

return :(SimpleValueGraphs.rem_edge!(g::$GT, s::Integer, d::Integer) = rem_edge!(wrapped_graph(g), s, d))
end

function _generate_wrapped_function!(::Val{:get_vertexval}, GT)

return quote
SimpleValueGraphs.get_vertexval(g::$GT, v::Integer, key::Integer) = get_vertexval(wrapped_graph(g), v, key)
SimpleValueGraphs.get_vertexval(g::$GT, v::Integer, key::Symbol) = get_vertexval(wrapped_graph(g), v, key)
SimpleValueGraphs.get_vertexval(g::$GT, v::Integer, ::Colon) = get_vertexval(wrapped_graph(g), v, :)
end
end

function _generate_wrapped_function!(::Val{:set_vertexval!}, GT)

return quote
SimpleValueGraphs.set_vertexval!(g::$GT, v::Integer, key::Integer, value) = set_vertexval!(wrapped_graph(g), v, key, value)
SimpleValueGraphs.set_vertexval!(g::$GT, v::Integer, key::Symbol, value) = set_vertexval!(wrapped_graph(g), v, key, value)
SimpleValueGraphs.set_vertexval!(g::$GT, v::Integer, ::Colon, values) = set_vertexval!(wrapped_graph(g), v, :, values)
end
end

function _generate_wrapped_function!(::Val{:get_edgeval}, GT)

return quote
SimpleValueGraphs.get_edgeval(g::$GT, s::Integer, d::Integer, key::Integer) = get_edgeval(wrapped_graph(g), s, d, key)
SimpleValueGraphs.get_edgeval(g::$GT, s::Integer, d::Integer, key::Symbol) = get_edgeval(wrapped_graph(g), s, d, key)
SimpleValueGraphs.get_edgeval(g::$GT, s::Integer, d::Integer, ::Colon) = get_edgeval(wrapped_graph(g), s, d, :)
end
end

function _generate_wrapped_function!(::Val{:set_edgeval!}, GT)

return quote
SimpleValueGraphs.set_edgeval!(g::$GT, s::Integer, d::Integer, key::Integer, value) = set_edgeval!(wrapped_graph(g), s, d, key, value)
SimpleValueGraphs.set_edgeval!(g::$GT, s::Integer, d::Integer, key::Symbol, value) = set_edgeval!(wrapped_graph(g), s, d, key, value)
SimpleValueGraphs.set_edgeval!(g::$GT, s::Integer, d::Integer, ::Colon, values) = set_edgeval!(wrapped_graph(g), s, d, :, values)
end
end

function _generate_wrapped_function!(::Val{:get_graphval}, GT)

return quote
SimpleValueGraphs.get_graphval(g::$GT, key::Integer) = get_graphval(wrapped_graph(g), key)
SimpleValueGraphs.get_graphval(g::$GT, key::Symbol) = get_graphval(wrapped_graph(g), key)
SimpleValueGraphs.get_graphval(g::$GT, ::Colon) = get_graphval(wrapped_graph(g), :)
end
end

function _generate_wrapped_function!(::Val{:set_graphval!}, GT)

return quote
SimpleValueGraphs.set_graphval!(g::$GT, key::Integer, value) = set_graphval!(wrapped_graph(g), key, value)
SimpleValueGraphs.set_graphval!(g::$GT, key::Symbol, value) = set_graphval!(wrapped_graph(g), key, value)
SimpleValueGraphs.set_graphval!(g::$GT, ::Colon, values) = set_graphval!(wrapped_graph(g), :, values)
end
end

function _generate_wrapped_function!(::Val{:ne}, GT)

return :(SimpleValueGraphs.ne(g::$GT) = ne(wrapped_graph(g)))
end

function _generate_wrapped_function!(::Val{:outneighbors}, GT)

return :(SimpleValueGraphs.outneighbors(g::$GT, u) = outneighbors(wrapped_graph(g), u))
end

function _generate_wrapped_function!(::Val{:inneighbors}, GT)

return :(SimpleValueGraphs.inneighbors(g::$GT, v) = inneighbors(wrapped_graph(g), v))
end

function _generate_wrapped_function!(::Val{:outedgevals}, GT)

return quote
SimpleValueGraphs.outedgevals(g::$GT, u, key::Integer) = outedgevals(wrapped_graph(g), u, key)
SimpleValueGraphs.outedgevals(g::$GT, u, key::Symbol) = outedgevals(wrapped_graph(g), u, key)
SimpleValueGraphs.outedgevals(g::$GT, u, ::Colon) = outedgevals(wrapped_graph(g), u, :)
end
end

function _generate_wrapped_function!(::Val{:inedgevals}, GT)

return quote
SimpleValueGraphs.inedgevals(g::$GT, v, key::Integer) = inedgevals(wrapped_graph(g), v, key)
SimpleValueGraphs.inedgevals(g::$GT, v, key::Symbol) = inedgevals(wrapped_graph(g), v, key)
SimpleValueGraphs.inedgevals(g::$GT, v, ::Colon) = inedgevals(wrapped_graph(g), v, :)
end
end

function _generate_wrapped_function!(::Val{:edges}, GT)

return :(SimpleValueGraphs.edges(g::$GT, key=nothing) = edges(wrapped_graph(g), key))
end

43 changes: 17 additions & 26 deletions src/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,11 @@ end
Lazy wrapper for value graphs that reverses the directions of the graphs edges.
If this object is mutated, the wrapped graph is mutated as well.
"""
struct Reverse{V, V_VALS, E_VALS, G_VALS, G<:AbstractValGraph{V, V_VALS, E_VALS, G_VALS}} <: AbstractValGraph{V, V_VALS, E_VALS, G_VALS}
struct Reverse{V <: Integer, V_VALS, E_VALS, G_VALS, G<:AbstractValGraph{V, V_VALS, E_VALS, G_VALS}} <: AbstractValGraph{V, V_VALS, E_VALS, G_VALS}

graph::G
end

_graphtype(::Type{<:Reverse{V, V_VALS, E_VALS, G_VALS, G}}) where {V, V_VALS, E_VALS, G_VALS, G} = G

"""
reverse(g::AbstractValGraph)

Expand All @@ -59,21 +57,15 @@ reverse(rg::Reverse) = rg.graph
## Reverse
## ---------------------------------

nv(rg::Reverse) = nv(rg.graph)

has_edge(rg::Reverse, s::Integer, d::Integer) = has_edge(rg.graph, d, s)

LG.is_directed(RG::Type{<:Reverse}) = is_directed(_graphtype(RG))

zero(RG::Type{<:Reverse}) = Reverse{_graphtype(RG)}(zero(RG))
wrapped_graph(g::Reverse) = g.graph
wrapped_graph_type(::Type{<:Reverse{V, V_VALS, E_VALS, G_VALS, G}}) where {V, V_VALS, E_VALS, G_VALS, G} = G
@wrap_graph! Reverse include=[ne] exclude=[has_edge, add_edge!, rem_edge!, get_edgeval, set_edgeval!]

get_vertexval(rg::Reverse, v::Integer, key::Integer) = get_vertexval(rg.graph, v, key)
get_vertexval(rg::Reverse, v::Integer, key::Symbol) = get_vertexval(rg.graph, v, key)
get_vertexval(rg::Reverse, v::Integer, ::Colon) = get_vertexval(rg.graph, v, :)
Base.zero(RG::Type{<:Reverse}) = Reverse(zero(wrapped_graph_type(RG)))

set_vertexval!(rg::Reverse, v::Integer, key::Integer, value) = set_vertexval!(rg.graph, v, key, value)
set_vertexval!(rg::Reverse, v::Integer, key::Symbol, value) = set_vertexval!(rg.graph, v, key, value)
set_vertexval!(rg::Reverse, v::Integer, ::Colon, values) = set_vertexval!(rg.graph, v, :, values)
has_edge(rg::Reverse, s::Integer, d::Integer) = has_edge(rg.graph, d, s)
add_edge!(rg::Reverse, s::Integer, d::Integer, values) = add_edge!(rg.graph, d, s, values)
rem_edge!(rg::Reverse, s::Integer, d::Integer) = rem_edge!(rg.graph, d, s)

get_edgeval(rg::Reverse, s::Integer, d::Integer, key::Integer) = get_edgeval(rg.graph, d, s, key)
get_edgeval(rg::Reverse, s::Integer, d::Integer, key::Symbol) = get_edgeval(rg.graph, d, s, key)
Expand All @@ -83,16 +75,15 @@ set_edgeval!(rg::Reverse, s::Integer, d::Integer, key::Integer, value) = set_edg
set_edgeval!(rg::Reverse, s::Integer, d::Integer, key::Symbol, value) = set_edgeval!(rg.graph, d, s, key, value)
set_edgeval!(rg::Reverse, s::Integer, d::Integer, ::Colon, values) = set_edgeval!(rg.graph, d, s, :, values)

get_graphval(rg::Reverse, key::Integer) = get_graphval(rg.graph, key)
get_graphval(rg::Reverse, key::Symbol) = get_graphval(rg.graph, key)
get_graphval(rg::Reverse, ::Colon) = get_graphval(rg.graph, :)
outneighbors(rg::Reverse, u) = inneighbors(wrapped_graph(rg), u)
inneighbors(rg::Reverse, v) = outneighbors(wrapped_graph(rg), v)

set_graphval!(rg::Reverse, key::Integer, value) = set_graphval!(rg.graph, key, value)
set_graphval!(rg::Reverse, key::Symbol, value) = set_graphval!(rg.graph, key, value)
set_graphval!(rg::Reverse, ::Colon, values) = set_graphval!(rg.graph, :, values)
outedgevals(rg::Reverse, u, key::Integer) = inedgevals(wrapped_graph(rg), u, key)
outedgevals(rg::Reverse, u, key::Symbol) = inedgevals(wrapped_graph(rg), u, key)
outedgevals(rg::Reverse, u, ::Colon) = inedgevals(wrapped_graph(rg), u, :)

add_vertex!(rg::Reverse, values) = add_vertex!(rg.graph, values)

add_edge!(rg::Reverse, s::Integer, d::Integer, values) = add_edge!(rg.graph, d, s, values)
inedgevals(rg::Reverse, v, key::Integer) = outedgevals(wrapped_graph(rg), v, key)
inedgevals(rg::Reverse, v, key::Symbol) = outedgevals(wrapped_graph(rg), v, key)
inedgevals(rg::Reverse, v, ::Colon) = outedgevals(wrapped_graph(rg), v, :)

ne(rg::Reverse) = ne(rg.graph)
# TODO implementing edges efficiently and with correct edge type
Loading