python-graphblas 2023.12.0__tar.gz → 2025.2.0__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 (119) hide show
  1. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/PKG-INFO +58 -19
  2. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/README.md +40 -1
  3. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/agg/__init__.py +2 -1
  4. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/binary/numpy.py +1 -0
  5. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/automethods.py +1 -5
  6. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/base.py +1 -1
  7. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/dtypes.py +16 -6
  8. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/expr.py +4 -4
  9. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/formatting.py +1 -1
  10. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/infix.py +1 -2
  11. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/matrix.py +51 -106
  12. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/base.py +3 -3
  13. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/binary.py +2 -0
  14. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/indexunary.py +2 -0
  15. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/monoid.py +2 -0
  16. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/select.py +2 -0
  17. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/semiring.py +2 -0
  18. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/unary.py +2 -0
  19. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/utils.py +1 -0
  20. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/scalar.py +12 -1
  21. python_graphblas-2025.2.0/graphblas/core/ss/__init__.py +5 -0
  22. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/binary.py +1 -0
  23. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/config.py +1 -1
  24. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/descriptor.py +1 -0
  25. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/indexunary.py +1 -0
  26. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/matrix.py +90 -64
  27. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/select.py +1 -0
  28. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/unary.py +1 -0
  29. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/vector.py +61 -30
  30. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/utils.py +18 -6
  31. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/vector.py +31 -72
  32. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/exceptions.py +11 -3
  33. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/io/__init__.py +0 -1
  34. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/io/_awkward.py +1 -0
  35. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/io/_matrixmarket.py +2 -0
  36. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/io/_networkx.py +6 -2
  37. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/io/_scipy.py +1 -0
  38. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/io/_sparse.py +1 -0
  39. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/monoid/numpy.py +1 -0
  40. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/select/__init__.py +4 -11
  41. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/semiring/numpy.py +1 -0
  42. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/ss/_core.py +2 -4
  43. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/conftest.py +18 -6
  44. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_descriptor.py +1 -2
  45. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_dtype.py +9 -1
  46. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_infix.py +1 -1
  47. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_io.py +1 -12
  48. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_matrix.py +13 -21
  49. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_numpyops.py +10 -0
  50. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_op.py +0 -2
  51. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_scalar.py +5 -1
  52. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_ssjit.py +49 -3
  53. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_vector.py +16 -14
  54. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/unary/numpy.py +1 -0
  55. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/viz.py +2 -0
  56. python_graphblas-2025.2.0/pyproject.toml +499 -0
  57. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/python_graphblas.egg-info/PKG-INFO +58 -19
  58. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/python_graphblas.egg-info/SOURCES.txt +0 -2
  59. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/python_graphblas.egg-info/requires.txt +15 -13
  60. python-graphblas-2023.12.0/graphblas/core/agg.py +0 -17
  61. python-graphblas-2023.12.0/graphblas/core/ss/__init__.py +0 -3
  62. python-graphblas-2023.12.0/graphblas/io/_numpy.py +0 -104
  63. python-graphblas-2023.12.0/pyproject.toml +0 -479
  64. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/LICENSE +0 -0
  65. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/MANIFEST.in +0 -0
  66. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/conftest.py +0 -0
  67. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/__init__.py +0 -0
  68. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/agg/ss.py +0 -0
  69. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/binary/__init__.py +0 -0
  70. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/binary/ss.py +0 -0
  71. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/__init__.py +0 -0
  72. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/descriptor.py +0 -0
  73. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/infixmethods.py +0 -0
  74. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/mask.py +0 -0
  75. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/__init__.py +0 -0
  76. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/operator/agg.py +0 -0
  77. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/recorder.py +0 -0
  78. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/slice.py +0 -0
  79. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/context.py +0 -0
  80. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/dtypes.py +0 -0
  81. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/core/ss/prefix_scan.py +0 -0
  82. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/dtypes/__init__.py +0 -0
  83. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/dtypes/ss.py +0 -0
  84. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/graphblas.yaml +0 -0
  85. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/indexunary/__init__.py +0 -0
  86. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/indexunary/ss.py +0 -0
  87. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/monoid/__init__.py +0 -0
  88. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/monoid/ss.py +0 -0
  89. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/op/__init__.py +0 -0
  90. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/op/numpy.py +0 -0
  91. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/op/ss.py +0 -0
  92. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/select/ss.py +0 -0
  93. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/semiring/__init__.py +0 -0
  94. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/semiring/ss.py +0 -0
  95. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/ss/__init__.py +0 -0
  96. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/__init__.py +0 -0
  97. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/pickle1-vanilla.pkl +0 -0
  98. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/pickle1.pkl +0 -0
  99. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/pickle2-vanilla.pkl +0 -0
  100. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/pickle2.pkl +0 -0
  101. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/pickle3-vanilla.pkl +0 -0
  102. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/pickle3.pkl +0 -0
  103. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_auto_init.py +0 -0
  104. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_core.py +0 -0
  105. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_external_init.py +0 -0
  106. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_formatting.py +0 -0
  107. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_mask.py +0 -0
  108. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_operator_types.py +0 -0
  109. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_pickle.py +0 -0
  110. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_prefix_scan.py +0 -0
  111. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_recorder.py +0 -0
  112. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_resolving.py +0 -0
  113. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/tests/test_ss_utils.py +0 -0
  114. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/unary/__init__.py +0 -0
  115. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/graphblas/unary/ss.py +0 -0
  116. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/python_graphblas.egg-info/dependency_links.txt +0 -0
  117. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/python_graphblas.egg-info/top_level.txt +0 -0
  118. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/setup.cfg +0 -0
  119. {python-graphblas-2023.12.0 → python_graphblas-2025.2.0}/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: 2023.12.0
3
+ Version: 2025.2.0
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.21
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.12"
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.12" and extra == "io"
259
- Requires-Dist: awkward>=1.9; extra == "io"
260
- Requires-Dist: sparse>=0.13; python_version < "3.12" 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.12" 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.12" 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
 
@@ -73,7 +73,8 @@ Custom recipes (specific to SuiteSparse:GraphBLAS):
73
73
  # - bxnor monoid: even bits
74
74
  # - bnor monoid: odd bits
75
75
  """
76
- # All items are dynamically added by classes in core/agg.py
76
+
77
+ # All items are dynamically added by classes in core/operator/agg.py
77
78
  # This module acts as a container of Aggregator instances
78
79
  _deprecated = {}
79
80
 
@@ -5,6 +5,7 @@ See list of numpy ufuncs supported by numpy here:
5
5
  https://numba.readthedocs.io/en/stable/reference/numpysupported.html#math-operations
6
6
 
7
7
  """
8
+
8
9
  import numpy as _np
9
10
 
10
11
  from .. import _STANDARD_OPERATOR_NAMES
@@ -7,6 +7,7 @@ To automatically create the functions, run:
7
7
  $ python -m graphblas.core.automethods
8
8
 
9
9
  """
10
+
10
11
  from .. import config
11
12
 
12
13
 
@@ -281,10 +282,6 @@ def to_edgelist(self):
281
282
  return self._get_value("to_edgelist")
282
283
 
283
284
 
284
- def to_values(self):
285
- return self._get_value("to_values")
286
-
287
-
288
285
  def value(self):
289
286
  return self._get_value("value")
290
287
 
@@ -398,7 +395,6 @@ def _main():
398
395
  "ss",
399
396
  "to_coo",
400
397
  "to_dense",
401
- "to_values",
402
398
  }
403
399
  vector = {
404
400
  "_as_matrix",
@@ -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()
@@ -375,8 +386,7 @@ def lookup_dtype(key, value=None):
375
386
 
376
387
 
377
388
  def unify(type1, type2, *, is_left_scalar=False, is_right_scalar=False):
378
- """
379
- Returns a type that can hold both type1 and type2.
389
+ """Returns a type that can hold both type1 and type2.
380
390
 
381
391
  For example:
382
392
  unify(INT32, INT64) -> INT64
@@ -430,7 +440,7 @@ def _dtype_to_string(dtype):
430
440
  np_type = dtype.np_type
431
441
  s = str(np_type)
432
442
  try:
433
- 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)
434
444
  return s
435
445
  except Exception:
436
446
  pass
@@ -449,5 +459,5 @@ def _string_to_dtype(s):
449
459
  return lookup_dtype(s)
450
460
  except Exception:
451
461
  pass
452
- np_type = np.dtype(np.lib.format.safe_eval(s))
462
+ np_type = np.dtype(literal_eval(s))
453
463
  return lookup_dtype(np_type)
@@ -147,13 +147,13 @@ class IndexerResolver:
147
147
  return self.indices[0]._py_index()
148
148
 
149
149
  def parse_indices(self, indices, shape):
150
- """
151
- Returns
150
+ """Returns
152
151
  -------
153
152
  [(rows, rowsize), (cols, colsize)] for Matrix
154
153
  [(idx, idx_size)] for Vector
155
154
 
156
155
  Within each tuple, if the index is of type int, the size will be None
156
+
157
157
  """
158
158
  if len(shape) == 1:
159
159
  if type(indices) is tuple:
@@ -312,8 +312,8 @@ class AmbiguousAssignOrExtract:
312
312
  Updater(self.parent, opts=opts)._setitem(self.resolved_indexes, expr, is_submask=False)
313
313
 
314
314
  def new(self, dtype=None, *, mask=None, input_mask=None, name=None, **opts):
315
- """
316
- Force extraction of the indexes into a new object
315
+ """Force extraction of the indexes into a new object.
316
+
317
317
  dtype and mask are the only controllable parameters.
318
318
  """
319
319
  if input_mask is not None:
@@ -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))
@@ -236,7 +236,6 @@ class VectorInfixExpr(InfixExprBase):
236
236
  to_coo = wrapdoc(Vector.to_coo)(property(automethods.to_coo))
237
237
  to_dense = wrapdoc(Vector.to_dense)(property(automethods.to_dense))
238
238
  to_dict = wrapdoc(Vector.to_dict)(property(automethods.to_dict))
239
- to_values = wrapdoc(Vector.to_values)(property(automethods.to_values))
240
239
  vxm = wrapdoc(Vector.vxm)(property(automethods.vxm))
241
240
  wait = wrapdoc(Vector.wait)(property(automethods.wait))
242
241
  # These raise exceptions
@@ -317,6 +316,7 @@ class MatrixInfixExpr(InfixExprBase):
317
316
  ndim = 2
318
317
  output_type = MatrixExpression
319
318
  _is_transposed = False
319
+ __networkx_backend__ = "graphblas"
320
320
  __networkx_plugin__ = "graphblas"
321
321
 
322
322
  def __init__(self, left, right):
@@ -396,7 +396,6 @@ class MatrixInfixExpr(InfixExprBase):
396
396
  to_dense = wrapdoc(Matrix.to_dense)(property(automethods.to_dense))
397
397
  to_dicts = wrapdoc(Matrix.to_dicts)(property(automethods.to_dicts))
398
398
  to_edgelist = wrapdoc(Matrix.to_edgelist)(property(automethods.to_edgelist))
399
- to_values = wrapdoc(Matrix.to_values)(property(automethods.to_values))
400
399
  wait = wrapdoc(Matrix.wait)(property(automethods.wait))
401
400
  # These raise exceptions
402
401
  __array__ = Matrix.__array__