167 lines
5.4 KiB
Python
167 lines
5.4 KiB
Python
|
"""
|
||
|
Adjacency matrix and incidence matrix of graphs.
|
||
|
"""
|
||
|
import networkx as nx
|
||
|
|
||
|
__all__ = ["incidence_matrix", "adjacency_matrix"]
|
||
|
|
||
|
|
||
|
@nx._dispatch(edge_attrs="weight")
|
||
|
def incidence_matrix(
|
||
|
G, nodelist=None, edgelist=None, oriented=False, weight=None, *, dtype=None
|
||
|
):
|
||
|
"""Returns incidence matrix of G.
|
||
|
|
||
|
The incidence matrix assigns each row to a node and each column to an edge.
|
||
|
For a standard incidence matrix a 1 appears wherever a row's node is
|
||
|
incident on the column's edge. For an oriented incidence matrix each
|
||
|
edge is assigned an orientation (arbitrarily for undirected and aligning to
|
||
|
direction for directed). A -1 appears for the source (tail) of an edge and
|
||
|
1 for the destination (head) of the edge. The elements are zero otherwise.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
G : graph
|
||
|
A NetworkX graph
|
||
|
|
||
|
nodelist : list, optional (default= all nodes in G)
|
||
|
The rows are ordered according to the nodes in nodelist.
|
||
|
If nodelist is None, then the ordering is produced by G.nodes().
|
||
|
|
||
|
edgelist : list, optional (default= all edges in G)
|
||
|
The columns are ordered according to the edges in edgelist.
|
||
|
If edgelist is None, then the ordering is produced by G.edges().
|
||
|
|
||
|
oriented: bool, optional (default=False)
|
||
|
If True, matrix elements are +1 or -1 for the head or tail node
|
||
|
respectively of each edge. If False, +1 occurs at both nodes.
|
||
|
|
||
|
weight : string or None, optional (default=None)
|
||
|
The edge data key used to provide each value in the matrix.
|
||
|
If None, then each edge has weight 1. Edge weights, if used,
|
||
|
should be positive so that the orientation can provide the sign.
|
||
|
|
||
|
dtype : a NumPy dtype or None (default=None)
|
||
|
The dtype of the output sparse array. This type should be a compatible
|
||
|
type of the weight argument, eg. if weight would return a float this
|
||
|
argument should also be a float.
|
||
|
If None, then the default for SciPy is used.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
A : SciPy sparse array
|
||
|
The incidence matrix of G.
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
For MultiGraph/MultiDiGraph, the edges in edgelist should be
|
||
|
(u,v,key) 3-tuples.
|
||
|
|
||
|
"Networks are the best discrete model for so many problems in
|
||
|
applied mathematics" [1]_.
|
||
|
|
||
|
References
|
||
|
----------
|
||
|
.. [1] Gil Strang, Network applications: A = incidence matrix,
|
||
|
http://videolectures.net/mit18085f07_strang_lec03/
|
||
|
"""
|
||
|
import scipy as sp
|
||
|
|
||
|
if nodelist is None:
|
||
|
nodelist = list(G)
|
||
|
if edgelist is None:
|
||
|
if G.is_multigraph():
|
||
|
edgelist = list(G.edges(keys=True))
|
||
|
else:
|
||
|
edgelist = list(G.edges())
|
||
|
A = sp.sparse.lil_array((len(nodelist), len(edgelist)), dtype=dtype)
|
||
|
node_index = {node: i for i, node in enumerate(nodelist)}
|
||
|
for ei, e in enumerate(edgelist):
|
||
|
(u, v) = e[:2]
|
||
|
if u == v:
|
||
|
continue # self loops give zero column
|
||
|
try:
|
||
|
ui = node_index[u]
|
||
|
vi = node_index[v]
|
||
|
except KeyError as err:
|
||
|
raise nx.NetworkXError(
|
||
|
f"node {u} or {v} in edgelist but not in nodelist"
|
||
|
) from err
|
||
|
if weight is None:
|
||
|
wt = 1
|
||
|
else:
|
||
|
if G.is_multigraph():
|
||
|
ekey = e[2]
|
||
|
wt = G[u][v][ekey].get(weight, 1)
|
||
|
else:
|
||
|
wt = G[u][v].get(weight, 1)
|
||
|
if oriented:
|
||
|
A[ui, ei] = -wt
|
||
|
A[vi, ei] = wt
|
||
|
else:
|
||
|
A[ui, ei] = wt
|
||
|
A[vi, ei] = wt
|
||
|
return A.asformat("csc")
|
||
|
|
||
|
|
||
|
@nx._dispatch(edge_attrs="weight")
|
||
|
def adjacency_matrix(G, nodelist=None, dtype=None, weight="weight"):
|
||
|
"""Returns adjacency matrix of G.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
G : graph
|
||
|
A NetworkX graph
|
||
|
|
||
|
nodelist : list, optional
|
||
|
The rows and columns are ordered according to the nodes in nodelist.
|
||
|
If nodelist is None, then the ordering is produced by G.nodes().
|
||
|
|
||
|
dtype : NumPy data-type, optional
|
||
|
The desired data-type for the array.
|
||
|
If None, then the NumPy default is used.
|
||
|
|
||
|
weight : string or None, optional (default='weight')
|
||
|
The edge data key used to provide each value in the matrix.
|
||
|
If None, then each edge has weight 1.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
A : SciPy sparse array
|
||
|
Adjacency matrix representation of G.
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
For directed graphs, entry i,j corresponds to an edge from i to j.
|
||
|
|
||
|
If you want a pure Python adjacency matrix representation try
|
||
|
networkx.convert.to_dict_of_dicts which will return a
|
||
|
dictionary-of-dictionaries format that can be addressed as a
|
||
|
sparse matrix.
|
||
|
|
||
|
For MultiGraph/MultiDiGraph with parallel edges the weights are summed.
|
||
|
See `to_numpy_array` for other options.
|
||
|
|
||
|
The convention used for self-loop edges in graphs is to assign the
|
||
|
diagonal matrix entry value to the edge weight attribute
|
||
|
(or the number 1 if the edge has no weight attribute). If the
|
||
|
alternate convention of doubling the edge weight is desired the
|
||
|
resulting SciPy sparse array can be modified as follows:
|
||
|
|
||
|
>>> G = nx.Graph([(1, 1)])
|
||
|
>>> A = nx.adjacency_matrix(G)
|
||
|
>>> print(A.todense())
|
||
|
[[1]]
|
||
|
>>> A.setdiag(A.diagonal() * 2)
|
||
|
>>> print(A.todense())
|
||
|
[[2]]
|
||
|
|
||
|
See Also
|
||
|
--------
|
||
|
to_numpy_array
|
||
|
to_scipy_sparse_array
|
||
|
to_dict_of_dicts
|
||
|
adjacency_spectrum
|
||
|
"""
|
||
|
return nx.to_scipy_sparse_array(G, nodelist=nodelist, dtype=dtype, weight=weight)
|