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

First shot at the interface #9

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ uuid = "ad2ac648-372e-45be-9d57-a550431b71c3"
authors = ["JuliaGraphs contributors"]
version = "0.1.0-DEV"

[deps]
SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
SimpleTraits = "0.9"
julia = "1.6"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using GraphsBase
using Graphs
using Documenter

DocMeta.setdocmeta!(GraphsBase, :DocTestSetup, :(using GraphsBase); recursive=true)
Expand Down
77 changes: 75 additions & 2 deletions src/GraphsBase.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,78 @@
module GraphsBase

# Write your package code here.
using SimpleTraits

end

# import Base: adjoint, write, ==, <, *, ≈, convert, isless, issubset,
# reverse, reverse!, isassigned, getindex, setindex!, show,
# print, copy, in, sum, size, eltype, length, ndims, transpose,
# iterate, eltype, get, Pair, Tuple, zero

export
# Interface
AbstractVertex, is_vertex, AbstractEdge, AbstractEdgeIter,
AbstractGraph, vertices, edges, edgetype, nv, ne, src, dst,
is_directed, IsDirected, is_range_based, IsRangeBased, is_simply_mutable, IsSimplyMutable,
is_mutable, IsMutable, is_weight_mutable, IsWeightMutable, is_vertex_stable, IsVertexStable,
has_vertex, has_edge, inneighbors, outneighbors, outedges, inedges,
weight, get_vertex_container, get_edge_container,
Edge, Graph, SimpleGraph, SimpleGraphFromIterator, DiGraph, SimpleDiGraphFromIterator,
SimpleDiGraph,

# core
is_ordered, add_vertices!, indegree, outdegree, degree,
neighbors, all_neighbors, has_self_loops, weights,

# simplegraphs
add_edge!, add_vertex!, add_vertices!, rem_edge!, rem_vertex!, rem_vertices!

"""
GraphsBase

The API for the Graphs ecosystem.

Simple graphs (not multi- or hypergraphs) are represented in a memory- and
time-efficient manner with adjacency lists and edge sets. Both directed and
undirected graphs are supported via separate types, and conversion is available
from directed to undirected.

The project goal is to mirror the functionality of robust network and graph
analysis libraries such as NetworkX while being simpler to use and more
efficient than existing Julian graph libraries such as Graphs.jl. It is an
explicit design decision that any data not required for graph manipulation
(attributes and other information, for example) is expected to be stored
outside of the graph structure itself. Such data lends itself to storage in
more traditional and better-optimized mechanisms.

[Full documentation](http://codecov.io/github/JuliaGraphs/Graphs.jl) is available,
and tutorials are available at the
[JuliaGraphsTutorials repository](https://github.com/JuliaGraphs/JuliaGraphsTutorials).
"""
GraphsBase
include("interface.jl")
include("utils.jl")
include("core.jl")
include("SimpleGraphs/SimpleGraphs.jl")

using .SimpleGraphs
"""
Graph

A datastruture representing an undirected graph.
"""
const Graph = GraphsBase.SimpleGraphs.SimpleGraph
"""
DiGraph

A datastruture representing a directed graph.
"""
const DiGraph = GraphsBase.SimpleGraphs.SimpleDiGraph
"""
Edge

A datastruture representing an edge between two vertices in
a `Graph` or `DiGraph`.
"""
const Edge = GraphsBase.SimpleGraphs.SimpleEdge

end # module
216 changes: 216 additions & 0 deletions src/SimpleGraphs/SimpleGraphs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
module SimpleGraphs

using SparseArrays
# using LinearAlgebra
using GraphsBase
using SimpleTraits

import Base:
eltype, show, ==, Pair, Tuple, copy, length, issubset, reverse, zero, in, iterate

import GraphsBase:
_NI, AbstractGraph, AbstractEdge, AbstractEdgeIter,
src, dst, edgetype, nv, ne, vertices, edges, outedges, inedges, is_directed,
is_simply_mutable, is_range_based,
has_vertex, has_edge, inneighbors, outneighbors, all_neighbors,
get_vertex_container, get_edge_container,
deepcopy_adjlist, indegree, outdegree, degree, has_self_loops,
insorted

export AbstractSimpleGraph, AbstractSimpleEdge,
SimpleEdge, SimpleGraph, SimpleGraphFromIterator, SimpleGraphEdge,
SimpleDiGraph, SimpleDiGraphFromIterator, SimpleDiGraphEdge,
add_vertex!, add_edge!, rem_vertex!, rem_vertices!, rem_edge!

abstract type AbstractSimpleEdge{T<:Integer} <: AbstractEdge{T, Int} end

"""
AbstractSimpleGraph

An abstract type representing a simple graph structure.
`AbstractSimpleGraph`s must have the following elements:
- `vertices::UnitRange{Integer}`
- `fadjlist::Vector{Vector{Integer}}`
- `ne::Integer`
"""
abstract type AbstractSimpleGraph{T<:Integer} <: AbstractGraph{T, AbstractSimpleEdge{T}} end

function show(io::IO, ::MIME"text/plain", g::AbstractSimpleGraph{T}) where T
dir = is_directed(g) ? "directed" : "undirected"
print(io, "{$(nv(g)), $(ne(g))} $dir simple $T graph")
end

nv(g::AbstractSimpleGraph{T}) where T = T(length(fadj(g)))
vertices(g::AbstractSimpleGraph) = Base.OneTo(nv(g))

"""
throw_if_invalid_eltype(T)

Internal function, throw a `DomainError` if `T` is not a concrete type `Integer`.
Can be used in the constructor of AbstractSimpleGraphs,
as Julia's typesystem does not enforce concrete types, which can lead to
problems. E.g `SimpleGraph{Signed}`.
"""
function throw_if_invalid_eltype(T::Type{<:Integer})
if !isconcretetype(T)
throw(DomainError(T, "Eltype for AbstractSimpleGraph must be concrete type."))
end
end


edges(g::AbstractSimpleGraph) = SimpleEdgeIter(g)


fadj(g::AbstractSimpleGraph) = g.fadjlist
fadj(g::AbstractSimpleGraph, v::Integer) = g.fadjlist[v]


badj(x...) = _NI("badj")

# handles single-argument edge constructors such as pairs and tuples
has_edge(g::AbstractSimpleGraph, x) = has_edge(g, edgetype(g)(x))
add_edge!(g::AbstractSimpleGraph, x) = add_edge!(g, edgetype(g)(x))
@traitfn get_edges(g::AbstractSimpleGraph::IsDirected, u, v) = has_edge(g, u, v) ? [Edge(u, v)] : Edge[]
@traitfn function get_edges(g::AbstractSimpleGraph::(!IsDirected), u, v)
!has_edge(g, u, v) && return Edge[]
u < v && return [Edge(u, v)]
return [Edge(v, u)]
end

# handles two-argument edge constructors like src,dst
has_edge(g::AbstractSimpleGraph, x, y) = has_edge(g, edgetype(g)(x, y))
add_edge!(g::AbstractSimpleGraph, x, y) = add_edge!(g, edgetype(g)(x, y))

inneighbors(g::AbstractSimpleGraph, v::Integer) = badj(g, v)
outneighbors(g::AbstractSimpleGraph, v::Integer) = fadj(g, v)
outedges(g::AbstractSimpleGraph, v::Integer) = Edge.(v, outneighbors(g, v))
inedges(g::AbstractSimpleGraph, v::Integer) = Edge.(v, inneighbors(g, v))

get_vertex_container(g::AbstractSimpleGraph, K::Type) = Vector{K}(undef, nv(g))
# get_edge_container(g::AbstractGraph, K::Type) = Array{K, 2}(undef, (nv(g), nv(g))

function issubset(g::T, h::T) where T <: AbstractSimpleGraph
nv(g) <= nv(h) || return false
for u in vertices(g)
u_nbrs_g = neighbors(g, u)
len_u_nbrs_g = length(u_nbrs_g)
len_u_nbrs_g == 0 && continue
u_nbrs_h = neighbors(h, u)
p = 1
len_u_nbrs_g > length(u_nbrs_h) && return false
(u_nbrs_g[1] < u_nbrs_h[1] || u_nbrs_g[end] > u_nbrs_h[end]) && return false
@inbounds for v in u_nbrs_h
if v == u_nbrs_g[p]
p == len_u_nbrs_g && break
p += 1
end
end
p == len_u_nbrs_g || return false
end
return true
end

has_vertex(g::AbstractSimpleGraph, v::Integer) = v in vertices(g)

ne(g::AbstractSimpleGraph) = g.ne

function rem_edge!(g::AbstractSimpleGraph{T}, u::Integer, v::Integer) where T
rem_edge!(g, edgetype(g)(T(u), T(v)))
end

"""
rem_vertex!(g, v)

Remove the vertex `v` from graph `g`. Return `false` if removal fails
(e.g., if vertex is not in the graph); `true` otherwise.

### Performance
Time complexity is ``\\mathcal{O}(k^2)``, where ``k`` is the max of the degrees
of vertex ``v`` and vertex ``|V|``.

### Implementation Notes
This operation has to be performed carefully if one keeps external
data structures indexed by edges or vertices in the graph, since
internally the removal is performed swapping the vertices `v` and ``|V|``,
and removing the last vertex ``|V|`` from the graph. After removal the
vertices in `g` will be indexed by ``1:|V|-1``.

# Examples
```jldoctest
julia> using Graphs

julia> g = SimpleGraph(2);

julia> rem_vertex!(g, 2)
true

julia> rem_vertex!(g, 2)
false
```
"""
function rem_vertex!(g::AbstractSimpleGraph, v::Integer)
v in vertices(g) || return false
n = nv(g)
self_loop_n = false # true if n is self-looped (see #820)

# remove the in_edges from v
srcs = copy(inneighbors(g, v))
@inbounds for s in srcs
rem_edge!(g, edgetype(g)(s, v))
end
# remove the in_edges from the last vertex
neigs = copy(inneighbors(g, n))
@inbounds for s in neigs
rem_edge!(g, edgetype(g)(s, n))
end
if v != n
# add the edges from n back to v
@inbounds for s in neigs
if s != n # don't add an edge to the last vertex - see #820.
add_edge!(g, edgetype(g)(s, v))
else
self_loop_n = true
end
end
end

if is_directed(g)
# remove the out_edges from v
dsts = copy(outneighbors(g, v))
@inbounds for d in dsts
rem_edge!(g, edgetype(g)(v, d))
end
# remove the out_edges from the last vertex
neigs = copy(outneighbors(g, n))
@inbounds for d in neigs
rem_edge!(g, edgetype(g)(n, d))
end
if v != n
# add the out_edges back to v
@inbounds for d in neigs
if d != n
add_edge!(g, edgetype(g)(v, d))
end
end
end
end
if self_loop_n
add_edge!(g, edgetype(g)(v, v))
end
pop!(g.fadjlist)
if is_directed(g)
pop!(g.badjlist)
end
return true
end

zero(::Type{G}) where {G<:AbstractSimpleGraph} = G()

is_range_based(::Type{<:AbstractSimpleGraph}) = true

include("./simpleedge.jl")
include("./simpledigraph.jl")
include("./simplegraph.jl")
include("./simpleedgeiter.jl")

end # module
Loading