11module NetworkX
22
33using Graphs
4- using PythonCall: Py, pybuiltins, pyconvert, pyimport
4+ using PythonCall: Py, pynew, pycopy!, pybuiltins, pyconvert, pyimport
55
66export AbstractNetworkXGraph,
77 NetworkXGraph,
88 NetworkXDiGraph,
99 networkx_graph,
10- wrap_networkx,
1110 refresh_index!
1211
12+ """
13+ NetworkX.PythonNetworkX
14+
15+ Sub-module providing direct access to the Python `networkx` package.
16+ Use this namespace when you need raw Python networkx objects or algorithms
17+ that are not yet wrapped by the Julia API.
18+
19+ # Example
20+ ```julia
21+ using NetworkX
22+ nx = NetworkX.PythonNetworkX.networkx
23+ pyg = nx.complete_graph(5)
24+ ```
25+ """
26+ module PythonNetworkX
27+ using PythonCall: pynew, pycopy!, pyimport
28+
29+ """ The raw Python `networkx` module."""
30+ const networkx = pynew ()
31+
32+ function __init__ ()
33+ pycopy! (networkx, pyimport (" networkx" ))
34+ end
35+ end # module PythonNetworkX
36+
1337"""
1438 AbstractNetworkXGraph{T} <: Graphs.AbstractGraph{T}
1539
@@ -19,8 +43,17 @@ abstract type AbstractNetworkXGraph{T<:Integer} <: Graphs.AbstractGraph{T} end
1943
2044"""
2145 NetworkXGraph{T}(pygraph)
46+ NetworkXGraph(pygraph)
2247
23- Wrapper for an undirected NetworkX graph.
48+ Wrap an undirected Python `networkx.Graph` as a `Graphs.AbstractGraph`.
49+
50+ # Example
51+ ```julia
52+ using NetworkX
53+ nx = NetworkX.PythonNetworkX.networkx
54+ pyg = nx.path_graph(5)
55+ gw = NetworkXGraph(pyg)
56+ ```
2457"""
2558mutable struct NetworkXGraph{T<: Integer } <: AbstractNetworkXGraph{T}
2659 pygraph:: Py
3063
3164"""
3265 NetworkXDiGraph{T}(pygraph)
33-
34- Wrapper for a directed NetworkX graph.
66+ NetworkXDiGraph(pygraph)
67+
68+ Wrap a directed Python `networkx.DiGraph` as a `Graphs.AbstractGraph`.
69+
70+ # Example
71+ ```julia
72+ using NetworkX
73+ nx = NetworkX.PythonNetworkX.networkx
74+ pyg = nx.DiGraph()
75+ pyg.add_edges_from([(1, 2), (2, 3)])
76+ gw = NetworkXDiGraph(pyg)
77+ ```
3578"""
3679mutable struct NetworkXDiGraph{T<: Integer } <: AbstractNetworkXGraph{T}
3780 pygraph:: Py
3881 nodes:: Vector{Any}
3982 node_to_index:: Dict{Any,T}
4083end
4184
42- const _NX = Ref {Py} ()
43-
44- _nx () = isassigned (_NX) ? _NX[] : (_NX[] = pyimport (" networkx" ))
45- _pylist (x) = pybuiltins. list (x)
46- _nodes (pygraph:: Py ) = pyconvert (Vector{Any}, _pylist (pygraph. nodes ()))
47- _edges (pygraph:: Py ) = pyconvert (Vector{Tuple{Any,Any}}, _pylist (pygraph. edges ()))
48- _neighbors (pyiter) = pyconvert (Vector{Any}, _pylist (pyiter))
49-
5085function _node_to_index (nodes:: Vector{Any} , :: Type{T} ) where {T<: Integer }
5186 mapping = Dict {Any,T} ()
5287 for (i, node) in enumerate (nodes)
@@ -56,9 +91,8 @@ function _node_to_index(nodes::Vector{Any}, ::Type{T}) where {T<:Integer}
5691end
5792
5893function refresh_index! (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
59- nodes = _nodes (g. pygraph)
60- g. nodes = nodes
61- g. node_to_index = _node_to_index (nodes, T)
94+ g. nodes = pyconvert (Vector{Any}, pybuiltins. list (g. pygraph. nodes ()))
95+ g. node_to_index = _node_to_index (g. nodes, T)
6296 return g
6397end
6498
85119
86120NetworkXDiGraph (pygraph:: Py ) = NetworkXDiGraph {Int} (pygraph)
87121
88- """
89- wrap_networkx(pygraph; T=Int)
90-
91- Wrap a NetworkX Python graph object as a `Graphs.AbstractGraph` implementation.
92- """
93- function wrap_networkx (pygraph:: Py ; T:: Type{<:Integer} = Int)
94- return pyconvert (Bool, pygraph. is_directed ()) ? NetworkXDiGraph {T} (pygraph) :
95- NetworkXGraph {T} (pygraph)
96- end
97-
98122"""
99123 networkx_graph(g)
100124
101125Convert a `Graphs.AbstractGraph` to a Python NetworkX graph object.
126+ Returns the underlying Python object for `AbstractNetworkXGraph` wrappers,
127+ or creates a new Python networkx graph for any other `Graphs.AbstractGraph`.
102128"""
103129networkx_graph (g:: AbstractNetworkXGraph ) = g. pygraph
104130
105131function networkx_graph (g:: Graphs.AbstractGraph )
106- nx = _nx ()
132+ nx = PythonNetworkX . networkx
107133 pyg = Graphs. is_directed (g) ? nx. DiGraph () : nx. Graph ()
108134 pyg. add_nodes_from (collect (Graphs. vertices (g)))
109135 pyg. add_edges_from ([(Graphs. src (e), Graphs. dst (e)) for e in Graphs. edges (g)])
110136 return pyg
111137end
112138
113- wrap_networkx (g:: Graphs.AbstractGraph{T} ) where {T<: Integer } =
114- wrap_networkx (networkx_graph (g); T= T)
115-
116139Graphs. is_directed (:: Type{<:NetworkXGraph} ) = false
117140Graphs. is_directed (:: NetworkXGraph ) = false
118141Graphs. is_directed (:: Type{<:NetworkXDiGraph} ) = true
@@ -127,9 +150,6 @@ Graphs.eltype(::Type{G}) where {T<:Integer,G<:AbstractNetworkXGraph{T}} = T
127150Graphs. eltype (:: AbstractNetworkXGraph{T} ) where {T<: Integer } = T
128151
129152_node (g:: AbstractNetworkXGraph , v:: Integer ) = g. nodes[Int (v)]
130- _label_to_vertex (g:: AbstractNetworkXGraph{T} , label) where {T<: Integer } =
131- g. node_to_index[label]:: T
132- _label_to_vertex (g:: Graphs.AbstractGraph , label) = label
133153
134154function Graphs. has_edge (g:: AbstractNetworkXGraph , s, d)
135155 Graphs. has_vertex (g, s) || return false
@@ -138,7 +158,7 @@ function Graphs.has_edge(g::AbstractNetworkXGraph, s, d)
138158end
139159
140160function _mapped_neighbors (g:: AbstractNetworkXGraph{T} , pyiter) where {T<: Integer }
141- py_ns = _neighbors ( pyiter)
161+ py_ns = pyconvert (Vector{Any}, pybuiltins . list ( pyiter) )
142162 return T[g. node_to_index[n] for n in py_ns]
143163end
144164
@@ -160,12 +180,14 @@ function Graphs.inneighbors(g::NetworkXDiGraph{T}, v) where {T<:Integer}
160180end
161181
162182function Graphs. edges (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
183+ py_edges = pyconvert (Vector{Tuple{Any,Any}}, pybuiltins. list (g. pygraph. edges ()))
163184 return Graphs. Edge{T}[
164- Graphs. Edge {T} (g. node_to_index[u], g. node_to_index[v]) for (u, v) in _edges (g . pygraph)
185+ Graphs. Edge {T} (g. node_to_index[u], g. node_to_index[v]) for (u, v) in py_edges
165186 ]
166187end
167188
168- Graphs. has_self_loops (g:: AbstractNetworkXGraph ) = pyconvert (Int, _nx (). number_of_selfloops (g. pygraph)) > 0
189+ Graphs. has_self_loops (g:: AbstractNetworkXGraph ) =
190+ pyconvert (Int, PythonNetworkX. networkx. number_of_selfloops (g. pygraph)) > 0
169191
170192function Graphs. add_vertex! (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
171193 new_index = T (Graphs. nv (g) + 1 )
@@ -234,25 +256,19 @@ function Graphs.rem_vertices!(g::AbstractNetworkXGraph{T}, vs; keep_order::Bool=
234256end
235257
236258function Graphs. squash (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
237- copyg = wrap_networkx (g . pygraph. copy (); T = Int )
259+ copyg = typeof (g)(g . pygraph. copy ())
238260 copyg. nodes = copy (g. nodes)
239261 _refresh_index_from_nodes! (copyg)
240262 return copyg, collect (Graphs. vertices (g))
241263end
242264
243- Graphs. zero (:: Type{<:NetworkXGraph{T}} ) where {T<: Integer } = wrap_networkx (_nx (). Graph (); T= T)
265+ Graphs. zero (:: Type{<:NetworkXGraph{T}} ) where {T<: Integer } =
266+ NetworkXGraph {T} (PythonNetworkX. networkx. Graph ())
244267Graphs. zero (:: Type{<:NetworkXDiGraph{T}} ) where {T<: Integer } =
245- wrap_networkx (_nx (). DiGraph (); T= T)
246-
247- function Base. copy (g:: NetworkXGraph{T} ) where {T<: Integer }
248- copyg = NetworkXGraph {T} (g. pygraph. copy ())
249- copyg. nodes = copy (g. nodes)
250- copyg. node_to_index = copy (g. node_to_index)
251- return copyg
252- end
268+ NetworkXDiGraph {T} (PythonNetworkX. networkx. DiGraph ())
253269
254- function Base. copy (g:: NetworkXDiGraph {T} ) where {T<: Integer }
255- copyg = NetworkXDiGraph {T} (g. pygraph. copy ())
270+ function Base. copy (g:: AbstractNetworkXGraph {T} ) where {T<: Integer }
271+ copyg = typeof (g) (g. pygraph. copy ())
256272 copyg. nodes = copy (g. nodes)
257273 copyg. node_to_index = copy (g. node_to_index)
258274 return copyg
0 commit comments