Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Graphs belong to the class `UGraph` or `DGraph` for undirected or directed graph

***
- `g.adjacency()` adjacency matrix
- `g.Laplacian()` Laplacian matrix
- `g.laplacian()` Laplacian matrix
- `g.incidence()` incidence matrix

### Properties and methods of a vertex
Expand Down
2 changes: 1 addition & 1 deletion docs/source/matrices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ Convert graph to matrix form
These methods convert a graph to a matrix representation .

.. autoclass:: pgraph.PGraph._BaseGraph
:members: distance, adjacency, degree, incidence, Laplacian
:members: distance, adjacency, degree, incidence, laplacian
32 changes: 25 additions & 7 deletions src/pgraph/PGraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -1966,14 +1966,14 @@ def average_degree(self) -> float:

# MATRIX REPRESENTATIONS

def Laplacian(self) -> NDArray:
def laplacian(self) -> NDArray:
"""
Laplacian matrix for the graph

:return: Laplacian matrix
:rtype: NumPy ndarray

``g.Laplacian()`` is the Laplacian matrix (NxN) of the graph where N
``g.laplacian()`` is the Laplacian matrix (NxN) of the graph where N
is the number of vertices.

.. runblock:: pycon
Expand All @@ -1987,7 +1987,7 @@ def Laplacian(self) -> NDArray:
>>> for i, j in [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (3, 4)]:
... g.add_edge(g[i], g[j])
...
>>> L = g.Laplacian()
>>> L = g.laplacian()
>>> print(L)

.. note::
Expand Down Expand Up @@ -2023,6 +2023,24 @@ def Laplacian(self) -> NDArray:
"""
return np.diag([v.out_degree for v in self]) - self.adjacency()

def Laplacian(self) -> NDArray:
"""
Laplacian matrix for the graph (deprecated)

.. deprecated:: 1.0
Use :meth:`laplacian` instead. A capitalized name denotes a class
method that constructs a graph, like :meth:`Adjacency` and
:meth:`Dict`; this is an instance method that returns a matrix.

:seealso: :meth:`laplacian`
"""
warnings.warn(
"Laplacian() is deprecated, use laplacian() instead",
DeprecationWarning,
stacklevel=2,
)
return self.laplacian()

def connectivity(self, vertices: Iterable[BaseVertex] | None = None) -> list[int]:
"""
Graph connectivity
Expand Down Expand Up @@ -2094,7 +2112,7 @@ def degree(self) -> NDArray:
For a ``DGraph`` this was the out-degree matrix, counting only the
edges leaving each vertex. It is now the total, in plus out. The
old matrix is ``np.diag([v.out_degree for v in g])``, which is what
:meth:`Laplacian` uses.
:meth:`laplacian` uses.

.. runblock:: pycon

Expand All @@ -2116,7 +2134,7 @@ def degree(self) -> NDArray:
add ``k`` to the degree of each end. A self-loop adds 2, for both
kinds of graph, since it has two ends.

:seealso: :meth:`adjacency` :meth:`incidence` :meth:`Laplacian`
:seealso: :meth:`adjacency` :meth:`incidence` :meth:`laplacian`
:meth:`connectivity` :meth:`average_degree` :attr:`BaseVertex.degree`
"""

Expand Down Expand Up @@ -2166,7 +2184,7 @@ def adjacency(self) -> NDArray:
(consistent with :meth:`degree`) and 1 for a ``DGraph``. In every case
each row of ``A`` sums to the degree of that vertex.

:seealso: :meth:`Laplacian` :meth:`incidence` :meth:`degree`
:seealso: :meth:`laplacian` :meth:`incidence` :meth:`degree`
"""
# create a dict mapping vertex to an id
vdict = {}
Expand Down Expand Up @@ -2222,7 +2240,7 @@ def incidence(self) -> NDArray:
so row sums still equal :meth:`degree`); for a ``DGraph`` it holds a
single 1.

:seealso: :meth:`Laplacian` :meth:`adjacency` :meth:`degree`
:seealso: :meth:`laplacian` :meth:`adjacency` :meth:`degree`
"""
edges = self.edges()
I = np.zeros((self.n, len(edges)))
Expand Down
4 changes: 2 additions & 2 deletions tests/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ def test_matrices(self):
D = g.degree()
nt.assert_array_equal(D, np.diag([2, 1, 1, 0]))

L = g.Laplacian()
L = g.laplacian()
expected_L = np.diag([2, 1, 1, 0]) - expected_A
nt.assert_array_equal(L, expected_L)

Expand Down Expand Up @@ -902,7 +902,7 @@ def test_matrices(self):

# the directed Laplacian uses the OUT-degree, D_out - A, whose rows
# sum to zero; it is not degree() - A for a directed graph
L = g.Laplacian()
L = g.laplacian()
expected_L = np.diag([2, 1, 0]) - expected_A
nt.assert_array_equal(L, expected_L)
nt.assert_array_equal(L.sum(axis=1), np.zeros(3))
Expand Down
2 changes: 1 addition & 1 deletion tests/test_in_edges.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def test_average_degree(self):

def test_laplacian_still_has_zero_row_sums(self):
# a directed Laplacian uses the OUT-degree: D_out - A
L = self.g.Laplacian()
L = self.g.laplacian()
np.testing.assert_allclose(L.sum(axis=1), 0)
np.testing.assert_array_equal(
L, np.diag([v.out_degree for v in self.g]) - self.g.adjacency()
Expand Down
43 changes: 43 additions & 0 deletions tests/test_laplacian_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""``laplacian()`` is canonical; ``Laplacian()`` is a deprecated alias."""

import unittest
import warnings

import numpy as np

from pgraph import DGraph, UGraph


class _Name:
cls: type

def setUp(self):
self.g = self.cls()
a, b, c = (self.g.add_vertex() for _ in range(3))
self.g.add_edge(a, b)
self.g.add_edge(b, c)

def test_lowercase_does_not_warn(self):
with warnings.catch_warnings():
warnings.simplefilter("error")
L = self.g.laplacian()
self.assertEqual(L.shape, (3, 3))

def test_capitalized_warns_and_agrees(self):
with self.assertWarns(DeprecationWarning) as cm:
L = self.g.Laplacian()
self.assertIn("laplacian()", str(cm.warning))
self.assertEqual(cm.filename, __file__) # stacklevel points at caller
np.testing.assert_array_equal(L, self.g.laplacian())


class TestUGraphName(_Name, unittest.TestCase):
cls = UGraph


class TestDGraphName(_Name, unittest.TestCase):
cls = DGraph


if __name__ == "__main__":
unittest.main()
4 changes: 2 additions & 2 deletions tests/test_multigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
towards the (out-)degree of a ``DGraph`` vertex
- ``adjacency()`` counts edges, so ``A[i, j]`` is the number of edges
from ``i`` to ``j`` and each row of ``A`` sums to the degree of the vertex
- ``Laplacian()`` is therefore ``degree() - adjacency()``, rows sum to zero
- ``laplacian()`` is therefore ``degree() - adjacency()``, rows sum to zero
- ``distance()`` and path lengths use the cheapest of any parallel edges
"""

Expand Down Expand Up @@ -118,7 +118,7 @@ def check_matrices_consistent(self):
np.testing.assert_array_equal(out, deg)

# the Laplacian is D_out - A, so each row sums to zero for any graph
L = g.Laplacian()
L = g.laplacian()
np.testing.assert_array_equal(L, np.diag(out) - A)
np.testing.assert_allclose(L.sum(axis=1), 0)
# and degrees sum to twice the edges (every edge has two ends)
Expand Down
Loading