python-graphblas 2024.2.0__py3-none-any.whl → 2025.2.0a1__py3-none-any.whl
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.
- graphblas/core/base.py +1 -1
- graphblas/core/dtypes.py +15 -4
- graphblas/core/formatting.py +1 -1
- graphblas/core/infix.py +1 -0
- graphblas/core/matrix.py +8 -3
- graphblas/core/operator/base.py +1 -2
- graphblas/core/scalar.py +1 -1
- graphblas/core/ss/__init__.py +3 -1
- graphblas/core/ss/config.py +1 -1
- graphblas/core/ss/matrix.py +37 -4
- graphblas/core/ss/vector.py +33 -2
- graphblas/core/utils.py +15 -5
- graphblas/core/vector.py +2 -2
- graphblas/exceptions.py +11 -3
- graphblas/io/_networkx.py +4 -2
- graphblas/tests/conftest.py +8 -0
- graphblas/tests/test_dtype.py +4 -0
- graphblas/tests/test_io.py +1 -1
- graphblas/tests/test_matrix.py +13 -10
- graphblas/tests/test_numpyops.py +10 -0
- graphblas/tests/test_scalar.py +5 -1
- graphblas/tests/test_ssjit.py +49 -3
- graphblas/tests/test_vector.py +16 -5
- {python_graphblas-2024.2.0.dist-info → python_graphblas-2025.2.0a1.dist-info}/METADATA +82 -43
- {python_graphblas-2024.2.0.dist-info → python_graphblas-2025.2.0a1.dist-info}/RECORD +28 -28
- {python_graphblas-2024.2.0.dist-info → python_graphblas-2025.2.0a1.dist-info}/WHEEL +1 -1
- {python_graphblas-2024.2.0.dist-info → python_graphblas-2025.2.0a1.dist-info}/LICENSE +0 -0
- {python_graphblas-2024.2.0.dist-info → python_graphblas-2025.2.0a1.dist-info}/top_level.txt +0 -0
graphblas/core/base.py
CHANGED
|
@@ -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."
|
graphblas/core/dtypes.py
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
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(
|
|
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(
|
|
462
|
+
np_type = np.dtype(literal_eval(s))
|
|
452
463
|
return lookup_dtype(np_type)
|
graphblas/core/formatting.py
CHANGED
|
@@ -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))
|
graphblas/core/infix.py
CHANGED
graphblas/core/matrix.py
CHANGED
|
@@ -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):
|
graphblas/core/operator/base.py
CHANGED
|
@@ -251,8 +251,7 @@ class TypedOpBase:
|
|
|
251
251
|
|
|
252
252
|
def __repr__(self):
|
|
253
253
|
classname = self.opclass.lower()
|
|
254
|
-
|
|
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
|
|
graphblas/core/scalar.py
CHANGED
|
@@ -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)
|
graphblas/core/ss/__init__.py
CHANGED
graphblas/core/ss/config.py
CHANGED
|
@@ -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
|
|
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:
|
graphblas/core/ss/matrix.py
CHANGED
|
@@ -58,7 +58,7 @@ def head(matrix, n=10, dtype=None, *, sort=False):
|
|
|
58
58
|
dtype = matrix.dtype
|
|
59
59
|
else:
|
|
60
60
|
dtype = lookup_dtype(dtype)
|
|
61
|
-
rows, cols, vals = zip(*itertools.islice(matrix.ss.iteritems(), n))
|
|
61
|
+
rows, cols, vals = zip(*itertools.islice(matrix.ss.iteritems(), n), strict=True)
|
|
62
62
|
return np.array(rows, np.uint64), np.array(cols, np.uint64), np.array(vals, dtype.np_type)
|
|
63
63
|
|
|
64
64
|
|
|
@@ -3650,8 +3650,10 @@ class ss:
|
|
|
3650
3650
|
def unpack_hyperhash(self, *, compute=False, name=None, **opts):
|
|
3651
3651
|
"""Unpacks the hyper_hash of a hypersparse matrix if possible.
|
|
3652
3652
|
|
|
3653
|
-
Will return None if the matrix is not hypersparse
|
|
3654
|
-
Use ``compute=True`` to compute the hyper_hash
|
|
3653
|
+
Will return None if the matrix is not hypersparse, if the hash is not computed,
|
|
3654
|
+
or if the hash is not needed. Use ``compute=True`` to try to compute the hyper_hash
|
|
3655
|
+
if the input is hypersparse. The hyper_hash is optional in SuiteSparse:GraphBLAS,
|
|
3656
|
+
so it may not be computed even with ``compute=True``.
|
|
3655
3657
|
|
|
3656
3658
|
Use ``pack_hyperhash`` to move a hyper_hash matrix that was previously unpacked
|
|
3657
3659
|
back into a matrix.
|
|
@@ -4079,6 +4081,21 @@ class ss:
|
|
|
4079
4081
|
blob_handle = ffi_new("void**")
|
|
4080
4082
|
blob_size_handle = ffi_new("GrB_Index*")
|
|
4081
4083
|
parent = self._parent
|
|
4084
|
+
if parent.dtype._is_udt and hasattr(lib, "GrB_Type_get_String"):
|
|
4085
|
+
# Get the name from the dtype and set it to the name of the matrix so we can
|
|
4086
|
+
# recreate the UDT. This is a bit hacky and we should restore the original name.
|
|
4087
|
+
# First get the size of name.
|
|
4088
|
+
dtype_size = ffi_new("size_t*")
|
|
4089
|
+
status = lib.GrB_Type_get_SIZE(parent.dtype.gb_obj[0], dtype_size, lib.GrB_NAME)
|
|
4090
|
+
check_status_carg(status, "Type", parent.dtype.gb_obj[0])
|
|
4091
|
+
# Then get the name
|
|
4092
|
+
dtype_char = ffi_new(f"char[{dtype_size[0]}]")
|
|
4093
|
+
status = lib.GrB_Type_get_String(parent.dtype.gb_obj[0], dtype_char, lib.GrB_NAME)
|
|
4094
|
+
check_status_carg(status, "Type", parent.dtype.gb_obj[0])
|
|
4095
|
+
# Then set the name
|
|
4096
|
+
status = lib.GrB_Matrix_set_String(parent._carg, dtype_char, lib.GrB_NAME)
|
|
4097
|
+
check_status_carg(status, "Matrix", parent._carg)
|
|
4098
|
+
|
|
4082
4099
|
check_status(
|
|
4083
4100
|
lib.GxB_Matrix_serialize(
|
|
4084
4101
|
blob_handle,
|
|
@@ -4120,8 +4137,8 @@ class ss:
|
|
|
4120
4137
|
else:
|
|
4121
4138
|
data = np.frombuffer(data, np.uint8)
|
|
4122
4139
|
data_obj = ffi.from_buffer("void*", data)
|
|
4123
|
-
# Get the dtype name first
|
|
4124
4140
|
if dtype is None:
|
|
4141
|
+
# Get the dtype name first (for non-UDTs)
|
|
4125
4142
|
cname = ffi_new(f"char[{lib.GxB_MAX_NAME_LEN}]")
|
|
4126
4143
|
info = lib.GxB_deserialize_type_name(
|
|
4127
4144
|
cname,
|
|
@@ -4131,6 +4148,22 @@ class ss:
|
|
|
4131
4148
|
if info != lib.GrB_SUCCESS:
|
|
4132
4149
|
raise _error_code_lookup[info]("Matrix deserialize failed to get the dtype name")
|
|
4133
4150
|
dtype_name = b"".join(itertools.takewhile(b"\x00".__ne__, cname)).decode()
|
|
4151
|
+
if not dtype_name and hasattr(lib, "GxB_Serialized_get_String"):
|
|
4152
|
+
# Handle UDTs. First get the size of name
|
|
4153
|
+
dtype_size = ffi_new("size_t*")
|
|
4154
|
+
info = lib.GxB_Serialized_get_SIZE(data_obj, dtype_size, lib.GrB_NAME, data.nbytes)
|
|
4155
|
+
if info != lib.GrB_SUCCESS:
|
|
4156
|
+
raise _error_code_lookup[info](
|
|
4157
|
+
"Matrix deserialize failed to get the size of name"
|
|
4158
|
+
)
|
|
4159
|
+
# Then get the name
|
|
4160
|
+
dtype_char = ffi_new(f"char[{dtype_size[0]}]")
|
|
4161
|
+
info = lib.GxB_Serialized_get_String(
|
|
4162
|
+
data_obj, dtype_char, lib.GrB_NAME, data.nbytes
|
|
4163
|
+
)
|
|
4164
|
+
if info != lib.GrB_SUCCESS:
|
|
4165
|
+
raise _error_code_lookup[info]("Matrix deserialize failed to get the name")
|
|
4166
|
+
dtype_name = ffi.string(dtype_char).decode()
|
|
4134
4167
|
dtype = _string_to_dtype(dtype_name)
|
|
4135
4168
|
else:
|
|
4136
4169
|
dtype = lookup_dtype(dtype)
|
graphblas/core/ss/vector.py
CHANGED
|
@@ -43,7 +43,7 @@ def head(vector, n=10, dtype=None, *, sort=False):
|
|
|
43
43
|
dtype = vector.dtype
|
|
44
44
|
else:
|
|
45
45
|
dtype = lookup_dtype(dtype)
|
|
46
|
-
indices, vals = zip(*itertools.islice(vector.ss.iteritems(), n))
|
|
46
|
+
indices, vals = zip(*itertools.islice(vector.ss.iteritems(), n), strict=True)
|
|
47
47
|
return np.array(indices, np.uint64), np.array(vals, dtype.np_type)
|
|
48
48
|
|
|
49
49
|
|
|
@@ -1652,6 +1652,21 @@ class ss:
|
|
|
1652
1652
|
blob_handle = ffi_new("void**")
|
|
1653
1653
|
blob_size_handle = ffi_new("GrB_Index*")
|
|
1654
1654
|
parent = self._parent
|
|
1655
|
+
if parent.dtype._is_udt and hasattr(lib, "GrB_Type_get_String"):
|
|
1656
|
+
# Get the name from the dtype and set it to the name of the vector so we can
|
|
1657
|
+
# recreate the UDT. This is a bit hacky and we should restore the original name.
|
|
1658
|
+
# First get the size of name.
|
|
1659
|
+
dtype_size = ffi_new("size_t*")
|
|
1660
|
+
status = lib.GrB_Type_get_SIZE(parent.dtype.gb_obj[0], dtype_size, lib.GrB_NAME)
|
|
1661
|
+
check_status_carg(status, "Type", parent.dtype.gb_obj[0])
|
|
1662
|
+
# Then get the name
|
|
1663
|
+
dtype_char = ffi_new(f"char[{dtype_size[0]}]")
|
|
1664
|
+
status = lib.GrB_Type_get_String(parent.dtype.gb_obj[0], dtype_char, lib.GrB_NAME)
|
|
1665
|
+
check_status_carg(status, "Type", parent.dtype.gb_obj[0])
|
|
1666
|
+
# Then set the name
|
|
1667
|
+
status = lib.GrB_Vector_set_String(parent._carg, dtype_char, lib.GrB_NAME)
|
|
1668
|
+
check_status_carg(status, "Vector", parent._carg)
|
|
1669
|
+
|
|
1655
1670
|
check_status(
|
|
1656
1671
|
lib.GxB_Vector_serialize(
|
|
1657
1672
|
blob_handle,
|
|
@@ -1694,7 +1709,7 @@ class ss:
|
|
|
1694
1709
|
data = np.frombuffer(data, np.uint8)
|
|
1695
1710
|
data_obj = ffi.from_buffer("void*", data)
|
|
1696
1711
|
if dtype is None:
|
|
1697
|
-
# Get the dtype name first
|
|
1712
|
+
# Get the dtype name first (for non-UDTs)
|
|
1698
1713
|
cname = ffi_new(f"char[{lib.GxB_MAX_NAME_LEN}]")
|
|
1699
1714
|
info = lib.GxB_deserialize_type_name(
|
|
1700
1715
|
cname,
|
|
@@ -1704,6 +1719,22 @@ class ss:
|
|
|
1704
1719
|
if info != lib.GrB_SUCCESS:
|
|
1705
1720
|
raise _error_code_lookup[info]("Vector deserialize failed to get the dtype name")
|
|
1706
1721
|
dtype_name = b"".join(itertools.takewhile(b"\x00".__ne__, cname)).decode()
|
|
1722
|
+
if not dtype_name and hasattr(lib, "GxB_Serialized_get_String"):
|
|
1723
|
+
# Handle UDTs. First get the size of name
|
|
1724
|
+
dtype_size = ffi_new("size_t*")
|
|
1725
|
+
info = lib.GxB_Serialized_get_SIZE(data_obj, dtype_size, lib.GrB_NAME, data.nbytes)
|
|
1726
|
+
if info != lib.GrB_SUCCESS:
|
|
1727
|
+
raise _error_code_lookup[info](
|
|
1728
|
+
"Vector deserialize failed to get the size of name"
|
|
1729
|
+
)
|
|
1730
|
+
# Then get the name
|
|
1731
|
+
dtype_char = ffi_new(f"char[{dtype_size[0]}]")
|
|
1732
|
+
info = lib.GxB_Serialized_get_String(
|
|
1733
|
+
data_obj, dtype_char, lib.GrB_NAME, data.nbytes
|
|
1734
|
+
)
|
|
1735
|
+
if info != lib.GrB_SUCCESS:
|
|
1736
|
+
raise _error_code_lookup[info]("Vector deserialize failed to get the name")
|
|
1737
|
+
dtype_name = ffi.string(dtype_char).decode()
|
|
1707
1738
|
dtype = _string_to_dtype(dtype_name)
|
|
1708
1739
|
else:
|
|
1709
1740
|
dtype = lookup_dtype(dtype)
|
graphblas/core/utils.py
CHANGED
|
@@ -5,6 +5,8 @@ import numpy as np
|
|
|
5
5
|
from ..dtypes import _INDEX, lookup_dtype
|
|
6
6
|
from . import ffi, lib
|
|
7
7
|
|
|
8
|
+
_NP2 = np.__version__.startswith("2.")
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
def libget(name):
|
|
10
12
|
"""Helper to get items from GraphBLAS which might be GrB or GxB."""
|
|
@@ -60,7 +62,8 @@ def ints_to_numpy_buffer(array, dtype, *, name="array", copy=False, ownable=Fals
|
|
|
60
62
|
and not np.issubdtype(array.dtype, np.bool_)
|
|
61
63
|
):
|
|
62
64
|
raise ValueError(f"{name} must be integers, not {array.dtype.name}")
|
|
63
|
-
|
|
65
|
+
# https://numpy.org/doc/stable/release/2.0.0-notes.html#new-copy-keyword-meaning-for-array-and-asarray-constructors
|
|
66
|
+
array = np.array(array, dtype, copy=copy or _NP2 and None, order=order)
|
|
64
67
|
if ownable and (not array.flags.owndata or not array.flags.writeable):
|
|
65
68
|
array = array.copy(order)
|
|
66
69
|
return array
|
|
@@ -90,10 +93,14 @@ def values_to_numpy_buffer(
|
|
|
90
93
|
"""
|
|
91
94
|
if dtype is not None:
|
|
92
95
|
dtype = lookup_dtype(dtype)
|
|
93
|
-
|
|
96
|
+
# https://numpy.org/doc/stable/release/2.0.0-notes.html#new-copy-keyword-meaning-for-array-and-asarray-constructors
|
|
97
|
+
array = np.array(
|
|
98
|
+
array, _get_subdtype(dtype.np_type), copy=copy or _NP2 and None, order=order
|
|
99
|
+
)
|
|
94
100
|
else:
|
|
95
101
|
is_input_np = isinstance(array, np.ndarray)
|
|
96
|
-
|
|
102
|
+
# https://numpy.org/doc/stable/release/2.0.0-notes.html#new-copy-keyword-meaning-for-array-and-asarray-constructors
|
|
103
|
+
array = np.array(array, copy=copy or _NP2 and None, order=order)
|
|
97
104
|
if array.dtype.hasobject:
|
|
98
105
|
raise ValueError("object dtype for values is not allowed")
|
|
99
106
|
if not is_input_np and array.dtype == np.int32: # pragma: no cover
|
|
@@ -202,7 +209,7 @@ def normalize_chunks(chunks, shape):
|
|
|
202
209
|
f"chunks argument must be of length {len(shape)} (one for each dimension of a {typ})"
|
|
203
210
|
)
|
|
204
211
|
chunksizes = []
|
|
205
|
-
for size, chunk in zip(shape, chunks):
|
|
212
|
+
for size, chunk in zip(shape, chunks, strict=True):
|
|
206
213
|
if chunk is None:
|
|
207
214
|
cur_chunks = [size]
|
|
208
215
|
elif (c := maybe_integral(chunk)) is not None:
|
|
@@ -312,7 +319,10 @@ class _CArray:
|
|
|
312
319
|
if size is not None:
|
|
313
320
|
self.array = np.empty(size, dtype=dtype.np_type)
|
|
314
321
|
else:
|
|
315
|
-
|
|
322
|
+
# https://numpy.org/doc/stable/release/2.0.0-notes.html#new-copy-keyword-meaning-for-array-and-asarray-constructors
|
|
323
|
+
self.array = np.array(
|
|
324
|
+
array, dtype=_get_subdtype(dtype.np_type), copy=_NP2 and None, order="C"
|
|
325
|
+
)
|
|
316
326
|
c_type = dtype.c_type if dtype._is_udt else f"{dtype.c_type}*"
|
|
317
327
|
self._carg = ffi.cast(c_type, ffi.from_buffer(self.array))
|
|
318
328
|
self.dtype = dtype
|
graphblas/core/vector.py
CHANGED
|
@@ -772,7 +772,7 @@ class Vector(BaseType):
|
|
|
772
772
|
"""
|
|
773
773
|
if isinstance(pairs, np.ndarray):
|
|
774
774
|
raise TypeError("pairs as NumPy array is not supported; use `Vector.from_coo` instead")
|
|
775
|
-
unzipped = list(zip(*pairs))
|
|
775
|
+
unzipped = list(zip(*pairs, strict=True))
|
|
776
776
|
if len(unzipped) == 2:
|
|
777
777
|
indices, values = unzipped
|
|
778
778
|
elif not unzipped:
|
|
@@ -2105,7 +2105,7 @@ class Vector(BaseType):
|
|
|
2105
2105
|
|
|
2106
2106
|
"""
|
|
2107
2107
|
indices, values = self.to_coo(sort=False)
|
|
2108
|
-
return dict(zip(indices.tolist(), values.tolist()))
|
|
2108
|
+
return dict(zip(indices.tolist(), values.tolist(), strict=True))
|
|
2109
2109
|
|
|
2110
2110
|
|
|
2111
2111
|
if backend == "suitesparse":
|
graphblas/exceptions.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from . import backend as _backend
|
|
2
1
|
from .core import ffi as _ffi
|
|
3
2
|
from .core import lib as _lib
|
|
4
3
|
from .core.utils import _Pointer
|
|
@@ -85,9 +84,14 @@ class NotImplementedException(GraphblasException):
|
|
|
85
84
|
"""
|
|
86
85
|
|
|
87
86
|
|
|
87
|
+
# SuiteSparse errors
|
|
88
|
+
class JitError(GraphblasException):
|
|
89
|
+
"""SuiteSparse:GraphBLAS error using JIT."""
|
|
90
|
+
|
|
91
|
+
|
|
88
92
|
# Our errors
|
|
89
93
|
class UdfParseError(GraphblasException):
|
|
90
|
-
"""
|
|
94
|
+
"""SuiteSparse:GraphBLAS unable to parse the user-defined function."""
|
|
91
95
|
|
|
92
96
|
|
|
93
97
|
_error_code_lookup = {
|
|
@@ -112,8 +116,12 @@ _error_code_lookup = {
|
|
|
112
116
|
}
|
|
113
117
|
GrB_SUCCESS = _lib.GrB_SUCCESS
|
|
114
118
|
GrB_NO_VALUE = _lib.GrB_NO_VALUE
|
|
115
|
-
|
|
119
|
+
|
|
120
|
+
# SuiteSparse-specific errors
|
|
121
|
+
if hasattr(_lib, "GxB_EXHAUSTED"):
|
|
116
122
|
_error_code_lookup[_lib.GxB_EXHAUSTED] = StopIteration
|
|
123
|
+
if hasattr(_lib, "GxB_JIT_ERROR"): # Added in 9.4
|
|
124
|
+
_error_code_lookup[_lib.GxB_JIT_ERROR] = JitError
|
|
117
125
|
|
|
118
126
|
|
|
119
127
|
def check_status(response_code, args):
|
graphblas/io/_networkx.py
CHANGED
|
@@ -55,7 +55,9 @@ def to_networkx(m, edge_attribute="weight"):
|
|
|
55
55
|
cols = cols.tolist()
|
|
56
56
|
G = nx.DiGraph()
|
|
57
57
|
if edge_attribute is None:
|
|
58
|
-
G.add_edges_from(zip(rows, cols))
|
|
58
|
+
G.add_edges_from(zip(rows, cols, strict=True))
|
|
59
59
|
else:
|
|
60
|
-
G.add_weighted_edges_from(
|
|
60
|
+
G.add_weighted_edges_from(
|
|
61
|
+
zip(rows, cols, vals.tolist(), strict=True), weight=edge_attribute
|
|
62
|
+
)
|
|
61
63
|
return G
|
graphblas/tests/conftest.py
CHANGED
|
@@ -3,6 +3,7 @@ import contextlib
|
|
|
3
3
|
import functools
|
|
4
4
|
import itertools
|
|
5
5
|
import platform
|
|
6
|
+
import sys
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
@@ -156,3 +157,10 @@ def compute(x):
|
|
|
156
157
|
def shouldhave(module, opname):
|
|
157
158
|
"""Whether an "operator" module should have the given operator."""
|
|
158
159
|
return supports_udfs or hasattr(module, opname)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def dprint(*args, **kwargs): # pragma: no cover (debug)
|
|
163
|
+
"""Print to stderr for debugging purposes."""
|
|
164
|
+
kwargs["file"] = sys.stderr
|
|
165
|
+
kwargs["flush"] = True
|
|
166
|
+
print(*args, **kwargs)
|
graphblas/tests/test_dtype.py
CHANGED
|
@@ -224,6 +224,10 @@ def test_record_dtype_from_dict():
|
|
|
224
224
|
def test_dtype_to_from_string():
|
|
225
225
|
types = [dtypes.BOOL, dtypes.FP64]
|
|
226
226
|
for c in string.ascii_letters:
|
|
227
|
+
if c == "T":
|
|
228
|
+
# See NEP 55 about StringDtype "T". Notably, this doesn't work:
|
|
229
|
+
# >>> np.dtype(np.dtype("T").str)
|
|
230
|
+
continue
|
|
227
231
|
try:
|
|
228
232
|
dtype = np.dtype(c)
|
|
229
233
|
types.append(dtype)
|
graphblas/tests/test_io.py
CHANGED
|
@@ -146,7 +146,7 @@ def test_matrix_to_from_networkx():
|
|
|
146
146
|
M = gb.io.from_networkx(G, nodelist=range(7))
|
|
147
147
|
if suitesparse:
|
|
148
148
|
assert M.ss.is_iso
|
|
149
|
-
rows, cols = zip(*edges)
|
|
149
|
+
rows, cols = zip(*edges, strict=True)
|
|
150
150
|
expected = gb.Matrix.from_coo(rows, cols, 1)
|
|
151
151
|
assert expected.isequal(M)
|
|
152
152
|
# Test empty
|
graphblas/tests/test_matrix.py
CHANGED
|
@@ -2603,12 +2603,14 @@ def test_iter(A):
|
|
|
2603
2603
|
zip(
|
|
2604
2604
|
[3, 0, 3, 5, 6, 0, 6, 1, 6, 2, 4, 1],
|
|
2605
2605
|
[0, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6],
|
|
2606
|
+
strict=True,
|
|
2606
2607
|
)
|
|
2607
2608
|
)
|
|
2608
2609
|
assert set(A.T) == set(
|
|
2609
2610
|
zip(
|
|
2610
2611
|
[0, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6],
|
|
2611
2612
|
[3, 0, 3, 5, 6, 0, 6, 1, 6, 2, 4, 1],
|
|
2613
|
+
strict=True,
|
|
2612
2614
|
)
|
|
2613
2615
|
)
|
|
2614
2616
|
|
|
@@ -2731,8 +2733,8 @@ def test_ss_split(A):
|
|
|
2731
2733
|
for results in [A.ss.split([4, 3]), A.ss.split([[4, None], 3], name="split")]:
|
|
2732
2734
|
row_boundaries = [0, 4, 7]
|
|
2733
2735
|
col_boundaries = [0, 3, 6, 7]
|
|
2734
|
-
for i, (i1, i2) in enumerate(
|
|
2735
|
-
for j, (j1, j2) in enumerate(
|
|
2736
|
+
for i, (i1, i2) in enumerate(itertools.pairwise(row_boundaries)):
|
|
2737
|
+
for j, (j1, j2) in enumerate(itertools.pairwise(col_boundaries)):
|
|
2736
2738
|
expected = A[i1:i2, j1:j2].new()
|
|
2737
2739
|
assert expected.isequal(results[i][j])
|
|
2738
2740
|
with pytest.raises(DimensionMismatch):
|
|
@@ -3068,7 +3070,7 @@ def test_ss_flatten(A):
|
|
|
3068
3070
|
[3, 2, 3, 1, 5, 3, 7, 8, 3, 1, 7, 4],
|
|
3069
3071
|
]
|
|
3070
3072
|
# row-wise
|
|
3071
|
-
indices = [row * A.ncols + col for row, col in zip(data[0], data[1])]
|
|
3073
|
+
indices = [row * A.ncols + col for row, col in zip(data[0], data[1], strict=True)]
|
|
3072
3074
|
expected = Vector.from_coo(indices, data[2], size=A.nrows * A.ncols)
|
|
3073
3075
|
for fmt in ["csr", "hypercsr", "bitmapr"]:
|
|
3074
3076
|
B = Matrix.ss.import_any(**A.ss.export(format=fmt))
|
|
@@ -3087,7 +3089,7 @@ def test_ss_flatten(A):
|
|
|
3087
3089
|
assert C.isequal(B)
|
|
3088
3090
|
|
|
3089
3091
|
# column-wise
|
|
3090
|
-
indices = [col * A.nrows + row for row, col in zip(data[0], data[1])]
|
|
3092
|
+
indices = [col * A.nrows + row for row, col in zip(data[0], data[1], strict=True)]
|
|
3091
3093
|
expected = Vector.from_coo(indices, data[2], size=A.nrows * A.ncols)
|
|
3092
3094
|
for fmt in ["csc", "hypercsc", "bitmapc"]:
|
|
3093
3095
|
B = Matrix.ss.import_any(**A.ss.export(format=fmt))
|
|
@@ -3626,9 +3628,9 @@ def test_ss_iteration(A):
|
|
|
3626
3628
|
assert not list(B.ss.itervalues())
|
|
3627
3629
|
assert not list(B.ss.iteritems())
|
|
3628
3630
|
rows, columns, values = A.to_coo()
|
|
3629
|
-
assert sorted(zip(rows, columns)) == sorted(A.ss.iterkeys())
|
|
3631
|
+
assert sorted(zip(rows, columns, strict=True)) == sorted(A.ss.iterkeys())
|
|
3630
3632
|
assert sorted(values) == sorted(A.ss.itervalues())
|
|
3631
|
-
assert sorted(zip(rows, columns, values)) == sorted(A.ss.iteritems())
|
|
3633
|
+
assert sorted(zip(rows, columns, values, strict=True)) == sorted(A.ss.iteritems())
|
|
3632
3634
|
N = rows.size
|
|
3633
3635
|
|
|
3634
3636
|
A = Matrix.ss.import_bitmapr(**A.ss.export("bitmapr"))
|
|
@@ -4072,10 +4074,11 @@ def test_ss_pack_hyperhash(A):
|
|
|
4072
4074
|
Y = C.ss.unpack_hyperhash()
|
|
4073
4075
|
Y = C.ss.unpack_hyperhash(compute=True)
|
|
4074
4076
|
assert C.ss.unpack_hyperhash() is None
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4077
|
+
if Y is not None: # hyperhash may or may not be computed
|
|
4078
|
+
assert Y.nrows == C.nrows
|
|
4079
|
+
C.ss.pack_hyperhash(Y)
|
|
4080
|
+
assert Y.gb_obj[0] == gb.core.NULL
|
|
4081
|
+
assert C.ss.unpack_hyperhash() is not None # May or may not be computed
|
|
4079
4082
|
|
|
4080
4083
|
|
|
4081
4084
|
def test_to_dicts_from_dicts(A):
|
graphblas/tests/test_numpyops.py
CHANGED
|
@@ -5,6 +5,7 @@ import sys
|
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import pytest
|
|
8
|
+
from packaging.version import parse
|
|
8
9
|
|
|
9
10
|
import graphblas as gb
|
|
10
11
|
import graphblas.binary.numpy as npbinary
|
|
@@ -112,6 +113,15 @@ def test_npunary():
|
|
|
112
113
|
match(accum=gb.binary.lor) << gb_result.apply(npunary.isnan)
|
|
113
114
|
compare = match.reduce(gb.monoid.land).new()
|
|
114
115
|
if not compare: # pragma: no cover (debug)
|
|
116
|
+
import numba
|
|
117
|
+
|
|
118
|
+
if (
|
|
119
|
+
unary_name in {"sign"}
|
|
120
|
+
and np.__version__.startswith("2.")
|
|
121
|
+
and parse(numba.__version__) < parse("0.61.0")
|
|
122
|
+
):
|
|
123
|
+
# numba <0.61.0 does not match numpy 2.0
|
|
124
|
+
continue
|
|
115
125
|
print(unary_name, gb_input.dtype)
|
|
116
126
|
print(compute(gb_result))
|
|
117
127
|
print(np_result)
|
graphblas/tests/test_scalar.py
CHANGED
|
@@ -50,7 +50,7 @@ def test_dup(s):
|
|
|
50
50
|
s_empty = Scalar(dtypes.FP64)
|
|
51
51
|
s_unempty = Scalar.from_value(0.0)
|
|
52
52
|
if s_empty.is_cscalar:
|
|
53
|
-
# NumPy wraps around
|
|
53
|
+
# NumPy <2 wraps around; >=2 raises OverflowError
|
|
54
54
|
uint_data = [
|
|
55
55
|
("UINT8", 2**8 - 2),
|
|
56
56
|
("UINT16", 2**16 - 2),
|
|
@@ -73,6 +73,10 @@ def test_dup(s):
|
|
|
73
73
|
("FP32", -2.5),
|
|
74
74
|
*uint_data,
|
|
75
75
|
]:
|
|
76
|
+
if dtype.startswith("UINT") and s_empty.is_cscalar and not np.__version__.startswith("1."):
|
|
77
|
+
with pytest.raises(OverflowError, match="out of bounds for uint"):
|
|
78
|
+
s4.dup(dtype=dtype, name="s5")
|
|
79
|
+
continue
|
|
76
80
|
s5 = s4.dup(dtype=dtype, name="s5")
|
|
77
81
|
assert s5.dtype == dtype
|
|
78
82
|
assert s5.value == val
|
graphblas/tests/test_ssjit.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import pathlib
|
|
3
|
+
import platform
|
|
3
4
|
import sys
|
|
5
|
+
import sysconfig
|
|
4
6
|
|
|
5
7
|
import numpy as np
|
|
6
8
|
import pytest
|
|
@@ -26,11 +28,48 @@ if backend != "suitesparse":
|
|
|
26
28
|
|
|
27
29
|
@pytest.fixture(scope="module", autouse=True)
|
|
28
30
|
def _setup_jit():
|
|
31
|
+
"""Set up the SuiteSparse:GraphBLAS JIT."""
|
|
32
|
+
if _IS_SSGB7:
|
|
33
|
+
# SuiteSparse JIT was added in SSGB 8
|
|
34
|
+
yield
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
if not os.environ.get("GITHUB_ACTIONS"):
|
|
38
|
+
# Try to run the tests with defaults from sysconfig if not running in CI
|
|
39
|
+
prev = gb.ss.config["jit_c_control"]
|
|
40
|
+
cc = sysconfig.get_config_var("CC")
|
|
41
|
+
cflags = sysconfig.get_config_var("CFLAGS")
|
|
42
|
+
include = sysconfig.get_path("include")
|
|
43
|
+
libs = sysconfig.get_config_var("LIBS")
|
|
44
|
+
if not (cc is None or cflags is None or include is None or libs is None):
|
|
45
|
+
gb.ss.config["jit_c_control"] = "on"
|
|
46
|
+
gb.ss.config["jit_c_compiler_name"] = cc
|
|
47
|
+
gb.ss.config["jit_c_compiler_flags"] = f"{cflags} -I{include}"
|
|
48
|
+
gb.ss.config["jit_c_libraries"] = libs
|
|
49
|
+
else:
|
|
50
|
+
# Should we skip or try to run if sysconfig vars aren't set?
|
|
51
|
+
gb.ss.config["jit_c_control"] = "on" # "off"
|
|
52
|
+
try:
|
|
53
|
+
yield
|
|
54
|
+
finally:
|
|
55
|
+
gb.ss.config["jit_c_control"] = prev
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
sys.platform == "darwin"
|
|
60
|
+
or sys.platform == "linux"
|
|
61
|
+
and "conda" not in gb.ss.config["jit_c_compiler_name"]
|
|
62
|
+
):
|
|
63
|
+
# XXX TODO: tests for SuiteSparse JIT are not passing on linux when using wheels or on osx
|
|
64
|
+
# This should be understood and fixed!
|
|
65
|
+
gb.ss.config["jit_c_control"] = "off"
|
|
66
|
+
yield
|
|
67
|
+
return
|
|
68
|
+
|
|
29
69
|
# Configuration values below were obtained from the output of the JIT config
|
|
30
70
|
# in CI, but with paths changed to use `{conda_prefix}` where appropriate.
|
|
31
|
-
if "CONDA_PREFIX" not in os.environ or _IS_SSGB7:
|
|
32
|
-
return
|
|
33
71
|
conda_prefix = os.environ["CONDA_PREFIX"]
|
|
72
|
+
prev = gb.ss.config["jit_c_control"]
|
|
34
73
|
gb.ss.config["jit_c_control"] = "on"
|
|
35
74
|
if sys.platform == "linux":
|
|
36
75
|
gb.ss.config["jit_c_compiler_name"] = f"{conda_prefix}/bin/x86_64-conda-linux-gnu-cc"
|
|
@@ -59,7 +98,7 @@ def _setup_jit():
|
|
|
59
98
|
gb.ss.config["jit_c_compiler_flags"] = (
|
|
60
99
|
"-march=core2 -mtune=haswell -mssse3 -ftree-vectorize -fPIC -fPIE "
|
|
61
100
|
f"-fstack-protector-strong -O2 -pipe -isystem {conda_prefix}/include -DGBNCPUFEAT "
|
|
62
|
-
"-Wno-pointer-sign -O3 -DNDEBUG -fopenmp=libomp -fPIC -arch
|
|
101
|
+
f"-Wno-pointer-sign -O3 -DNDEBUG -fopenmp=libomp -fPIC -arch {platform.machine()}"
|
|
63
102
|
)
|
|
64
103
|
gb.ss.config["jit_c_linker_flags"] = (
|
|
65
104
|
"-Wl,-pie -Wl,-headerpad_max_install_names -Wl,-dead_strip_dylibs "
|
|
@@ -72,6 +111,7 @@ def _setup_jit():
|
|
|
72
111
|
# This probably means we're testing a `python-suitesparse-graphblas` wheel
|
|
73
112
|
# in a conda environment. This is not yet working.
|
|
74
113
|
gb.ss.config["jit_c_control"] = "off"
|
|
114
|
+
yield
|
|
75
115
|
return
|
|
76
116
|
|
|
77
117
|
gb.ss.config["jit_c_compiler_name"] = f"{conda_prefix}/bin/cc"
|
|
@@ -86,6 +126,12 @@ def _setup_jit():
|
|
|
86
126
|
if not pathlib.Path(gb.ss.config["jit_c_compiler_name"]).exists():
|
|
87
127
|
# Can't use the JIT if we don't have a compiler!
|
|
88
128
|
gb.ss.config["jit_c_control"] = "off"
|
|
129
|
+
yield
|
|
130
|
+
return
|
|
131
|
+
try:
|
|
132
|
+
yield
|
|
133
|
+
finally:
|
|
134
|
+
gb.ss.config["jit_c_control"] = prev
|
|
89
135
|
|
|
90
136
|
|
|
91
137
|
@pytest.fixture
|
graphblas/tests/test_vector.py
CHANGED
|
@@ -29,6 +29,8 @@ from graphblas import Matrix, Scalar, Vector # isort:skip (for dask-graphblas)
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
suitesparse = backend == "suitesparse"
|
|
32
|
+
if suitesparse:
|
|
33
|
+
ss_version_major = gb.core.ss.version_major
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
@pytest.fixture
|
|
@@ -2205,7 +2207,10 @@ def test_udt():
|
|
|
2205
2207
|
|
|
2206
2208
|
long_dtype = np.dtype([("x", np.bool_), ("y" * 1000, np.float64)], align=True)
|
|
2207
2209
|
if suitesparse:
|
|
2208
|
-
|
|
2210
|
+
if ss_version_major < 9:
|
|
2211
|
+
with pytest.warns(UserWarning, match="too large"):
|
|
2212
|
+
long_udt = dtypes.register_anonymous(long_dtype)
|
|
2213
|
+
else:
|
|
2209
2214
|
long_udt = dtypes.register_anonymous(long_dtype)
|
|
2210
2215
|
else:
|
|
2211
2216
|
# UDTs don't currently have a name in vanilla GraphBLAS
|
|
@@ -2216,13 +2221,19 @@ def test_udt():
|
|
|
2216
2221
|
if suitesparse:
|
|
2217
2222
|
vv = Vector.ss.deserialize(v.ss.serialize(), dtype=long_udt)
|
|
2218
2223
|
assert v.isequal(vv, check_dtype=True)
|
|
2219
|
-
|
|
2220
|
-
|
|
2224
|
+
if ss_version_major < 9:
|
|
2225
|
+
with pytest.raises(SyntaxError):
|
|
2226
|
+
# The size of the UDT name is limited
|
|
2227
|
+
Vector.ss.deserialize(v.ss.serialize())
|
|
2228
|
+
else:
|
|
2221
2229
|
Vector.ss.deserialize(v.ss.serialize())
|
|
2222
2230
|
# May be able to look up non-anonymous dtypes by name if their names are too long
|
|
2223
2231
|
named_long_dtype = np.dtype([("x", np.bool_), ("y" * 1000, np.float64)], align=False)
|
|
2224
2232
|
if suitesparse:
|
|
2225
|
-
|
|
2233
|
+
if ss_version_major < 9:
|
|
2234
|
+
with pytest.warns(UserWarning, match="too large"):
|
|
2235
|
+
named_long_udt = dtypes.register_new("LongUDT", named_long_dtype)
|
|
2236
|
+
else:
|
|
2226
2237
|
named_long_udt = dtypes.register_new("LongUDT", named_long_dtype)
|
|
2227
2238
|
else:
|
|
2228
2239
|
named_long_udt = dtypes.register_new("LongUDT", named_long_dtype)
|
|
@@ -2270,7 +2281,7 @@ def test_ss_iteration(v):
|
|
|
2270
2281
|
# This is what I would expect
|
|
2271
2282
|
assert sorted(indices) == sorted(v.ss.iterkeys())
|
|
2272
2283
|
assert sorted(values) == sorted(v.ss.itervalues())
|
|
2273
|
-
assert sorted(zip(indices, values)) == sorted(v.ss.iteritems())
|
|
2284
|
+
assert sorted(zip(indices, values, strict=True)) == sorted(v.ss.iteritems())
|
|
2274
2285
|
|
|
2275
2286
|
N = indices.size
|
|
2276
2287
|
v = Vector.ss.import_bitmap(**v.ss.export("bitmap"))
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: python-graphblas
|
|
3
|
-
Version:
|
|
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>
|
|
7
7
|
Maintainer-email: Erik Welch <erik.n.welch@gmail.com>, Jim Kitchen <jim22k@gmail.com>, Sultan Orazbayev <contact@econpoint.com>
|
|
8
|
-
License:
|
|
8
|
+
License: Apache License
|
|
9
9
|
Version 2.0, January 2004
|
|
10
10
|
https://www.apache.org/licenses/
|
|
11
11
|
|
|
@@ -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,52 +231,52 @@ 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.
|
|
234
|
+
Requires-Python: >=3.10
|
|
235
235
|
Description-Content-Type: text/markdown
|
|
236
236
|
License-File: LICENSE
|
|
237
|
-
Requires-Dist: numpy
|
|
238
|
-
Requires-Dist: donfig
|
|
239
|
-
Requires-Dist: pyyaml
|
|
240
|
-
Requires-Dist: suitesparse-graphblas
|
|
241
|
-
Requires-Dist: numba
|
|
242
|
-
Provides-Extra:
|
|
243
|
-
Requires-Dist:
|
|
244
|
-
Requires-Dist: python-graphblas[datashade] ; (python_version < "3.13") and extra == 'all'
|
|
245
|
-
Provides-Extra: datashade
|
|
246
|
-
Requires-Dist: python-graphblas[numba,pandas,scipy] ; extra == 'datashade'
|
|
247
|
-
Requires-Dist: datashader >=0.12 ; extra == 'datashade'
|
|
248
|
-
Requires-Dist: hvplot >=0.7 ; extra == 'datashade'
|
|
249
|
-
Provides-Extra: default
|
|
250
|
-
Requires-Dist: python-graphblas[pandas,scipy,suitesparse] ; extra == 'default'
|
|
251
|
-
Requires-Dist: python-graphblas[numba] ; (python_version < "3.13") and extra == 'default'
|
|
252
|
-
Provides-Extra: io
|
|
253
|
-
Requires-Dist: python-graphblas[networkx,scipy] ; extra == 'io'
|
|
254
|
-
Requires-Dist: awkward >=1.9 ; extra == 'io'
|
|
255
|
-
Requires-Dist: fast-matrix-market >=1.4.5 ; extra == 'io'
|
|
256
|
-
Requires-Dist: python-graphblas[numba] ; (python_version < "3.13") and extra == 'io'
|
|
257
|
-
Requires-Dist: sparse >=0.13 ; (python_version < "3.13") and extra == 'io'
|
|
237
|
+
Requires-Dist: numpy>=1.23
|
|
238
|
+
Requires-Dist: donfig>=0.6
|
|
239
|
+
Requires-Dist: pyyaml>=5.4
|
|
240
|
+
Requires-Dist: suitesparse-graphblas<10,>=7.4.0.0
|
|
241
|
+
Requires-Dist: numba>=0.55; python_version < "3.14"
|
|
242
|
+
Provides-Extra: suitesparse
|
|
243
|
+
Requires-Dist: suitesparse-graphblas<10,>=7.4.0.0; extra == "suitesparse"
|
|
258
244
|
Provides-Extra: networkx
|
|
259
|
-
Requires-Dist: networkx
|
|
245
|
+
Requires-Dist: networkx>=2.8; extra == "networkx"
|
|
260
246
|
Provides-Extra: numba
|
|
261
|
-
Requires-Dist: numba
|
|
247
|
+
Requires-Dist: numba>=0.55; extra == "numba"
|
|
262
248
|
Provides-Extra: pandas
|
|
263
|
-
Requires-Dist: pandas
|
|
264
|
-
Provides-Extra: repr
|
|
265
|
-
Requires-Dist: python-graphblas[pandas] ; extra == 'repr'
|
|
249
|
+
Requires-Dist: pandas>=1.5; extra == "pandas"
|
|
266
250
|
Provides-Extra: scipy
|
|
267
|
-
Requires-Dist: scipy
|
|
268
|
-
Provides-Extra: suitesparse
|
|
269
|
-
Requires-Dist: suitesparse-graphblas <9,>=7.4.0.0 ; extra == 'suitesparse'
|
|
251
|
+
Requires-Dist: scipy>=1.9; extra == "scipy"
|
|
270
252
|
Provides-Extra: suitesparse-udf
|
|
271
|
-
Requires-Dist: python-graphblas[numba,suitesparse]
|
|
272
|
-
Provides-Extra:
|
|
273
|
-
Requires-Dist: python-graphblas[pandas
|
|
274
|
-
|
|
275
|
-
Requires-Dist:
|
|
276
|
-
Requires-Dist:
|
|
253
|
+
Requires-Dist: python-graphblas[numba,suitesparse]; extra == "suitesparse-udf"
|
|
254
|
+
Provides-Extra: repr
|
|
255
|
+
Requires-Dist: python-graphblas[pandas]; extra == "repr"
|
|
256
|
+
Provides-Extra: io
|
|
257
|
+
Requires-Dist: python-graphblas[networkx,scipy]; 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"
|
|
277
262
|
Provides-Extra: viz
|
|
278
|
-
Requires-Dist: python-graphblas[networkx,scipy]
|
|
279
|
-
Requires-Dist: matplotlib
|
|
263
|
+
Requires-Dist: python-graphblas[networkx,scipy]; extra == "viz"
|
|
264
|
+
Requires-Dist: matplotlib>=3.6; extra == "viz"
|
|
265
|
+
Provides-Extra: datashade
|
|
266
|
+
Requires-Dist: python-graphblas[numba,pandas,scipy]; extra == "datashade"
|
|
267
|
+
Requires-Dist: datashader>=0.14; extra == "datashade"
|
|
268
|
+
Requires-Dist: hvplot>=0.8; extra == "datashade"
|
|
269
|
+
Provides-Extra: test
|
|
270
|
+
Requires-Dist: python-graphblas[pandas,scipy,suitesparse]; extra == "test"
|
|
271
|
+
Requires-Dist: packaging>=21; extra == "test"
|
|
272
|
+
Requires-Dist: pytest>=6.2; extra == "test"
|
|
273
|
+
Requires-Dist: tomli>=1; extra == "test"
|
|
274
|
+
Provides-Extra: default
|
|
275
|
+
Requires-Dist: python-graphblas[pandas,scipy,suitesparse]; extra == "default"
|
|
276
|
+
Requires-Dist: python-graphblas[numba]; python_version < "3.14" and extra == "default"
|
|
277
|
+
Provides-Extra: all
|
|
278
|
+
Requires-Dist: python-graphblas[default,io,test,viz]; extra == "all"
|
|
279
|
+
Requires-Dist: python-graphblas[datashade]; python_version < "3.14" and extra == "all"
|
|
280
280
|
|
|
281
281
|

|
|
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
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
graphblas/__init__.py,sha256=hXLvBYOSgP210Z04K9hJ_k80zLJ1X7_u6HUpDJfZVRw,7131
|
|
2
|
-
graphblas/exceptions.py,sha256=
|
|
2
|
+
graphblas/exceptions.py,sha256=y_e_2erCA4o1DSBfKgyiYrh_1CagKmDLfXCP8wj-pw4,4509
|
|
3
3
|
graphblas/graphblas.yaml,sha256=AuJWCjDwiUYuUYSwn3W5vbeqAenobqrM5HC081V28Ic,33
|
|
4
4
|
graphblas/viz.py,sha256=5JVpiFsYNG9AetXKXX9Jh7T4OmbN-_JJ-GwVi9983As,7558
|
|
5
5
|
graphblas/agg/__init__.py,sha256=fsFwEV5IOiP_a8w12X-NA-8Ltlhg81UFYOkc2UG_7Xg,3282
|
|
@@ -9,23 +9,23 @@ graphblas/binary/numpy.py,sha256=mxqRnWmzX9kofjvY-XhYvxkvMyojHPBOpi90MGPW7hE,537
|
|
|
9
9
|
graphblas/binary/ss.py,sha256=7m-x5oihpD4CiNEf9aD3AQNGNu9T1XSL6ubU1iaVq7k,113
|
|
10
10
|
graphblas/core/__init__.py,sha256=hgtNRdWJ5nC3zJo1vYE1L3oxXXBiTSWTtHTDPuQAyKQ,345
|
|
11
11
|
graphblas/core/automethods.py,sha256=wJymJjfHQNWCZ-TePxXNJg71llhbMEhWOND2iASLDvI,12680
|
|
12
|
-
graphblas/core/base.py,sha256=
|
|
12
|
+
graphblas/core/base.py,sha256=FuLn2U6AadiMo5ZnAbEpxef67qFaF6zVVc3wxkcILug,24510
|
|
13
13
|
graphblas/core/descriptor.py,sha256=dAdGs780GRK-a3lw_tI9u1sMhAP38LYoIxS_kZlezMQ,5979
|
|
14
|
-
graphblas/core/dtypes.py,sha256=
|
|
14
|
+
graphblas/core/dtypes.py,sha256=Tob23pR5__jsJ9xqapd31KW0oB7AXp79u7mm6W83JUM,13478
|
|
15
15
|
graphblas/core/expr.py,sha256=xf7XcdFWI_UEDGfLZZC31hLesb_dRwBIwJuASIrWVuQ,21154
|
|
16
|
-
graphblas/core/formatting.py,sha256=
|
|
17
|
-
graphblas/core/infix.py,sha256=
|
|
16
|
+
graphblas/core/formatting.py,sha256=o0HXpUfbivD04MGcNkn6wBH4p0DAUR7vd-4L0PxJ6JQ,30165
|
|
17
|
+
graphblas/core/infix.py,sha256=g48IIhyaSHhk3pWyrdLJNNugjmJBUz4TO4cUSGmSrt4,23077
|
|
18
18
|
graphblas/core/infixmethods.py,sha256=Ig36RjmHXW63uBWAqTa5QgpJcn4lV0u2V4S0zUdeXbo,11860
|
|
19
19
|
graphblas/core/mask.py,sha256=aPC17q9fLH_VP2kfNYayetBU6tN-4Nx0HUKIT2fbBok,15877
|
|
20
|
-
graphblas/core/matrix.py,sha256
|
|
20
|
+
graphblas/core/matrix.py,sha256=GrW7fJZmTENX2UU3V027PQ-Ky9tV8n8qKrFB_1_PROc,153267
|
|
21
21
|
graphblas/core/recorder.py,sha256=KJjVoS83LYDrRcqzc4zDL52m4rnuf-kTO5y2pQ_8qo8,5935
|
|
22
|
-
graphblas/core/scalar.py,sha256=
|
|
22
|
+
graphblas/core/scalar.py,sha256=QuBHl-0GdSPnUe3cBp84nw9gAbhn46c1vBys9tsnkNk,43048
|
|
23
23
|
graphblas/core/slice.py,sha256=hmuEEdwdiXPYkyx67Ywqs1EBdYFyP_Jg7KFpZYc7vTA,1826
|
|
24
|
-
graphblas/core/utils.py,sha256=
|
|
25
|
-
graphblas/core/vector.py,sha256=
|
|
24
|
+
graphblas/core/utils.py,sha256=uHBC1AlYf2Pp6OmXYVSgo6rdwp6d7d0uiPvdWopwTd0,13848
|
|
25
|
+
graphblas/core/vector.py,sha256=ELHHA3cpwrHvPTwLgSSZlMwWCK3-djblL_3SUBjxRvA,86049
|
|
26
26
|
graphblas/core/operator/__init__.py,sha256=uUTI1uat6ndA-qYpvbliyAt6arDTxXIVstDEdpmPyYo,743
|
|
27
27
|
graphblas/core/operator/agg.py,sha256=aoUnd9oHK1QXi0dQ4lZZBpzijD2IAeTKa6h0EB6nEWU,24816
|
|
28
|
-
graphblas/core/operator/base.py,sha256=
|
|
28
|
+
graphblas/core/operator/base.py,sha256=69SvKsP8L3TQZpCs67GB2xa_0lJww038hehBq2jt0qQ,18133
|
|
29
29
|
graphblas/core/operator/binary.py,sha256=y0frqF0vmCdH5BP_JtVm6rKfVkCrwmlZn_dJz7antVI,36343
|
|
30
30
|
graphblas/core/operator/indexunary.py,sha256=ZDOXra06AAyPRY_9XDHgxGfjldsXG6lYYHcX0Ccv_B8,19396
|
|
31
31
|
graphblas/core/operator/monoid.py,sha256=1oQ2qQMENMoMD9WbyC3Q8b9lpRGDz_tgg-MYtDE7N4A,15893
|
|
@@ -33,18 +33,18 @@ graphblas/core/operator/select.py,sha256=CnOHQIBuI5ANgDqsnAFHiWnJVc8pKtesZNY2PEq
|
|
|
33
33
|
graphblas/core/operator/semiring.py,sha256=aF4hv8G46gXgmgM_4w-6A3m935riTYvLP8vygnG2dsE,21708
|
|
34
34
|
graphblas/core/operator/unary.py,sha256=Nrn-rSx4uZZ4OAJvrzsDkKTXRvFTNcGh_CyjLlW6iao,18340
|
|
35
35
|
graphblas/core/operator/utils.py,sha256=WxxyqtlBg783-vLOWic4JPv0VpdzQ5Et42F_MaYjd6Q,15383
|
|
36
|
-
graphblas/core/ss/__init__.py,sha256=
|
|
36
|
+
graphblas/core/ss/__init__.py,sha256=546hNJvo_OeRrphaLMyJ18EmJrT_qGDiAF7AVnny65s,160
|
|
37
37
|
graphblas/core/ss/binary.py,sha256=TDmFG5RIOO7sbSx2MpLnOuMlyBzel7Qcrd75uNUz-AU,4735
|
|
38
|
-
graphblas/core/ss/config.py,sha256=
|
|
38
|
+
graphblas/core/ss/config.py,sha256=4DIkEl7_ETISlB6PY_GawjK-POwyjXcfUzi2IeoKea8,8276
|
|
39
39
|
graphblas/core/ss/context.py,sha256=t2v9W1Lnwi4JJJp90tQxJjmdY4HDXSDhXU6DGE4aHHI,4620
|
|
40
40
|
graphblas/core/ss/descriptor.py,sha256=0m4eyMbg355ev2mFPy53N9gVebBjYbRVXTbyxroxfoM,8304
|
|
41
41
|
graphblas/core/ss/dtypes.py,sha256=QGRscMdb3WVygBXjLocW3k9XNN3WYr2qDKzVSnj3OvI,3583
|
|
42
42
|
graphblas/core/ss/indexunary.py,sha256=G-GyCHZuTLInxcMCKiuOLY-pX4kfFIiwvo3dW8-j3jM,5941
|
|
43
|
-
graphblas/core/ss/matrix.py,sha256=
|
|
43
|
+
graphblas/core/ss/matrix.py,sha256=k4ZomCKrSotNYwjkwyUZu30quC33WuXfeKFwcPas9NY,152100
|
|
44
44
|
graphblas/core/ss/prefix_scan.py,sha256=JtxeKCQ4NNvhSgqmyaDVyMGdLEOgr3ARjNSBRfugsw4,5999
|
|
45
45
|
graphblas/core/ss/select.py,sha256=QqgOQx3NemYYKgz8UWaaxix7syKLpHWxANqVs8z_cU4,3437
|
|
46
46
|
graphblas/core/ss/unary.py,sha256=zuTYY3iHNhmA1yEPHO-Vt2yA6bM-KZppJj8nC_AVBR4,4036
|
|
47
|
-
graphblas/core/ss/vector.py,sha256=
|
|
47
|
+
graphblas/core/ss/vector.py,sha256=sEO3n7CRoeVprpXHR74kELv20eaZgsCNW8bPkMJ2eL4,61073
|
|
48
48
|
graphblas/dtypes/__init__.py,sha256=veA5FgUQaBAoqyb3NPWJae3mP1dai2IeoLGI8nsyE10,943
|
|
49
49
|
graphblas/dtypes/ss.py,sha256=_Xexm1SeoI3zr5AxSWUtWWTZC3Fg2AkZx82QK-4ZHDs,56
|
|
50
50
|
graphblas/indexunary/__init__.py,sha256=PNubKaG9yiijl4sJJRcc7p4EFonUuk8y5Mqv10z3TyM,878
|
|
@@ -52,7 +52,7 @@ graphblas/indexunary/ss.py,sha256=FqVm91nMeOeU6sU3GaiGBTDTmjtABDjuO6Nl818_EZ8,11
|
|
|
52
52
|
graphblas/io/__init__.py,sha256=YZHXdMGSc2cEmISECYrAaiNRKxupFaiCFhtgb2qvp-Y,253
|
|
53
53
|
graphblas/io/_awkward.py,sha256=WCq3lwcH1L54qh79TykJlh-W3IkPLGr-zQElvL4ObUs,6598
|
|
54
54
|
graphblas/io/_matrixmarket.py,sha256=qRJ3m87gifOaazgg0c35CyGQWHoTC_ZumLfGw-XKNSc,5484
|
|
55
|
-
graphblas/io/_networkx.py,sha256=
|
|
55
|
+
graphblas/io/_networkx.py,sha256=w2dJXXTI1ZH6xCsL5wFYtwscrM5CZVK179k61BKGx98,1667
|
|
56
56
|
graphblas/io/_scipy.py,sha256=JLELo8lDWH5eza0AdqyEuI4y60FlNTjKyZ4v9GZba24,4111
|
|
57
57
|
graphblas/io/_sparse.py,sha256=7GQsn6-a5duO5G6uW0Nh-lNxaQL-rUU0dzvDUwsjqOw,3247
|
|
58
58
|
graphblas/monoid/__init__.py,sha256=VF-uQiH7DXLoAIOcLOO0ftMy875nBXN1Tv5klnc7I4w,1076
|
|
@@ -69,7 +69,7 @@ graphblas/semiring/ss.py,sha256=_ZS0Iyx0O2oYoTOXza2SLjWNXeGBYuzKEvEljBIww4Y,57
|
|
|
69
69
|
graphblas/ss/__init__.py,sha256=MRUECkNFgmY5BfMn7J5NE_N_oGZ5hCRETqOhzWsA2yo,235
|
|
70
70
|
graphblas/ss/_core.py,sha256=i4XApSUWlSBmWvRxK3WJ29LKbooPPF3d7mRRvGfpjXo,12543
|
|
71
71
|
graphblas/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
|
-
graphblas/tests/conftest.py,sha256=
|
|
72
|
+
graphblas/tests/conftest.py,sha256=BqQqTD9qjw7hU5IByRMsBlsipX-xWC7tWD_G4GeTELc,4874
|
|
73
73
|
graphblas/tests/pickle1-vanilla.pkl,sha256=pJqNg_K-NtM8cmSGNcWJGGklFFAVYgJA0c8P8ofhenQ,2755
|
|
74
74
|
graphblas/tests/pickle1.pkl,sha256=4E7TctG64NyxoyDawj6tawZolKyE_-T6zVp4RNgtPHQ,2998
|
|
75
75
|
graphblas/tests/pickle2-vanilla.pkl,sha256=y62DcVUC0IBLeC8Xq_VAE6hL7gZIRKlJ20GOcJVFOBY,1453
|
|
@@ -79,29 +79,29 @@ graphblas/tests/pickle3.pkl,sha256=OOsg91sHvrZbp_LFmzufsZIaAIwdxq0QH6ZmprUbI2E,8
|
|
|
79
79
|
graphblas/tests/test_auto_init.py,sha256=uCFe7KCk_rT_Hx2yKHaFm6yX1V00SnEdbXL5E5j80vY,506
|
|
80
80
|
graphblas/tests/test_core.py,sha256=vZaVZfxogXYhk_9q77DwOmBcLMX4NM1ZM1F4LPdu6Q0,3278
|
|
81
81
|
graphblas/tests/test_descriptor.py,sha256=H-ZClw8ZNSb22V446MR3kdHeGGvpGeF24g3FLTuch0g,579
|
|
82
|
-
graphblas/tests/test_dtype.py,sha256=
|
|
82
|
+
graphblas/tests/test_dtype.py,sha256=bbFgJIdBtQvuYaOtQjwkZcqmnQx1Z-5cjQ27LcVw4bw,8879
|
|
83
83
|
graphblas/tests/test_external_init.py,sha256=AqtKhi9w-gdkerMa_MEYH4Av1gV-dFdy_840Z7Ka1Do,302
|
|
84
84
|
graphblas/tests/test_formatting.py,sha256=HaIzQJU9FUSJxlYOb61Z1Lp5Mm84UPJ3C7ErEojdAoE,187980
|
|
85
85
|
graphblas/tests/test_infix.py,sha256=n3ltg07DIlqT7sUNubUHxC2EBSPkYb9RJtn-t65qvoo,29058
|
|
86
|
-
graphblas/tests/test_io.py,sha256=
|
|
86
|
+
graphblas/tests/test_io.py,sha256=t_iYWAM8EJEYvpmpJe-ljMg-UtlMOle-l4yigBAqAHo,17254
|
|
87
87
|
graphblas/tests/test_mask.py,sha256=LYYy46dzzJlOMLAU5zZ2Yivony8wFxAESTkiL9RJLKM,4693
|
|
88
|
-
graphblas/tests/test_matrix.py,sha256=
|
|
89
|
-
graphblas/tests/test_numpyops.py,sha256=
|
|
88
|
+
graphblas/tests/test_matrix.py,sha256=mYshUu2OHV61xXhA7iVrOHSlcqU8WwiS7h5_XaVZUBo,157557
|
|
89
|
+
graphblas/tests/test_numpyops.py,sha256=by3MVs5NtONLNSkAqghyyK1KCMz82jDaNas5UfOmejk,13658
|
|
90
90
|
graphblas/tests/test_op.py,sha256=SX-actrjLqEcGSdlf-y8bFonLhYo93rAAgMisqUnu8g,55332
|
|
91
91
|
graphblas/tests/test_operator_types.py,sha256=JJY8BlHmyK8GVzyPf1eirBfahGMJ12wP_a1e40ZEqvU,11007
|
|
92
92
|
graphblas/tests/test_pickle.py,sha256=uzFH9WzpdYbYy1pN_a7oGX-mZS2QvU2fF-Tta6FDKDQ,12735
|
|
93
93
|
graphblas/tests/test_prefix_scan.py,sha256=nvwIywIDj6earTyZ6QLwpcWEZNssQebuV5P1XlSbyHw,2476
|
|
94
94
|
graphblas/tests/test_recorder.py,sha256=YcqGt99PpV2iBYtOEQlwh_rWOY0CHp44e21IqRiiAA8,9199
|
|
95
95
|
graphblas/tests/test_resolving.py,sha256=oQDCjTc2dimLavAJ7JeaEsQ1UH_ezsbigQq4zLAm3RQ,10923
|
|
96
|
-
graphblas/tests/test_scalar.py,sha256=
|
|
96
|
+
graphblas/tests/test_scalar.py,sha256=x4-9-ptNA9E1rubqQSMNL5OWoVgihhFwb0Ff3yoO-w0,19747
|
|
97
97
|
graphblas/tests/test_ss_utils.py,sha256=8-GYG7ITeXYcsU0qbOof_5dosKZaXiVt3fsgzFLKKJs,10985
|
|
98
|
-
graphblas/tests/test_ssjit.py,sha256=
|
|
99
|
-
graphblas/tests/test_vector.py,sha256=
|
|
98
|
+
graphblas/tests/test_ssjit.py,sha256=P6HCsWXJfl7Krh0M8ieyK3G2DM8zSUHSxyjPzHHdJDw,17784
|
|
99
|
+
graphblas/tests/test_vector.py,sha256=ZU4g1vV5PEChVkLaOZED5hlabVolxStbzpjjc_J0zs8,90318
|
|
100
100
|
graphblas/unary/__init__.py,sha256=PT-rSt4DfhPR7Go7HG6qc5CsXOy7I3XNq8M91e9ugrQ,1561
|
|
101
101
|
graphblas/unary/numpy.py,sha256=nnuoHrOlOftovU_gQHQKH-oNO87uD6i23QseYNNqL8o,4158
|
|
102
102
|
graphblas/unary/ss.py,sha256=0cetCBuSFKSE8Wt9JjduIP02581q83tg_tUJ4MrzC4I,112
|
|
103
|
-
python_graphblas-
|
|
104
|
-
python_graphblas-
|
|
105
|
-
python_graphblas-
|
|
106
|
-
python_graphblas-
|
|
107
|
-
python_graphblas-
|
|
103
|
+
python_graphblas-2025.2.0a1.dist-info/LICENSE,sha256=tc3W3NZiv6HV_-4e16TQoM-zm7fohpX2t283tjKC_hg,11368
|
|
104
|
+
python_graphblas-2025.2.0a1.dist-info/METADATA,sha256=gWZldzcL_xz0wv7WvorToTIxEAU3p26fqX4nBi41oqw,29602
|
|
105
|
+
python_graphblas-2025.2.0a1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
106
|
+
python_graphblas-2025.2.0a1.dist-info/top_level.txt,sha256=QC8r4KyBl_LkZYZUdrsd0-NAJuQQcD_2fwGGiX104OM,10
|
|
107
|
+
python_graphblas-2025.2.0a1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|