@@ -4,281 +4,15 @@ using Graphs
44using PythonCall: Py, pynew, pycopy!, pybuiltins, pyconvert, pyimport
55
66export AbstractNetworkXGraph,
7- NetworkXGraph,
8- NetworkXDiGraph,
9- networkx_graph,
10- refresh_index!
7+ NetworkXGraph,
8+ NetworkXDiGraph,
9+ networkx_graph,
10+ refresh_index!
1111
12- """
13- NetworkXGraphs.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 NetworkXGraphs
22- nx = NetworkXGraphs.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-
37- """
38- AbstractNetworkXGraph{T} <: Graphs.AbstractGraph{T}
39-
40- Abstract supertype for wrappers around Python NetworkX graph objects.
41- """
42- abstract type AbstractNetworkXGraph{T<: Integer } <: Graphs.AbstractGraph{T} end
43-
44- """
45- NetworkXGraph{T}(pygraph)
46- NetworkXGraph(pygraph)
47-
48- Wrap an undirected Python `networkx.Graph` as a `Graphs.AbstractGraph`.
49-
50- # Example
51- ```julia
52- using NetworkXGraphs
53- nx = NetworkXGraphs.PythonNetworkX.networkx
54- pyg = nx.path_graph(5)
55- gw = NetworkXGraph(pyg)
56- ```
57- """
58- mutable struct NetworkXGraph{T<: Integer } <: AbstractNetworkXGraph{T}
59- pygraph:: Py
60- nodes:: Vector{Any}
61- node_to_index:: Dict{Any,T}
62- end
63-
64- """
65- NetworkXDiGraph{T}(pygraph)
66- NetworkXDiGraph(pygraph)
67-
68- Wrap a directed Python `networkx.DiGraph` as a `Graphs.AbstractGraph`.
69-
70- # Example
71- ```julia
72- using NetworkXGraphs
73- nx = NetworkXGraphs.PythonNetworkX.networkx
74- pyg = nx.DiGraph()
75- pyg.add_edges_from([(1, 2), (2, 3)])
76- gw = NetworkXDiGraph(pyg)
77- ```
78- """
79- mutable struct NetworkXDiGraph{T<: Integer } <: AbstractNetworkXGraph{T}
80- pygraph:: Py
81- nodes:: Vector{Any}
82- node_to_index:: Dict{Any,T}
83- end
84-
85- function _node_to_index (nodes:: Vector{Any} , :: Type{T} ) where {T<: Integer }
86- mapping = Dict {Any,T} ()
87- for (i, node) in enumerate (nodes)
88- mapping[node] = T (i)
89- end
90- return mapping
91- end
92-
93- function refresh_index! (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
94- g. nodes = pyconvert (Vector{Any}, pybuiltins. list (g. pygraph. nodes ()))
95- g. node_to_index = _node_to_index (g. nodes, T)
96- return g
97- end
98-
99- function _refresh_index_from_nodes! (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
100- g. node_to_index = _node_to_index (g. nodes, T)
101- return g
102- end
103-
104- function NetworkXGraph {T} (pygraph:: Py ) where {T<: Integer }
105- pyconvert (Bool, pygraph. is_directed ()) &&
106- throw (ArgumentError (" Expected an undirected networkx.Graph." ))
107- g = NetworkXGraph {T} (pygraph, Any[], Dict {Any,T} ())
108- return refresh_index! (g)
109- end
110-
111- NetworkXGraph (pygraph:: Py ) = NetworkXGraph {Int} (pygraph)
112-
113- function NetworkXDiGraph {T} (pygraph:: Py ) where {T<: Integer }
114- ! pyconvert (Bool, pygraph. is_directed ()) &&
115- throw (ArgumentError (" Expected a directed networkx.DiGraph." ))
116- g = NetworkXDiGraph {T} (pygraph, Any[], Dict {Any,T} ())
117- return refresh_index! (g)
118- end
119-
120- NetworkXDiGraph (pygraph:: Py ) = NetworkXDiGraph {Int} (pygraph)
121-
122- """
123- networkx_graph(g)
124-
125- Convert 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`.
128- """
129- networkx_graph (g:: AbstractNetworkXGraph ) = g. pygraph
130-
131- function networkx_graph (g:: Graphs.AbstractGraph )
132- nx = PythonNetworkX. networkx
133- pyg = Graphs. is_directed (g) ? nx. DiGraph () : nx. Graph ()
134- pyg. add_nodes_from (collect (Graphs. vertices (g)))
135- pyg. add_edges_from ([(Graphs. src (e), Graphs. dst (e)) for e in Graphs. edges (g)])
136- return pyg
137- end
138-
139- Graphs. is_directed (:: Type{<:NetworkXGraph} ) = false
140- Graphs. is_directed (:: NetworkXGraph ) = false
141- Graphs. is_directed (:: Type{<:NetworkXDiGraph} ) = true
142- Graphs. is_directed (:: NetworkXDiGraph ) = true
143-
144- Graphs. edgetype (:: AbstractNetworkXGraph{T} ) where {T<: Integer } = Graphs. Edge{T}
145- Graphs. nv (g:: AbstractNetworkXGraph ) = length (g. nodes)
146- Graphs. ne (g:: AbstractNetworkXGraph ) = pyconvert (Int, g. pygraph. number_of_edges ())
147- Graphs. vertices (g:: AbstractNetworkXGraph{T} ) where {T<: Integer } = T .(1 : Graphs. nv (g))
148- Graphs. has_vertex (g:: AbstractNetworkXGraph , v) = 1 <= v <= Graphs. nv (g)
149- Graphs. eltype (:: Type{G} ) where {T<: Integer ,G<: AbstractNetworkXGraph{T} } = T
150- Graphs. eltype (:: AbstractNetworkXGraph{T} ) where {T<: Integer } = T
151-
152- _node (g:: AbstractNetworkXGraph , v:: Integer ) = g. nodes[Int (v)]
153-
154- function Graphs. has_edge (g:: AbstractNetworkXGraph , s, d)
155- Graphs. has_vertex (g, s) || return false
156- Graphs. has_vertex (g, d) || return false
157- return pyconvert (Bool, g. pygraph. has_edge (_node (g, s), _node (g, d)))
158- end
159-
160- function _mapped_neighbors (g:: AbstractNetworkXGraph{T} , pyiter) where {T<: Integer }
161- py_ns = pyconvert (Vector{Any}, pybuiltins. list (pyiter))
162- return T[g. node_to_index[n] for n in py_ns]
163- end
164-
165- function Graphs. outneighbors (g:: NetworkXGraph{T} , v) where {T<: Integer }
166- Graphs. has_vertex (g, v) || return T[]
167- return _mapped_neighbors (g, g. pygraph. neighbors (_node (g, v)))
168- end
169-
170- Graphs. inneighbors (g:: NetworkXGraph{T} , v) where {T<: Integer } = Graphs. outneighbors (g, v)
171-
172- function Graphs. outneighbors (g:: NetworkXDiGraph{T} , v) where {T<: Integer }
173- Graphs. has_vertex (g, v) || return T[]
174- return _mapped_neighbors (g, g. pygraph. successors (_node (g, v)))
175- end
176-
177- function Graphs. inneighbors (g:: NetworkXDiGraph{T} , v) where {T<: Integer }
178- Graphs. has_vertex (g, v) || return T[]
179- return _mapped_neighbors (g, g. pygraph. predecessors (_node (g, v)))
180- end
181-
182- function Graphs. edges (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
183- py_edges = pyconvert (Vector{Tuple{Any,Any}}, pybuiltins. list (g. pygraph. edges ()))
184- return Graphs. Edge{T}[
185- Graphs. Edge {T} (g. node_to_index[u], g. node_to_index[v]) for (u, v) in py_edges
186- ]
187- end
188-
189- Graphs. has_self_loops (g:: AbstractNetworkXGraph ) =
190- pyconvert (Int, PythonNetworkX. networkx. number_of_selfloops (g. pygraph)) > 0
191-
192- function Graphs. add_vertex! (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
193- new_index = T (Graphs. nv (g) + 1 )
194- label = new_index
195- # Find a unique label if it already exists in the Python graph
196- while pyconvert (Bool, g. pygraph. has_node (label))
197- new_index += one (T)
198- label = new_index
199- end
200- g. pygraph. add_node (label)
201- push! (g. nodes, label)
202- g. node_to_index[label] = T (length (g. nodes))
203- return true
204- end
205-
206- function Graphs. add_edge! (g:: AbstractNetworkXGraph , s, d)
207- Graphs. has_vertex (g, s) || return false
208- Graphs. has_vertex (g, d) || return false
209- Graphs. has_edge (g, s, d) && return false
210- g. pygraph. add_edge (_node (g, s), _node (g, d))
211- return true
212- end
213-
214- function Graphs. rem_edge! (g:: AbstractNetworkXGraph , s, d)
215- if Graphs. has_edge (g, s, d)
216- g. pygraph. remove_edge (_node (g, s), _node (g, d))
217- return true
218- end
219- return false
220- end
221-
222- function Graphs. rem_vertex! (g:: AbstractNetworkXGraph{T} , v) where {T<: Integer }
223- Graphs. has_vertex (g, v) || return false
224- label = _node (g, v)
225- g. pygraph. remove_node (label)
226- # O(1) removal: swap with last node and pop
227- if v != length (g. nodes)
228- last_label = g. nodes[end ]
229- g. nodes[v] = last_label
230- g. node_to_index[last_label] = T (v)
231- end
232- pop! (g. nodes)
233- delete! (g. node_to_index, label)
234- return true
235- end
236-
237- function Graphs. rem_vertices! (g:: AbstractNetworkXGraph{T} , vs; keep_order:: Bool = true ) where {T<: Integer }
238- remove_set = Set {T} (T .(collect (vs)))
239- old_vertices = collect (Graphs. vertices (g))
240- for v in old_vertices
241- if v in remove_set
242- g. pygraph. remove_node (_node (g, v))
243- end
244- end
245- g. nodes = [g. nodes[v] for v in old_vertices if ! (v in remove_set)]
246- _refresh_index_from_nodes! (g)
247- vmap = zeros (T, length (old_vertices))
248- new_index = one (T)
249- for v in old_vertices
250- if ! (v in remove_set)
251- vmap[v] = new_index
252- new_index += one (T)
253- end
254- end
255- return vmap
256- end
257-
258- function Graphs. squash (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
259- copyg = typeof (g)(g. pygraph. copy ())
260- copyg. nodes = copy (g. nodes)
261- _refresh_index_from_nodes! (copyg)
262- return copyg, collect (Graphs. vertices (g))
263- end
264-
265- Graphs. zero (:: Type{<:NetworkXGraph{T}} ) where {T<: Integer } =
266- NetworkXGraph {T} (PythonNetworkX. networkx. Graph ())
267- Graphs. zero (:: Type{<:NetworkXDiGraph{T}} ) where {T<: Integer } =
268- NetworkXDiGraph {T} (PythonNetworkX. networkx. DiGraph ())
269-
270- function Base. copy (g:: AbstractNetworkXGraph{T} ) where {T<: Integer }
271- copyg = typeof (g)(g. pygraph. copy ())
272- copyg. nodes = copy (g. nodes)
273- copyg. node_to_index = copy (g. node_to_index)
274- return copyg
275- end
276-
277- function Base. reverse (g:: NetworkXDiGraph{T} ) where {T<: Integer }
278- reversed = NetworkXDiGraph {T} (g. pygraph. reverse (copy= true ))
279- reversed. nodes = copy (g. nodes)
280- reversed. node_to_index = copy (g. node_to_index)
281- return reversed
282- end
12+ include (" python_networkx.jl" )
13+ include (" types.jl" )
14+ include (" graph_api.jl" )
15+ include (" conversions.jl" )
28316
28417end # module NetworkXGraphs
18+
0 commit comments