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: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ extend-ignore =
per-file-ignores =
suitesparse_graphblas/io/binary.py:C408,
suitesparse_graphblas/tests/test_io.py:E721,
# The api package re-exports its submodules for `from suitesparse_graphblas import api`
suitesparse_graphblas/api/__init__.py:F401,
47 changes: 41 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,38 @@ jobs:
source: ["conda-forge"]
# os: ["ubuntu-latest"]
# source: ["source"]
python-version: ["3.11", "3.12", "3.13", "3.14"]
# A trailing "t" means the free-threaded (no-GIL) build of that version.
python-version: ["3.11", "3.12", "3.13", "3.14", "3.14t"]
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- name: Compose conda environment file
run: |
# conda-forge ships two ABIs for CPython 3.13+ ("cp314" and "cp314t"),
# and `python=3.14` alone lets the solver pick either one: it resolved
# to free-threaded on Linux and Windows but to the GIL build on macOS.
# The `python-gil` and `python-freethreading` metapackages pin
# `python_abi` to one or the other, so ask for the one this matrix
# entry wants instead of leaving it to the solver.
pyver="${{ matrix.python-version }}"
if [[ $pyver == *t ]]; then
variant=python-freethreading
pyver=${pyver%t}
else
variant=python-gil
fi
cp continuous_integration/environment.yml environment-ci.yml
echo " - python=$pyver" >> environment-ci.yml
echo " - $variant" >> environment-ci.yml
cat environment-ci.yml
- name: Conda
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
environment-file: continuous_integration/environment.yml
environment-file: environment-ci.yml
channels: conda-forge,nodefaults
channel-priority: strict
activate-environment: suitesparse-graphblas
Expand Down Expand Up @@ -62,15 +81,31 @@ jobs:
- name: Build
run: |
pip install -e . --no-deps
- name: Configure coverage
run: |
# Cython's coverage plugin (see [tool.coverage.run] in pyproject.toml)
# needs coverage's C tracer. conda-forge builds coverage without its C
# extension for free-threaded Python, and conda resolves `python=3.14`
# to the free-threaded build (3.14t) on some platforms. There, loading
# the plugin raises ImportError and every `coverage run` dies before it
# runs anything, so run the tests directly instead.
if python -c "import Cython.Coverage" 2>/dev/null; then
coverage erase
echo "COV=coverage run -a --branch" >> $GITHUB_ENV
else
echo "Cython coverage plugin unusable here; running without coverage"
python -VV
echo "COV=python" >> $GITHUB_ENV
fi
- name: Test
env:
CYTHON_COVERAGE: true
run: |
pytest -s -k test_print_jit_config
coverage run --branch -m pytest
coverage run -a --branch suitesparse_graphblas/tests/test_initialize.py
$COV -m pytest
$COV suitesparse_graphblas/tests/test_initialize.py
- name: create_headers.py check
if: (! contains(matrix.os, 'windows'))
run: |
coverage run -a --branch suitesparse_graphblas/create_headers.py
$COV suitesparse_graphblas/create_headers.py
git diff --exit-code # error if anything changed
8 changes: 6 additions & 2 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,12 @@ jobs:
# very verbose
CIBW_BUILD_VERBOSITY: 3

# cibuildwheel v3: PyPy no longer built by default; enable explicitly
# Note: cpython-freethreading not enabled because cffi doesn't support it yet
# cibuildwheel v3: PyPy no longer built by default; enable explicitly.
# Free-threaded wheels: cibuildwheel only gates cp313t behind
# "cpython-freethreading" (see its selector.py), so cp314t is already
# built and tested here by default. cp313t stays off because cffi
# publishes no cp313t wheels, so enabling it would build cffi from
# source in every wheel job.
CIBW_ENABLE: "pypy"

# Build SuiteSparse
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ GraphBLAS-*/
pip-log.txt
pip-delete-this-directory.txt

# Conda environment file composed by CI (see .github/workflows/test.yml)
environment-ci.yml

# Unit test / coverage reports
htmlcov/
.tox/
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ repos:
- id: autoflake
args: [--in-place]
- repo: https://github.com/pycqa/isort
rev: 8.0.1
rev: 9.0.0b2
hooks:
- id: isort
- repo: https://github.com/asottile/pyupgrade
Expand All @@ -50,7 +50,7 @@ repos:
- id: pyupgrade
args: [--py311-plus]
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 26.3.1
rev: 26.5.1
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
Expand Down Expand Up @@ -89,7 +89,7 @@ repos:
- id: python-no-log-warn
- id: text-unicode-replacement-char
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.37.0
rev: 0.37.4
hooks:
- id: check-dependabot
- id: check-github-workflows
Expand Down
2 changes: 1 addition & 1 deletion GB_VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
10.3.1
10.4.0
8 changes: 7 additions & 1 deletion suitesparse.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
set -x # echo on

# parse SuiteSparse version from first argument, a git tag that ends in the version (no leading v)
if [[ $1 =~ refs/tags/([0-9]*\.[0-9]*\.[0-9]*\.beta[0-9]*).*$ ]]; then
if [[ $1 =~ refs/tags/v?([0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+) ]]; then
# Naming used since v10.4.0-beta.1, e.g. "10.4.0-beta.2". Note that callers
# append the psg patch level (".0"), which is not part of the upstream tag.
echo "Beta version detected (X.Y.Z-beta.N)"
VERSION=${BASH_REMATCH[1]}
elif [[ $1 =~ refs/tags/([0-9]*\.[0-9]*\.[0-9]*\.beta[0-9]*).*$ ]]; then
# Older naming, e.g. "8.0.1.beta1"
echo "Beta version detected"
VERSION=${BASH_REMATCH[1]}
elif [[ $1 =~ refs/tags/([0-9]*\.[0-9]*\.[0-9]*)\..*$ ]]; then
Expand Down
10 changes: 7 additions & 3 deletions suitesparse_graphblas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,13 @@ def __repr__(self):

burble = burble()

# Backward-compatible re-exports: functional API moved to suitesparse_graphblas.api
from suitesparse_graphblas.api import iterator, matrix, scalar, vector # noqa: E402,F401
from suitesparse_graphblas.api.global_options import ( # noqa: E402,F401
# isort: off
# Backward-compatible re-exports: functional API moved to suitesparse_graphblas.api.
# These must stay at the bottom of the file: the api modules import names defined
# above (`check_status`, `ffi`, `lib`), so letting isort's `float_to_top` hoist them
# to the top would make `import suitesparse_graphblas` a circular import.
from suitesparse_graphblas.api import iterator, matrix, scalar, vector # noqa: E402, F401
from suitesparse_graphblas.api.global_options import ( # noqa: E402, F401
global_option_get_char,
global_option_get_fp64,
global_option_get_int32,
Expand Down
5 changes: 3 additions & 2 deletions suitesparse_graphblas/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
Guide](https://github.com/DrTimothyAldenDavis/GraphBLAS/blob/stable/Doc/GraphBLAS_UserGuide.pdf)

"""

from suitesparse_graphblas.api import io # noqa: F401
from suitesparse_graphblas.api import (
binaryop,
container,
Expand All @@ -33,9 +35,8 @@
matrix,
monoid,
scalar,
semiring,
selectop,
semiring,
unaryop,
vector,
)
from suitesparse_graphblas.api import io # noqa: F401
26 changes: 18 additions & 8 deletions suitesparse_graphblas/api/binaryop.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,15 @@ def binaryop_print(op, name="", level=lib.GxB_COMPLETE):
True

"""
check_status(op, lib.GxB_BinaryOp_fprint(
op[0], name.encode() if isinstance(name, str) else name,
level, ffi.NULL,
))
check_status(
op,
lib.GxB_BinaryOp_fprint(
op[0],
name.encode() if isinstance(name, str) else name,
level,
ffi.NULL,
),
)


def binaryop_fprint(op, f, name="", level=lib.GxB_COMPLETE):
Expand All @@ -122,10 +127,15 @@ def binaryop_fprint(op, f, name="", level=lib.GxB_COMPLETE):
True

"""
check_status(op, lib.GxB_BinaryOp_fprint(
op[0], name.encode() if isinstance(name, str) else name,
level, f,
))
check_status(
op,
lib.GxB_BinaryOp_fprint(
op[0],
name.encode() if isinstance(name, str) else name,
level,
f,
),
)


# ---------------------------------------------------------------------------
Expand Down
26 changes: 18 additions & 8 deletions suitesparse_graphblas/api/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,15 @@ def context_print(ctx, name="", level=lib.GxB_COMPLETE):
True

"""
check_status(ctx, lib.GxB_Context_fprint(
ctx[0], name.encode() if isinstance(name, str) else name,
level, ffi.NULL,
))
check_status(
ctx,
lib.GxB_Context_fprint(
ctx[0],
name.encode() if isinstance(name, str) else name,
level,
ffi.NULL,
),
)


def context_fprint(ctx, f, name="", level=lib.GxB_COMPLETE):
Expand All @@ -219,7 +224,12 @@ def context_fprint(ctx, f, name="", level=lib.GxB_COMPLETE):
True

"""
check_status(ctx, lib.GxB_Context_fprint(
ctx[0], name.encode() if isinstance(name, str) else name,
level, f,
))
check_status(
ctx,
lib.GxB_Context_fprint(
ctx[0],
name.encode() if isinstance(name, str) else name,
level,
f,
),
)
37 changes: 26 additions & 11 deletions suitesparse_graphblas/api/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@ def descriptor_print(desc, name="", level=lib.GxB_COMPLETE):
True

"""
check_status(desc, lib.GxB_Descriptor_fprint(
desc, name.encode() if isinstance(name, str) else name,
level, ffi.NULL,
))
check_status(
desc,
lib.GxB_Descriptor_fprint(
desc,
name.encode() if isinstance(name, str) else name,
level,
ffi.NULL,
),
)


def descriptor_fprint(desc, f, name="", level=lib.GxB_COMPLETE):
Expand All @@ -41,10 +46,15 @@ def descriptor_fprint(desc, f, name="", level=lib.GxB_COMPLETE):
True

"""
check_status(desc, lib.GxB_Descriptor_fprint(
desc, name.encode() if isinstance(name, str) else name,
level, f,
))
check_status(
desc,
lib.GxB_Descriptor_fprint(
desc,
name.encode() if isinstance(name, str) else name,
level,
f,
),
)


# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -72,9 +82,14 @@ def descriptor_set_int32(desc, field, value):
False

"""
check_status(desc, lib.GrB_Descriptor_set_INT32(
desc, ffi.cast("int32_t", value), field,
))
check_status(
desc,
lib.GrB_Descriptor_set_INT32(
desc,
ffi.cast("int32_t", value),
field,
),
)


def descriptor_get_size(desc, field):
Expand Down
29 changes: 22 additions & 7 deletions suitesparse_graphblas/api/examples/bfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,29 +95,44 @@ def bfs(A, src, compute_level=True, compute_parent=True):
if compute_level:
scalar.set_int64(level_scalar, current_level)
vector.vector_assign_scalar(
level_vec, level_scalar, lib.GrB_ALL, n,
mask=frontier, desc=lib.GrB_DESC_S,
level_vec,
level_scalar,
lib.GrB_ALL,
n,
mask=frontier,
desc=lib.GrB_DESC_S,
)

if compute_parent:
# Record parent IDs: parent<s(frontier)> = frontier
vector.vector_assign(
parent_vec, frontier, lib.GrB_ALL, n,
mask=frontier, desc=lib.GrB_DESC_S,
parent_vec,
frontier,
lib.GrB_ALL,
n,
mask=frontier,
desc=lib.GrB_DESC_S,
)
# Convert frontier values to their own indices (ROWINDEX).
# After this, frontier(i) == i for every stored entry,
# so the next vxm propagates node i as the parent ID.
vector.vector_apply_indexop(
frontier, lib.GrB_ROWINDEX_INT64, frontier, index_thunk,
frontier,
lib.GrB_ROWINDEX_INT64,
frontier,
index_thunk,
)

current_level += 1

# Expand frontier: frontier<!mask> = frontier * A
vector.vector_vxm(
frontier, semiring, frontier, A,
mask=mask, desc=lib.GrB_DESC_RSC,
frontier,
semiring,
frontier,
A,
mask=mask,
desc=lib.GrB_DESC_RSC,
)

# Stop when the frontier is empty.
Expand Down
Loading
Loading