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

WIP: update to support NetworkX 3.3 (dev), and other maintenance #91

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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 .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -20,4 +20,4 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: pre-commit/action@v3.0.0
- uses: pre-commit/action@v3.0.1
27 changes: 14 additions & 13 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ default_language_version:
python: python3
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
@@ -33,13 +33,13 @@ repos:
- id: name-tests-test
args: ["--pytest-test-first"]
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.15
rev: v0.18
hooks:
- id: validate-pyproject
name: Validate pyproject.toml
# I don't yet trust ruff to do what autoflake does
- repo: https://github.com/PyCQA/autoflake
rev: v2.2.1
rev: v2.3.1
hooks:
- id: autoflake
args: [--in-place]
@@ -48,48 +48,49 @@ repos:
hooks:
- id: isort
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
rev: v3.16.0
hooks:
- id: pyupgrade
args: [--py310-plus]
- repo: https://github.com/MarcoGorelli/auto-walrus
rev: v0.2.2
rev: 0.3.4
hooks:
- id: auto-walrus
additional_dependencies: [tomli]
args: [--line-length, "100"]
- repo: https://github.com/psf/black
rev: 23.12.1
rev: 24.4.2
hooks:
- id: black
# - id: black-jupyter
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
rev: v0.5.0
hooks:
- id: ruff
args: [--fix-only, --show-fixes]
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
rev: 7.1.0
hooks:
- id: flake8
additional_dependencies: &flake8_dependencies
# These versions need updated manually
- flake8==6.1.0
- flake8-bugbear==23.12.2
- flake8==7.1.0
- flake8-bugbear==24.2.6
- flake8-simplify==0.21.0
- repo: https://github.com/asottile/yesqa
rev: v1.5.0
hooks:
- id: yesqa
additional_dependencies: *flake8_dependencies
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
rev: v2.3.0
hooks:
- id: codespell
types_or: [python, rst, markdown]
additional_dependencies: [tomli]
files: ^(graphblas_algorithms|docs)/
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
rev: v0.5.0
hooks:
- id: ruff
# `pyroma` may help keep our package standards up to date if best practices change.
@@ -100,6 +101,6 @@ repos:
- id: pyroma
args: [-n, "10", .]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: no-commit-to-branch # no commit directly to main
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/graphblas-algorithms)](https://pypi.python.org/pypi/graphblas-algorithms/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/python-graphblas/graphblas-algorithms/blob/main/LICENSE)
<br>
[![Tests](https://github.com/python-graphblas/graphblas-algorithms/workflows/Tests/badge.svg?branch=main)](https://github.com/python-graphblas/graphblas-algorithms/actions)
[![Tests](https://github.com/python-graphblas/graphblas-algorithms/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/python-graphblas/graphblas-algorithms/actions)
[![Coverage](https://codecov.io/gh/python-graphblas/graphblas-algorithms/branch/main/graph/badge.svg)](https://codecov.io/gh/python-graphblas/graphblas-algorithms)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7329185.svg)](https://doi.org/10.5281/zenodo.7329185)
[![Discord](https://img.shields.io/badge/Chat-Discord-blue)](https://discord.com/invite/vur45CbwMz)
@@ -91,7 +91,7 @@ T5 = nx.k_truss(G2, 5)
```

`G2` is not a `nx.Graph`, but it does have an attribute
`__networkx_plugin__ = "graphblas"`. This tells NetworkX to
`__networkx_backend__ = "graphblas"`. This tells NetworkX to
dispatch the k_truss call to graphblas-algorithms. This link
connection exists because graphblas-algorithms registers
itself as a "networkx.plugin" entry point.
2 changes: 1 addition & 1 deletion graphblas_algorithms/algorithms/_bfs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""BFS routines used by other algorithms"""
"""BFS routines used by other algorithms."""

import numpy as np
from graphblas import Matrix, Vector, binary, indexunary, replace, semiring, unary
2 changes: 1 addition & 1 deletion graphblas_algorithms/algorithms/_helpers.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ def normalize(x, how):


def is_converged(xprev, x, tol):
"""Check convergence, L1 norm: err = sum(abs(xprev - x)); err < N * tol
"""Check convergence, L1 norm: ``err = sum(abs(xprev - x)); err < N * tol``.

This modifies `xprev`.
"""
3 changes: 2 additions & 1 deletion graphblas_algorithms/algorithms/shortest_paths/weighted.py
Original file line number Diff line number Diff line change
@@ -116,13 +116,14 @@ def bellman_ford_path_length(G, source, target):


def bellman_ford_path_lengths(G, nodes=None, *, expand_output=False):
"""Extra parameter: expand_output
"""Extra parameter: expand_output.

Parameters
----------
expand_output : bool, default False
When False, the returned Matrix has one row per node in nodes.
When True, the returned Matrix has the same shape as the input Matrix.

"""
# Same algorithms as in `single_source_bellman_ford_path_length`, but with
# `Cur` as a Matrix with each row corresponding to a source node.
4 changes: 2 additions & 2 deletions graphblas_algorithms/classes/_utils.py
Original file line number Diff line number Diff line change
@@ -177,7 +177,7 @@ def matrix_to_vectornodemap(self, A):


def matrix_to_dicts(self, A, *, use_row_index=False, use_column_index=False, values_are_keys=False):
"""Convert a Matrix to a dict of dicts of the form ``{row: {col: val}}``
"""Convert a Matrix to a dict of dicts of the form ``{row: {col: val}}``.

Use ``use_row_index=True`` to return the row index as keys in the dict,
and likewise for `use_column_index=True``.
@@ -256,7 +256,7 @@ def _cacheit(self, key, func, *args, **kwargs):


def renumber_key_to_id(self, indices):
"""Create `key_to_id` for e.g. a subgraph with node ids from `indices`"""
"""Create `key_to_id` for e.g. a subgraph with node ids from `indices`."""
id_to_key = self.id_to_key
return {id_to_key[index]: i for i, index in enumerate(indices)}
# Alternative (about the same performance)
24 changes: 12 additions & 12 deletions graphblas_algorithms/classes/digraph.py
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@


def get_AT(G, mask=None):
"""A.T"""
"""``A.T``."""
A = G._A
cache = G._cache
if "AT" not in cache:
@@ -31,7 +31,7 @@ def get_AT(G, mask=None):


def get_Up(G, mask=None):
"""select.triu(A)"""
"""``select.triu(A)``."""
A = G._A
cache = G._cache
if "U+" not in cache:
@@ -50,7 +50,7 @@ def get_Up(G, mask=None):


def get_Lp(G, mask=None):
"""select.tril(A)"""
"""``select.tril(A)``."""
A = G._A
cache = G._cache
if "L+" not in cache:
@@ -69,7 +69,7 @@ def get_Lp(G, mask=None):


def get_Um(G, mask=None):
"""select.triu(A, 1)"""
"""``select.triu(A, 1)``."""
A = G._A
cache = G._cache
if "U-" not in cache:
@@ -93,7 +93,7 @@ def get_Um(G, mask=None):


def get_Lm(G, mask=None):
"""select.tril(A, -1)"""
"""``select.tril(A, -1)``."""
A = G._A
cache = G._cache
if "L-" not in cache:
@@ -117,7 +117,7 @@ def get_Lm(G, mask=None):


def get_recip_degreesp(G, mask=None):
"""pair(A & A.T).reduce_rowwise()"""
"""``pair(A & A.T).reduce_rowwise()``."""
A = G._A
cache = G._cache
AT = cache.get("AT", A.T)
@@ -159,7 +159,7 @@ def get_recip_degreesp(G, mask=None):


def get_recip_degreesm(G, mask=None):
"""C = select.offdiag(A) ; pair(C & C.T).reduce_rowwise()"""
"""``C = select.offdiag(A) ; pair(C & C.T).reduce_rowwise()``."""
A = G._A
cache = G._cache
if "AT" in cache:
@@ -236,7 +236,7 @@ def get_recip_degreesm(G, mask=None):


def get_total_degreesp(G, mask=None):
"""A.reduce_rowwise(agg.count) + A.reduce_columnwise(agg.count)"""
"""``A.reduce_rowwise(agg.count) + A.reduce_columnwise(agg.count)``."""
cache = G._cache
if mask is not None:
if "total_degrees+" in cache:
@@ -266,7 +266,7 @@ def get_total_degreesp(G, mask=None):


def get_total_degreesm(G, mask=None):
"""C = select.offdiag(A) ; C.reduce_rowwise(agg.count) + C.reduce_columnwise(agg.count)"""
"""``C = select.offdiag(A) ; C.reduce_rowwise(agg.count) + C.reduce_columnwise(agg.count)``."""
cache = G._cache
if mask is not None:
if "total_degrees-" in cache:
@@ -296,7 +296,7 @@ def get_total_degreesm(G, mask=None):


def get_total_recipp(G, mask=None):
"""pair(A & A.T).reduce_scalar()"""
"""``pair(A & A.T).reduce_scalar()``."""
A = G._A
cache = G._cache
if "total_recip+" not in cache:
@@ -315,7 +315,7 @@ def get_total_recipp(G, mask=None):


def get_total_recipm(G, mask=None):
"""C = select.offdiag(A) ; pair(C & C.T).reduce_scalar()"""
"""``C = select.offdiag(A) ; pair(C & C.T).reduce_scalar()``."""
cache = G._cache
if "total_recip-" not in cache:
if "total_recip+" in cache and cache.get("has_self_edges") is False:
@@ -330,7 +330,7 @@ def get_total_recipm(G, mask=None):


def has_self_edges(G, mask=None):
"""A.diag().nvals > 0"""
"""``A.diag().nvals > 0``."""
A = G._A
cache = G._cache
if "has_self_edges" not in cache:
18 changes: 9 additions & 9 deletions graphblas_algorithms/classes/graph.py
Original file line number Diff line number Diff line change
@@ -10,19 +10,19 @@


def get_A(G, mask=None):
"""A"""
"""``A``."""
return G._A


def get_AT(G, mask=None):
"""A.T"""
"""``A.T``."""
A = G._A
G._cache["AT"] = A
return A


def get_offdiag(G, mask=None):
"""select.offdiag(A)"""
"""``select.offdiag(A)``."""
A = G._A
cache = G._cache
if "offdiag" not in cache:
@@ -38,7 +38,7 @@ def get_offdiag(G, mask=None):


def get_Up(G, mask=None):
"""select.triu(A)"""
"""``select.triu(A)``."""
A = G._A
cache = G._cache
if "U+" not in cache:
@@ -54,7 +54,7 @@ def get_Up(G, mask=None):


def get_Lp(G, mask=None):
"""select.tril(A)"""
"""``select.tril(A)``."""
A = G._A
cache = G._cache
if "L+" not in cache:
@@ -70,7 +70,7 @@ def get_Lp(G, mask=None):


def get_Um(G, mask=None):
"""select.triu(A, 1)"""
"""``select.triu(A, 1)``."""
A = G._A
cache = G._cache
if "U-" not in cache:
@@ -91,7 +91,7 @@ def get_Um(G, mask=None):


def get_Lm(G, mask=None):
"""select.tril(A, -1)"""
"""``select.tril(A, -1)``."""
A = G._A
cache = G._cache
if "L-" not in cache:
@@ -112,7 +112,7 @@ def get_Lm(G, mask=None):


def get_diag(G, mask=None):
"""A.diag()"""
"""``A.diag()``."""
A = G._A
cache = G._cache
if "diag" not in cache:
@@ -193,7 +193,7 @@ def has_negative_edgesm(G, mask=None):


def has_self_edges(G, mask=None):
"""A.diag().nvals > 0"""
"""``A.diag().nvals > 0``."""
A = G._A
cache = G._cache
if "has_self_edges" not in cache:
2 changes: 1 addition & 1 deletion graphblas_algorithms/conftest.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@

@pytest.fixture(scope="session", autouse=True)
def ic():
"""Make `ic` available everywhere during testing for easier debugging"""
"""Make `ic` available everywhere during testing for easier debugging."""
try:
import icecream
except ImportError:
4 changes: 2 additions & 2 deletions graphblas_algorithms/nxapi/_utils.py
Original file line number Diff line number Diff line change
@@ -88,7 +88,7 @@ def normalize_chunksize(chunksize, itemsize=1, N=None):


def partition(chunksize, L, *, evenly=True):
"""Partition a list into chunks"""
"""Partition a list into chunks."""
N = len(L)
if N == 0:
return
@@ -109,7 +109,7 @@ def partition(chunksize, L, *, evenly=True):


def split_evenly(k, L):
"""Split a list into approximately-equal parts"""
"""Split a list into approximately-equal parts."""
N = len(L)
if N == 0:
return
3 changes: 2 additions & 1 deletion graphblas_algorithms/tests/test_match_nx.py
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
For now, though, let's try to match and stay up-to-date with NetworkX!

"""

import sys
from collections import namedtuple
from pathlib import Path
@@ -49,7 +50,7 @@ def isdispatched(func):


def dispatchname(func):
"""The dispatched name of the dispatchable NetworkX function"""
"""The dispatched name of the dispatchable NetworkX function."""
# Haha, there should be a better way to get this
if not isdispatched(func):
raise ValueError(f"Function is not dispatched in NetworkX: {func.__name__}")
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.