python-graphblas 2024.2.0__tar.gz → 2025.2.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. {python-graphblas-2024.2.0/python_graphblas.egg-info → python_graphblas-2025.2.0a1}/PKG-INFO +58 -19
  2. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/README.md +40 -1
  3. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/base.py +1 -1
  4. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/dtypes.py +15 -4
  5. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/formatting.py +1 -1
  6. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/infix.py +1 -0
  7. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/matrix.py +8 -3
  8. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/base.py +1 -2
  9. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/scalar.py +1 -1
  10. python_graphblas-2025.2.0a1/graphblas/core/ss/__init__.py +5 -0
  11. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/config.py +1 -1
  12. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/matrix.py +37 -4
  13. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/vector.py +33 -2
  14. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/utils.py +15 -5
  15. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/vector.py +2 -2
  16. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/exceptions.py +11 -3
  17. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/io/_networkx.py +4 -2
  18. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/conftest.py +8 -0
  19. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_dtype.py +4 -0
  20. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_io.py +1 -1
  21. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_matrix.py +13 -10
  22. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_numpyops.py +10 -0
  23. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_scalar.py +5 -1
  24. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_ssjit.py +49 -3
  25. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_vector.py +16 -5
  26. python_graphblas-2025.2.0a1/pyproject.toml +499 -0
  27. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1/python_graphblas.egg-info}/PKG-INFO +58 -19
  28. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/python_graphblas.egg-info/requires.txt +15 -13
  29. python-graphblas-2024.2.0/graphblas/core/ss/__init__.py +0 -3
  30. python-graphblas-2024.2.0/pyproject.toml +0 -485
  31. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/LICENSE +0 -0
  32. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/MANIFEST.in +0 -0
  33. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/conftest.py +0 -0
  34. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/__init__.py +0 -0
  35. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/agg/__init__.py +0 -0
  36. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/agg/ss.py +0 -0
  37. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/binary/__init__.py +0 -0
  38. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/binary/numpy.py +0 -0
  39. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/binary/ss.py +0 -0
  40. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/__init__.py +0 -0
  41. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/automethods.py +0 -0
  42. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/descriptor.py +0 -0
  43. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/expr.py +0 -0
  44. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/infixmethods.py +0 -0
  45. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/mask.py +0 -0
  46. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/__init__.py +0 -0
  47. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/agg.py +0 -0
  48. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/binary.py +0 -0
  49. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/indexunary.py +0 -0
  50. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/monoid.py +0 -0
  51. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/select.py +0 -0
  52. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/semiring.py +0 -0
  53. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/unary.py +0 -0
  54. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/operator/utils.py +0 -0
  55. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/recorder.py +0 -0
  56. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/slice.py +0 -0
  57. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/binary.py +0 -0
  58. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/context.py +0 -0
  59. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/descriptor.py +0 -0
  60. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/dtypes.py +0 -0
  61. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/indexunary.py +0 -0
  62. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/prefix_scan.py +0 -0
  63. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/select.py +0 -0
  64. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/core/ss/unary.py +0 -0
  65. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/dtypes/__init__.py +0 -0
  66. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/dtypes/ss.py +0 -0
  67. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/graphblas.yaml +0 -0
  68. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/indexunary/__init__.py +0 -0
  69. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/indexunary/ss.py +0 -0
  70. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/io/__init__.py +0 -0
  71. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/io/_awkward.py +0 -0
  72. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/io/_matrixmarket.py +0 -0
  73. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/io/_scipy.py +0 -0
  74. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/io/_sparse.py +0 -0
  75. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/monoid/__init__.py +0 -0
  76. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/monoid/numpy.py +0 -0
  77. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/monoid/ss.py +0 -0
  78. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/op/__init__.py +0 -0
  79. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/op/numpy.py +0 -0
  80. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/op/ss.py +0 -0
  81. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/select/__init__.py +0 -0
  82. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/select/ss.py +0 -0
  83. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/semiring/__init__.py +0 -0
  84. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/semiring/numpy.py +0 -0
  85. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/semiring/ss.py +0 -0
  86. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/ss/__init__.py +0 -0
  87. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/ss/_core.py +0 -0
  88. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/__init__.py +0 -0
  89. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/pickle1-vanilla.pkl +0 -0
  90. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/pickle1.pkl +0 -0
  91. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/pickle2-vanilla.pkl +0 -0
  92. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/pickle2.pkl +0 -0
  93. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/pickle3-vanilla.pkl +0 -0
  94. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/pickle3.pkl +0 -0
  95. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_auto_init.py +0 -0
  96. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_core.py +0 -0
  97. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_descriptor.py +0 -0
  98. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_external_init.py +0 -0
  99. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_formatting.py +0 -0
  100. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_infix.py +0 -0
  101. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_mask.py +0 -0
  102. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_op.py +0 -0
  103. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_operator_types.py +0 -0
  104. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_pickle.py +0 -0
  105. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_prefix_scan.py +0 -0
  106. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_recorder.py +0 -0
  107. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_resolving.py +0 -0
  108. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/tests/test_ss_utils.py +0 -0
  109. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/unary/__init__.py +0 -0
  110. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/unary/numpy.py +0 -0
  111. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/unary/ss.py +0 -0
  112. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/graphblas/viz.py +0 -0
  113. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/python_graphblas.egg-info/SOURCES.txt +0 -0
  114. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/python_graphblas.egg-info/dependency_links.txt +0 -0
  115. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/python_graphblas.egg-info/top_level.txt +0 -0
  116. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/setup.cfg +0 -0
  117. {python-graphblas-2024.2.0 → python_graphblas-2025.2.0a1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: python-graphblas
3
- Version: 2024.2.0
3
+ Version: 2025.2.0a1
4
4
  Summary: Python library for GraphBLAS: high-performance sparse linear algebra for scalable graph analytics
5
5
  Author: Jim Kitchen, Python-graphblas contributors
6
6
  Author-email: Erik Welch <erik.n.welch@gmail.com>
@@ -219,10 +219,10 @@ Classifier: Operating System :: POSIX :: Linux
219
219
  Classifier: Operating System :: Microsoft :: Windows
220
220
  Classifier: Programming Language :: Python
221
221
  Classifier: Programming Language :: Python :: 3
222
- Classifier: Programming Language :: Python :: 3.9
223
222
  Classifier: Programming Language :: Python :: 3.10
224
223
  Classifier: Programming Language :: Python :: 3.11
225
224
  Classifier: Programming Language :: Python :: 3.12
225
+ Classifier: Programming Language :: Python :: 3.13
226
226
  Classifier: Programming Language :: Python :: 3 :: Only
227
227
  Classifier: Intended Audience :: Developers
228
228
  Classifier: Intended Audience :: Other Audience
@@ -231,22 +231,22 @@ Classifier: Topic :: Scientific/Engineering
231
231
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
232
232
  Classifier: Topic :: Scientific/Engineering :: Mathematics
233
233
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
234
- Requires-Python: >=3.9
234
+ Requires-Python: >=3.10
235
235
  Description-Content-Type: text/markdown
236
236
  License-File: LICENSE
237
- Requires-Dist: numpy>=1.22
237
+ Requires-Dist: numpy>=1.23
238
238
  Requires-Dist: donfig>=0.6
239
239
  Requires-Dist: pyyaml>=5.4
240
- Requires-Dist: suitesparse-graphblas<9,>=7.4.0.0
241
- Requires-Dist: numba>=0.55; python_version < "3.13"
240
+ Requires-Dist: suitesparse-graphblas<10,>=7.4.0.0
241
+ Requires-Dist: numba>=0.55; python_version < "3.14"
242
242
  Provides-Extra: suitesparse
243
- Requires-Dist: suitesparse-graphblas<9,>=7.4.0.0; extra == "suitesparse"
243
+ Requires-Dist: suitesparse-graphblas<10,>=7.4.0.0; extra == "suitesparse"
244
244
  Provides-Extra: networkx
245
245
  Requires-Dist: networkx>=2.8; extra == "networkx"
246
246
  Provides-Extra: numba
247
247
  Requires-Dist: numba>=0.55; extra == "numba"
248
248
  Provides-Extra: pandas
249
- Requires-Dist: pandas>=1.2; extra == "pandas"
249
+ Requires-Dist: pandas>=1.5; extra == "pandas"
250
250
  Provides-Extra: scipy
251
251
  Requires-Dist: scipy>=1.9; extra == "scipy"
252
252
  Provides-Extra: suitesparse-udf
@@ -255,17 +255,17 @@ Provides-Extra: repr
255
255
  Requires-Dist: python-graphblas[pandas]; extra == "repr"
256
256
  Provides-Extra: io
257
257
  Requires-Dist: python-graphblas[networkx,scipy]; extra == "io"
258
- Requires-Dist: python-graphblas[numba]; python_version < "3.13" and extra == "io"
259
- Requires-Dist: awkward>=1.9; extra == "io"
260
- Requires-Dist: sparse>=0.13; python_version < "3.13" and extra == "io"
261
- Requires-Dist: fast-matrix-market>=1.4.5; extra == "io"
258
+ Requires-Dist: python-graphblas[numba]; python_version < "3.14" and extra == "io"
259
+ Requires-Dist: awkward>=2.0; extra == "io"
260
+ Requires-Dist: sparse>=0.14; python_version < "3.13" and extra == "io"
261
+ Requires-Dist: fast-matrix-market>=1.4.5; python_version < "3.13" and extra == "io"
262
262
  Provides-Extra: viz
263
263
  Requires-Dist: python-graphblas[networkx,scipy]; extra == "viz"
264
- Requires-Dist: matplotlib>=3.5; extra == "viz"
264
+ Requires-Dist: matplotlib>=3.6; extra == "viz"
265
265
  Provides-Extra: datashade
266
266
  Requires-Dist: python-graphblas[numba,pandas,scipy]; extra == "datashade"
267
- Requires-Dist: datashader>=0.12; extra == "datashade"
268
- Requires-Dist: hvplot>=0.7; extra == "datashade"
267
+ Requires-Dist: datashader>=0.14; extra == "datashade"
268
+ Requires-Dist: hvplot>=0.8; extra == "datashade"
269
269
  Provides-Extra: test
270
270
  Requires-Dist: python-graphblas[pandas,scipy,suitesparse]; extra == "test"
271
271
  Requires-Dist: packaging>=21; extra == "test"
@@ -273,10 +273,10 @@ Requires-Dist: pytest>=6.2; extra == "test"
273
273
  Requires-Dist: tomli>=1; extra == "test"
274
274
  Provides-Extra: default
275
275
  Requires-Dist: python-graphblas[pandas,scipy,suitesparse]; extra == "default"
276
- Requires-Dist: python-graphblas[numba]; python_version < "3.13" and extra == "default"
276
+ Requires-Dist: python-graphblas[numba]; python_version < "3.14" and extra == "default"
277
277
  Provides-Extra: all
278
278
  Requires-Dist: python-graphblas[default,io,test,viz]; extra == "all"
279
- Requires-Dist: python-graphblas[datashade]; python_version < "3.13" and extra == "all"
279
+ Requires-Dist: python-graphblas[datashade]; python_version < "3.14" and extra == "all"
280
280
 
281
281
  ![Python-graphblas](https://raw.githubusercontent.com/python-graphblas/python-graphblas/main/docs/_static/img/logo-horizontal-medium-big.svg)
282
282
 
@@ -315,14 +315,19 @@ For algorithms, see
315
315
  </p>
316
316
 
317
317
  ## Install
318
+
318
319
  Install the latest version of Python-graphblas via conda:
320
+
319
321
  ```
320
322
  $ conda install -c conda-forge python-graphblas
321
323
  ```
324
+
322
325
  or pip:
326
+
323
327
  ```
324
- $ pip install python-graphblas[default]
328
+ $ pip install 'python-graphblas[default]'
325
329
  ```
330
+
326
331
  This will also install the [SuiteSparse:GraphBLAS](https://github.com/DrTimothyAldenDavis/GraphBLAS) compiled C library.
327
332
  We currently support the [GraphBLAS C API 2.0 specification](https://graphblas.org/docs/GraphBLAS_API_C_v2.0.0.pdf).
328
333
 
@@ -337,6 +342,7 @@ The following are not required by python-graphblas, but may be needed for certai
337
342
  - `fast-matrix-market` - for faster read/write of Matrix Market files with `gb.io.mmread` and `gb.io.mmwrite`.
338
343
 
339
344
  ## Description
345
+
340
346
  Currently works with [SuiteSparse:GraphBLAS](https://github.com/DrTimothyAldenDavis/GraphBLAS), but the goal is to make it work with all implementations of the GraphBLAS spec.
341
347
 
342
348
  The approach taken with this library is to follow the C-API 2.0 specification as closely as possible while making improvements
@@ -350,10 +356,12 @@ with how Python handles assignment, so instead we (ab)use the left-shift `<<` no
350
356
  assignment. This opens up all kinds of nice possibilities.
351
357
 
352
358
  This is an example of how the mapping works:
359
+
353
360
  ```C
354
361
  // C call
355
362
  GrB_Matrix_mxm(M, mask, GrB_PLUS_INT64, GrB_MIN_PLUS_INT64, A, B, NULL)
356
363
  ```
364
+
357
365
  ```python
358
366
  # Python call
359
367
  M(mask.V, accum=binary.plus) << A.mxm(B, semiring.min_plus)
@@ -371,10 +379,12 @@ is a much better approach, even if it doesn't feel very Pythonic.
371
379
  Descriptor flags are set on the appropriate elements to keep logic close to what it affects. Here is the same call
372
380
  with descriptor bits set. `ttcsr` indicates transpose the first and second matrices, complement the structure of the mask,
373
381
  and do a replacement on the output.
382
+
374
383
  ```C
375
384
  // C call
376
385
  GrB_Matrix_mxm(M, mask, GrB_PLUS_INT64, GrB_MIN_PLUS_INT64, A, B, desc.ttcsr)
377
386
  ```
387
+
378
388
  ```python
379
389
  # Python call
380
390
  M(~mask.S, accum=binary.plus, replace=True) << A.T.mxm(B.T, semiring.min_plus)
@@ -384,16 +394,20 @@ The objects receiving the flag operations (A.T, ~mask, etc) are also delayed obj
384
394
  do no computation, allowing the correct descriptor bits to be set in a single GraphBLAS call.
385
395
 
386
396
  **If no mask or accumulator is used, the call looks like this**:
397
+
387
398
  ```python
388
399
  M << A.mxm(B, semiring.min_plus)
389
400
  ```
401
+
390
402
  The use of `<<` to indicate updating is actually just syntactic sugar for a real `.update()` method. The above
391
403
  expression could be written as:
404
+
392
405
  ```python
393
406
  M.update(A.mxm(B, semiring.min_plus))
394
407
  ```
395
408
 
396
409
  ## Operations
410
+
397
411
  ```python
398
412
  M(mask, accum) << A.mxm(B, semiring) # mxm
399
413
  w(mask, accum) << A.mxv(v, semiring) # mxv
@@ -403,14 +417,18 @@ M(mask, accum) << A.ewise_mult(B, binaryop) # eWiseMult
403
417
  M(mask, accum) << A.kronecker(B, binaryop) # kronecker
404
418
  M(mask, accum) << A.T # transpose
405
419
  ```
420
+
406
421
  ## Extract
422
+
407
423
  ```python
408
424
  M(mask, accum) << A[rows, cols] # rows and cols are a list or a slice
409
425
  w(mask, accum) << A[rows, col_index] # extract column
410
426
  w(mask, accum) << A[row_index, cols] # extract row
411
427
  s = A[row_index, col_index].value # extract single element
412
428
  ```
429
+
413
430
  ## Assign
431
+
414
432
  ```python
415
433
  M(mask, accum)[rows, cols] << A # rows and cols are a list or a slice
416
434
  M(mask, accum)[rows, col_index] << v # assign column
@@ -420,31 +438,42 @@ M[row_index, col_index] << s # assign scalar to single element
420
438
  # (mask and accum not allowed)
421
439
  del M[row_index, col_index] # remove single element
422
440
  ```
441
+
423
442
  ## Apply
443
+
424
444
  ```python
425
445
  M(mask, accum) << A.apply(unaryop)
426
446
  M(mask, accum) << A.apply(binaryop, left=s) # bind-first
427
447
  M(mask, accum) << A.apply(binaryop, right=s) # bind-second
428
448
  ```
449
+
429
450
  ## Reduce
451
+
430
452
  ```python
431
453
  v(mask, accum) << A.reduce_rowwise(op) # reduce row-wise
432
454
  v(mask, accum) << A.reduce_columnwise(op) # reduce column-wise
433
455
  s(accum) << A.reduce_scalar(op)
434
456
  s(accum) << v.reduce(op)
435
457
  ```
458
+
436
459
  ## Creating new Vectors / Matrices
460
+
437
461
  ```python
438
462
  A = Matrix.new(dtype, num_rows, num_cols) # new_type
439
463
  B = A.dup() # dup
440
464
  A = Matrix.from_coo([row_indices], [col_indices], [values]) # build
441
465
  ```
466
+
442
467
  ## New from delayed
468
+
443
469
  Delayed objects can be used to create a new object using `.new()` method
470
+
444
471
  ```python
445
472
  C = A.mxm(B, semiring).new()
446
473
  ```
474
+
447
475
  ## Properties
476
+
448
477
  ```python
449
478
  size = v.size # size
450
479
  nrows = M.nrows # nrows
@@ -452,10 +481,13 @@ ncols = M.ncols # ncols
452
481
  nvals = M.nvals # nvals
453
482
  rindices, cindices, vals = M.to_coo() # extractTuples
454
483
  ```
484
+
455
485
  ## Initialization
486
+
456
487
  There is a mechanism to initialize `graphblas` with a context prior to use. This allows for setting the backend to
457
488
  use as well as the blocking/non-blocking mode. If the context is not initialized, a default initialization will
458
489
  be performed automatically.
490
+
459
491
  ```python
460
492
  import graphblas as gb
461
493
 
@@ -466,10 +498,13 @@ gb.init("suitesparse", blocking=True)
466
498
  from graphblas import binary, semiring
467
499
  from graphblas import Matrix, Vector, Scalar
468
500
  ```
501
+
469
502
  ## Performant User Defined Functions
503
+
470
504
  Python-graphblas requires `numba` which enables compiling user-defined Python functions to native C for use in GraphBLAS.
471
505
 
472
506
  Example customized UnaryOp:
507
+
473
508
  ```python
474
509
  from graphblas import unary
475
510
 
@@ -484,9 +519,11 @@ v = Vector.from_coo([0, 1, 3], [1, 2, 3])
484
519
  w = v.apply(unary.force_odd).new()
485
520
  w # indexes=[0, 1, 3], values=[1, 3, 3]
486
521
  ```
522
+
487
523
  Similar methods exist for BinaryOp, Monoid, and Semiring.
488
524
 
489
525
  ## Relation to other network analysis libraries
526
+
490
527
  Python-graphblas aims to provide an efficient and consistent expression
491
528
  of graph operations using linear algebra. This allows the development of
492
529
  high-performance implementations of existing and new graph algorithms
@@ -503,7 +540,9 @@ other libraries, `graphblas.io` contains multiple connectors, see the
503
540
  following section.
504
541
 
505
542
  ## Import/Export connectors to the Python ecosystem
543
+
506
544
  `graphblas.io` contains functions for converting to and from:
545
+
507
546
  ```python
508
547
  import graphblas as gb
509
548
 
@@ -35,14 +35,19 @@ For algorithms, see
35
35
  </p>
36
36
 
37
37
  ## Install
38
+
38
39
  Install the latest version of Python-graphblas via conda:
40
+
39
41
  ```
40
42
  $ conda install -c conda-forge python-graphblas
41
43
  ```
44
+
42
45
  or pip:
46
+
43
47
  ```
44
- $ pip install python-graphblas[default]
48
+ $ pip install 'python-graphblas[default]'
45
49
  ```
50
+
46
51
  This will also install the [SuiteSparse:GraphBLAS](https://github.com/DrTimothyAldenDavis/GraphBLAS) compiled C library.
47
52
  We currently support the [GraphBLAS C API 2.0 specification](https://graphblas.org/docs/GraphBLAS_API_C_v2.0.0.pdf).
48
53
 
@@ -57,6 +62,7 @@ The following are not required by python-graphblas, but may be needed for certai
57
62
  - `fast-matrix-market` - for faster read/write of Matrix Market files with `gb.io.mmread` and `gb.io.mmwrite`.
58
63
 
59
64
  ## Description
65
+
60
66
  Currently works with [SuiteSparse:GraphBLAS](https://github.com/DrTimothyAldenDavis/GraphBLAS), but the goal is to make it work with all implementations of the GraphBLAS spec.
61
67
 
62
68
  The approach taken with this library is to follow the C-API 2.0 specification as closely as possible while making improvements
@@ -70,10 +76,12 @@ with how Python handles assignment, so instead we (ab)use the left-shift `<<` no
70
76
  assignment. This opens up all kinds of nice possibilities.
71
77
 
72
78
  This is an example of how the mapping works:
79
+
73
80
  ```C
74
81
  // C call
75
82
  GrB_Matrix_mxm(M, mask, GrB_PLUS_INT64, GrB_MIN_PLUS_INT64, A, B, NULL)
76
83
  ```
84
+
77
85
  ```python
78
86
  # Python call
79
87
  M(mask.V, accum=binary.plus) << A.mxm(B, semiring.min_plus)
@@ -91,10 +99,12 @@ is a much better approach, even if it doesn't feel very Pythonic.
91
99
  Descriptor flags are set on the appropriate elements to keep logic close to what it affects. Here is the same call
92
100
  with descriptor bits set. `ttcsr` indicates transpose the first and second matrices, complement the structure of the mask,
93
101
  and do a replacement on the output.
102
+
94
103
  ```C
95
104
  // C call
96
105
  GrB_Matrix_mxm(M, mask, GrB_PLUS_INT64, GrB_MIN_PLUS_INT64, A, B, desc.ttcsr)
97
106
  ```
107
+
98
108
  ```python
99
109
  # Python call
100
110
  M(~mask.S, accum=binary.plus, replace=True) << A.T.mxm(B.T, semiring.min_plus)
@@ -104,16 +114,20 @@ The objects receiving the flag operations (A.T, ~mask, etc) are also delayed obj
104
114
  do no computation, allowing the correct descriptor bits to be set in a single GraphBLAS call.
105
115
 
106
116
  **If no mask or accumulator is used, the call looks like this**:
117
+
107
118
  ```python
108
119
  M << A.mxm(B, semiring.min_plus)
109
120
  ```
121
+
110
122
  The use of `<<` to indicate updating is actually just syntactic sugar for a real `.update()` method. The above
111
123
  expression could be written as:
124
+
112
125
  ```python
113
126
  M.update(A.mxm(B, semiring.min_plus))
114
127
  ```
115
128
 
116
129
  ## Operations
130
+
117
131
  ```python
118
132
  M(mask, accum) << A.mxm(B, semiring) # mxm
119
133
  w(mask, accum) << A.mxv(v, semiring) # mxv
@@ -123,14 +137,18 @@ M(mask, accum) << A.ewise_mult(B, binaryop) # eWiseMult
123
137
  M(mask, accum) << A.kronecker(B, binaryop) # kronecker
124
138
  M(mask, accum) << A.T # transpose
125
139
  ```
140
+
126
141
  ## Extract
142
+
127
143
  ```python
128
144
  M(mask, accum) << A[rows, cols] # rows and cols are a list or a slice
129
145
  w(mask, accum) << A[rows, col_index] # extract column
130
146
  w(mask, accum) << A[row_index, cols] # extract row
131
147
  s = A[row_index, col_index].value # extract single element
132
148
  ```
149
+
133
150
  ## Assign
151
+
134
152
  ```python
135
153
  M(mask, accum)[rows, cols] << A # rows and cols are a list or a slice
136
154
  M(mask, accum)[rows, col_index] << v # assign column
@@ -140,31 +158,42 @@ M[row_index, col_index] << s # assign scalar to single element
140
158
  # (mask and accum not allowed)
141
159
  del M[row_index, col_index] # remove single element
142
160
  ```
161
+
143
162
  ## Apply
163
+
144
164
  ```python
145
165
  M(mask, accum) << A.apply(unaryop)
146
166
  M(mask, accum) << A.apply(binaryop, left=s) # bind-first
147
167
  M(mask, accum) << A.apply(binaryop, right=s) # bind-second
148
168
  ```
169
+
149
170
  ## Reduce
171
+
150
172
  ```python
151
173
  v(mask, accum) << A.reduce_rowwise(op) # reduce row-wise
152
174
  v(mask, accum) << A.reduce_columnwise(op) # reduce column-wise
153
175
  s(accum) << A.reduce_scalar(op)
154
176
  s(accum) << v.reduce(op)
155
177
  ```
178
+
156
179
  ## Creating new Vectors / Matrices
180
+
157
181
  ```python
158
182
  A = Matrix.new(dtype, num_rows, num_cols) # new_type
159
183
  B = A.dup() # dup
160
184
  A = Matrix.from_coo([row_indices], [col_indices], [values]) # build
161
185
  ```
186
+
162
187
  ## New from delayed
188
+
163
189
  Delayed objects can be used to create a new object using `.new()` method
190
+
164
191
  ```python
165
192
  C = A.mxm(B, semiring).new()
166
193
  ```
194
+
167
195
  ## Properties
196
+
168
197
  ```python
169
198
  size = v.size # size
170
199
  nrows = M.nrows # nrows
@@ -172,10 +201,13 @@ ncols = M.ncols # ncols
172
201
  nvals = M.nvals # nvals
173
202
  rindices, cindices, vals = M.to_coo() # extractTuples
174
203
  ```
204
+
175
205
  ## Initialization
206
+
176
207
  There is a mechanism to initialize `graphblas` with a context prior to use. This allows for setting the backend to
177
208
  use as well as the blocking/non-blocking mode. If the context is not initialized, a default initialization will
178
209
  be performed automatically.
210
+
179
211
  ```python
180
212
  import graphblas as gb
181
213
 
@@ -186,10 +218,13 @@ gb.init("suitesparse", blocking=True)
186
218
  from graphblas import binary, semiring
187
219
  from graphblas import Matrix, Vector, Scalar
188
220
  ```
221
+
189
222
  ## Performant User Defined Functions
223
+
190
224
  Python-graphblas requires `numba` which enables compiling user-defined Python functions to native C for use in GraphBLAS.
191
225
 
192
226
  Example customized UnaryOp:
227
+
193
228
  ```python
194
229
  from graphblas import unary
195
230
 
@@ -204,9 +239,11 @@ v = Vector.from_coo([0, 1, 3], [1, 2, 3])
204
239
  w = v.apply(unary.force_odd).new()
205
240
  w # indexes=[0, 1, 3], values=[1, 3, 3]
206
241
  ```
242
+
207
243
  Similar methods exist for BinaryOp, Monoid, and Semiring.
208
244
 
209
245
  ## Relation to other network analysis libraries
246
+
210
247
  Python-graphblas aims to provide an efficient and consistent expression
211
248
  of graph operations using linear algebra. This allows the development of
212
249
  high-performance implementations of existing and new graph algorithms
@@ -223,7 +260,9 @@ other libraries, `graphblas.io` contains multiple connectors, see the
223
260
  following section.
224
261
 
225
262
  ## Import/Export connectors to the Python ecosystem
263
+
226
264
  `graphblas.io` contains functions for converting to and from:
265
+
227
266
  ```python
228
267
  import graphblas as gb
229
268
 
@@ -513,7 +513,7 @@ class BaseType:
513
513
  _expect_op = _expect_op
514
514
 
515
515
  # Don't let non-scalars be coerced to numpy arrays
516
- def __array__(self, dtype=None):
516
+ def __array__(self, dtype=None, *, copy=None):
517
517
  raise TypeError(
518
518
  f"{type(self).__name__} can't be directly converted to a numpy array; "
519
519
  f"perhaps use `{self.name}.to_coo()` method instead."
@@ -1,4 +1,5 @@
1
1
  import warnings
2
+ from ast import literal_eval
2
3
 
3
4
  import numpy as np
4
5
  from numpy import promote_types, result_type
@@ -97,7 +98,7 @@ def register_anonymous(dtype, name=None):
97
98
  # Allow dtypes such as `"INT64[3, 4]"` for convenience
98
99
  base_dtype, shape = dtype.split("[", 1)
99
100
  base_dtype = lookup_dtype(base_dtype)
100
- shape = np.lib.format.safe_eval(f"[{shape}")
101
+ shape = literal_eval(f"[{shape}")
101
102
  dtype = np.dtype((base_dtype.np_type, shape))
102
103
  else:
103
104
  raise
@@ -115,7 +116,17 @@ def register_anonymous(dtype, name=None):
115
116
  from ..exceptions import check_status_carg
116
117
 
117
118
  gb_obj = ffi.new("GrB_Type*")
118
- if backend == "suitesparse":
119
+
120
+ if hasattr(lib, "GrB_Type_set_String"):
121
+ # We name this so that we can serialize and deserialize UDTs
122
+ # We don't yet have C definitions
123
+ np_repr = _dtype_to_string(dtype)
124
+ status = lib.GrB_Type_new(gb_obj, dtype.itemsize)
125
+ check_status_carg(status, "Type", gb_obj[0])
126
+ val_obj = ffi.new("char[]", np_repr.encode())
127
+ status = lib.GrB_Type_set_String(gb_obj[0], val_obj, lib.GrB_NAME)
128
+ elif backend == "suitesparse":
129
+ # For SuiteSparse < 9
119
130
  # We name this so that we can serialize and deserialize UDTs
120
131
  # We don't yet have C definitions
121
132
  np_repr = _dtype_to_string(dtype).encode()
@@ -429,7 +440,7 @@ def _dtype_to_string(dtype):
429
440
  np_type = dtype.np_type
430
441
  s = str(np_type)
431
442
  try:
432
- if np.dtype(np.lib.format.safe_eval(s)) == np_type: # pragma: no branch (safety)
443
+ if np.dtype(literal_eval(s)) == np_type: # pragma: no branch (safety)
433
444
  return s
434
445
  except Exception:
435
446
  pass
@@ -448,5 +459,5 @@ def _string_to_dtype(s):
448
459
  return lookup_dtype(s)
449
460
  except Exception:
450
461
  pass
451
- np_type = np.dtype(np.lib.format.safe_eval(s))
462
+ np_type = np.dtype(literal_eval(s))
452
463
  return lookup_dtype(np_type)
@@ -630,7 +630,7 @@ def create_header(type_name, keys, vals, *, lower_border=False, name="", quote=T
630
630
  name = f'"{name}"'
631
631
  key_text = []
632
632
  val_text = []
633
- for key, val in zip(keys, vals):
633
+ for key, val in zip(keys, vals, strict=True):
634
634
  width = max(len(key), len(val)) + 2
635
635
  key_text.append(key.rjust(width))
636
636
  val_text.append(val.rjust(width))
@@ -316,6 +316,7 @@ class MatrixInfixExpr(InfixExprBase):
316
316
  ndim = 2
317
317
  output_type = MatrixExpression
318
318
  _is_transposed = False
319
+ __networkx_backend__ = "graphblas"
319
320
  __networkx_plugin__ = "graphblas"
320
321
 
321
322
  def __init__(self, left, right):
@@ -184,6 +184,7 @@ class Matrix(BaseType):
184
184
  ndim = 2
185
185
  _is_transposed = False
186
186
  _name_counter = itertools.count()
187
+ __networkx_backend__ = "graphblas"
187
188
  __networkx_plugin__ = "graphblas"
188
189
 
189
190
  def __new__(cls, dtype=FP64, nrows=0, ncols=0, *, name=None):
@@ -360,7 +361,7 @@ class Matrix(BaseType):
360
361
  def __iter__(self):
361
362
  """Iterate over (row, col) indices which are present in the matrix."""
362
363
  rows, columns, _ = self.to_coo(values=False)
363
- return zip(rows.flat, columns.flat)
364
+ return zip(rows.flat, columns.flat, strict=True)
364
365
 
365
366
  def __sizeof__(self):
366
367
  if backend == "suitesparse":
@@ -961,7 +962,7 @@ class Matrix(BaseType):
961
962
  rows = edgelist[:, 0]
962
963
  cols = edgelist[:, 1]
963
964
  else:
964
- unzipped = list(zip(*edgelist))
965
+ unzipped = list(zip(*edgelist, strict=True))
965
966
  if len(unzipped) == 2:
966
967
  rows, cols = unzipped
967
968
  elif len(unzipped) == 3:
@@ -1826,10 +1827,11 @@ class Matrix(BaseType):
1826
1827
  cols = cols.tolist()
1827
1828
  values = values.tolist()
1828
1829
  return {
1829
- row: dict(zip(cols[start:stop], values[start:stop]))
1830
+ row: dict(zip(cols[start:stop], values[start:stop], strict=True))
1830
1831
  for row, (start, stop) in zip(
1831
1832
  compressed_rows.tolist(),
1832
1833
  np.lib.stride_tricks.sliding_window_view(indptr, 2).tolist(),
1834
+ strict=True,
1833
1835
  )
1834
1836
  }
1835
1837
  # Alternative
@@ -3582,6 +3584,7 @@ class MatrixExpression(BaseExpression):
3582
3584
  ndim = 2
3583
3585
  output_type = Matrix
3584
3586
  _is_transposed = False
3587
+ __networkx_backend__ = "graphblas"
3585
3588
  __networkx_plugin__ = "graphblas"
3586
3589
 
3587
3590
  def __init__(
@@ -3723,6 +3726,7 @@ class MatrixIndexExpr(AmbiguousAssignOrExtract):
3723
3726
  ndim = 2
3724
3727
  output_type = Matrix
3725
3728
  _is_transposed = False
3729
+ __networkx_backend__ = "graphblas"
3726
3730
  __networkx_plugin__ = "graphblas"
3727
3731
 
3728
3732
  def __init__(self, parent, resolved_indexes, nrows, ncols):
@@ -3823,6 +3827,7 @@ class TransposedMatrix:
3823
3827
  ndim = 2
3824
3828
  _is_scalar = False
3825
3829
  _is_transposed = True
3830
+ __networkx_backend__ = "graphblas"
3826
3831
  __networkx_plugin__ = "graphblas"
3827
3832
 
3828
3833
  def __init__(self, matrix):
@@ -251,8 +251,7 @@ class TypedOpBase:
251
251
 
252
252
  def __repr__(self):
253
253
  classname = self.opclass.lower()
254
- if classname.endswith("op"):
255
- classname = classname[:-2]
254
+ classname = classname.removesuffix("op")
256
255
  dtype2 = "" if self._type2 is None else f", {self._type2.name}"
257
256
  return f"{classname}.{self.name}[{self.type.name}{dtype2}]"
258
257
 
@@ -165,7 +165,7 @@ class Scalar(BaseType):
165
165
  return self.__int__
166
166
  raise AttributeError("Scalar object only has `__index__` for integral dtypes")
167
167
 
168
- def __array__(self, dtype=None):
168
+ def __array__(self, dtype=None, *, copy=None):
169
169
  if dtype is None:
170
170
  dtype = self.dtype.np_type
171
171
  return np.array(self.value, dtype=dtype)
@@ -0,0 +1,5 @@
1
+ import suitesparse_graphblas as _ssgb
2
+
3
+ (version_major, version_minor, version_bug) = map(int, _ssgb.__version__.split(".")[:3])
4
+
5
+ _IS_SSGB7 = version_major == 7
@@ -99,7 +99,7 @@ class BaseConfig(MutableMapping):
99
99
  return {reverse_bitwise[val]}
100
100
  rv = set()
101
101
  for k, v in self._bitwise[key].items():
102
- if isinstance(k, str) and val & v and bin(v).count("1") == 1:
102
+ if isinstance(k, str) and val & v and (v).bit_count() == 1:
103
103
  rv.add(k)
104
104
  return rv
105
105
  if is_bool: