Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ import graphblas as gb

# scipy.sparse matrices
A = gb.io.from_scipy_sparse(m)
m = gb.io.to_scipy_sparse(m, format="csr")
m = gb.io.to_scipy_sparse(A, format="csr")

# networkx graphs
A = gb.io.from_networkx(g)
Expand Down
4 changes: 2 additions & 2 deletions docs/_static/matrix.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ table.matrix {
border: 0px;
}

/* Disable a horizintal line from the default stylesheet */
/* Disable a horizontal line from the default stylesheet */
.table.matrix > :not(caption) > * > * {
border-bottom-width: 0px;
}
Expand Down Expand Up @@ -48,7 +48,7 @@ table.matrix > tbody tr td:last-of-type {

/* prevents empty cells from collapsing, especially empty rows */
table.matrix > tbody tr td:empty::before {
/* basicaly fills empty cells with   */
/* basically fills empty cells with   */
content: "\00a0\00a0\00a0";
visibility: hidden;
}
Expand Down
2 changes: 1 addition & 1 deletion docs/user_guide/operators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ This facilitates writing more succinct code such as:

cur_min(accum=op.min) << op.min_plus(A @ B).reduce_rowwise(op.min)

In the case of name conflicts (ex. binary.min and monoid.min), only one will exist in the the
In the case of name conflicts (ex. binary.min and monoid.min), only one will exist in the
``graphblas.op`` namespace. However, almost all functions which require a specific kind of
operator have a mechanism to convert from an identically named operator of a different type.

Expand Down
19 changes: 12 additions & 7 deletions docs/user_guide/udt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,18 @@ import changes nothing else, so reading ``gb.ss.about`` does not change what
later operations compute.

``jit_c_control`` is raised from the SS default ``'run'`` (run cached kernels
only; no compile, no load from disk) to ``'on'`` (compile, load, and run) when
a UDT operator gets C source for SuiteSparse to compile, such as the first
``binary.plus[udt]``, or when ``gb.dtypes.ss.register_new`` registers a type
from a C typedef. Any other setting is kept, including ``'off'``, ``'pause'``,
and ``'load'`` (load and run cached kernels, never compile). SuiteSparse
itself sets ``'load'`` after a compile fails, so keeping it means a failing
compile is tried once, not again at every later UDT operation.
only; no compile, no load from disk) to ``'on'`` (compile, load, and run) by
each of these, since each asks SuiteSparse to compile C source:

- a UDT operator that gets C source, such as the first ``binary.plus[udt]``;
- ``gb.dtypes.ss.register_new``, which registers a type from a C typedef;
- ``gb.binary.ss.register_new`` and its ``unary``, ``indexunary``, ``select``,
and ``indexbinary`` siblings, which register an operator from a C definition.

A setting other than ``'run'`` is kept, including ``'off'``, ``'pause'``, and
``'load'`` (load and run cached kernels, never compile). SuiteSparse itself
sets ``'load'`` after a compile fails, so keeping it means a failing compile
is tried once, not again at every later request.

Call the helper manually to re-fix or verify::

Expand Down
2 changes: 1 addition & 1 deletion graphblas/core/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ def register_anonymous(dtype, name=None):
else:
np_repr = np_repr[: lib.GxB_MAX_NAME_LEN]
warnings.warn(
f"{msg}. It will use the following name, "
f"{msg} It will use the following name, "
f"and the dtype may need to be specified when deserializing: {np_repr}",
stacklevel=2,
)
Expand Down
16 changes: 8 additions & 8 deletions graphblas/core/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def __sizeof__(self):
size = ffi_new("size_t*")
check_status(lib.GxB_Matrix_memoryUsage(size, self.gb_obj[0]), self)
return size[0] + object.__sizeof__(self)
raise TypeError("Unable to get size of Matrix with backend: {backend}")
raise TypeError(f"Unable to get size of Matrix with backend: {backend}")

def isequal(self, other, *, check_dtype=False, **opts):
"""Check for exact equality (same size, same structure).
Expand All @@ -377,7 +377,7 @@ def isequal(self, other, *, check_dtype=False, **opts):
----------
other : Matrix
The matrix to compare against
check_dtypes : bool
check_dtype : bool
If True, also checks that dtypes match

Returns
Expand Down Expand Up @@ -532,7 +532,7 @@ def to_coo(self, dtype=None, *, rows=True, columns=True, values=True, sort=True)
Requested dtype for the output values array.
rows : bool, default=True
Whether to return rows; will return ``None`` for rows if ``False``
columns :bool, default=True
columns : bool, default=True
Whether to return columns; will return ``None`` for columns if ``False``
values : bool, default=True
Whether to return values; will return ``None`` for values if ``False``
Expand Down Expand Up @@ -1131,15 +1131,15 @@ def from_csc(
----------
indptr : list or np.ndarray
Pointers for each column into row_indices and values; ``indptr.size == ncols + 1``.
col_indices : list or np.ndarray
Column indices.
row_indices : list or np.ndarray
Row indices.
values : list or np.ndarray or scalar, default 1.0
List of values. If a scalar is provided, all values will be set to this single value.
dtype :
Data type of the Matrix. If not provided, the values will be inspected
to choose an appropriate dtype.
nrows : int, optional
Number of rows in the Matrix. If not provided, ``ncols`` is computed
Number of rows in the Matrix. If not provided, ``nrows`` is computed
from the maximum row index found in ``row_indices``.
ncols : int, optional
Number of columns in the Matrix. ncols is computed as ``len(indptr) - 1``.
Expand Down Expand Up @@ -1529,7 +1529,7 @@ def from_dicts(

Parameters
----------
d : Mapping or Sequence
nested_dicts : Mapping or Sequence
The dict-like object to convert. The keys will be cast to uint64 for the indices.
dtype :
Data type of the Matrix. If not provided, the values will be inspected
Expand Down Expand Up @@ -2974,7 +2974,7 @@ def setdiag(self, values, k=0, *, mask=None, accum=None, **opts):
elif v._size != size:
raise DimensionMismatch(
f"Dimensions not compatible for assigning length {v._size} Vector "
f"to {k}'th diagonal of Matrix with shape {self._nrows}x{self._ncols}."
f"to {k}'th diagonal of Matrix with shape {self._nrows}x{self._ncols}. "
f"The Vector should be size {size}."
)

Expand Down
4 changes: 2 additions & 2 deletions graphblas/core/operator/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ def __getitem__(self, type_):
raise KeyError(f"{self.name} does not work with {type_}")
else:
return self._typed_ops[type_]
# This is a UDT or is able to operate on UDTs such as `first` any `any`
# This is a UDT or is able to operate on UDTs such as `first` and `any`
dtype = lookup_dtype(type_)
return self._compile_udt(dtype, dtype)

Expand Down Expand Up @@ -903,7 +903,7 @@ def _initialize(cls, include_in_ops=True):
"""Initialize operators for this operator type.

include_in_ops determines whether the operators are included in the
``gb.ops`` namespace in addition to the defined module.
``gb.op`` namespace in addition to the defined module.
"""
if cls._initialized: # pragma: no cover (safety)
return
Expand Down
2 changes: 1 addition & 1 deletion graphblas/core/operator/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ class BinaryOp(OpBase):
"""Takes two inputs and returns one output, possibly of a different data type.

Built-in and registered BinaryOps are located in the ``graphblas.binary`` namespace
as well as in the ``graphblas.ops`` combined namespace.
as well as in the ``graphblas.op`` combined namespace.
"""

__slots__ = (
Expand Down
10 changes: 5 additions & 5 deletions graphblas/core/operator/monoid.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ class Monoid(OpBase):
"""Takes two inputs and returns one output, all of the same data type.

Built-in and registered Monoids are located in the ``graphblas.monoid`` namespace
as well as in the ``graphblas.ops`` combined namespace.
as well as in the ``graphblas.op`` combined namespace.
"""

__slots__ = "_binaryop", "_identity", "_is_idempotent"
Expand Down Expand Up @@ -347,10 +347,10 @@ def register_anonymous(cls, binaryop, identity, name=None, *, is_idempotent=Fals

Parameters
----------
binaryop: BinaryOp or ParameterizedBinaryOp
binaryop : BinaryOp or ParameterizedBinaryOp
The binary operator of the monoid, which should be able to use the same
dtype for both inputs and the output.
identity: scalar or Mapping
identity : scalar or Mapping
The identity of the monoid such that ``op(x, identity) == x`` for any x.
``identity`` may also be a mapping from dtype to scalar.
name : str, optional
Expand Down Expand Up @@ -381,10 +381,10 @@ def register_new(cls, name, binaryop, identity, *, is_idempotent=False, lazy=Fal
The name of the operator. This will show up as ``gb.monoid.{name}``.
The name may contain periods, ".", which will result in nested objects
such as ``gb.monoid.x.y.z`` for name ``"x.y.z"``.
binaryop: BinaryOp or ParameterizedBinaryOp
binaryop : BinaryOp or ParameterizedBinaryOp
The binary operator of the monoid, which should be able to use the same
dtype for both inputs and the output.
identity: scalar or Mapping
identity : scalar or Mapping
The identity of the monoid such that ``op(x, identity) == x`` for any x.
``identity`` may also be a mapping from dtype to scalar.
is_idempotent : bool, default False
Expand Down
2 changes: 1 addition & 1 deletion graphblas/core/operator/semiring.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class Semiring(OpBase):
and the Monoid taking the place of the standard addition operator.

Built-in and registered Semirings are located in the ``graphblas.semiring`` namespace
as well as in the ``graphblas.ops`` combined namespace.
as well as in the ``graphblas.op`` combined namespace.
"""

__slots__ = "_monoid", "_binaryop"
Expand Down
2 changes: 1 addition & 1 deletion graphblas/core/operator/unary.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class UnaryOp(OpBase):
"""Takes one input and returns one output, possibly of a different data type.

Built-in and registered UnaryOps are located in the ``graphblas.unary`` namespace
as well as in the ``graphblas.ops`` combined namespace.
as well as in the ``graphblas.op`` combined namespace.
"""

__slots__ = "orig_func", "is_positional", "_is_udt", "_numba_func"
Expand Down
4 changes: 2 additions & 2 deletions graphblas/core/scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def __sizeof__(self):
size = ffi_new("size_t*")
check_status(lib.GxB_Scalar_memoryUsage(size, self.gb_obj[0]), self)
return base + size[0]
raise TypeError("Unable to get size of GrB_Scalar with backend: {backend}")
raise TypeError(f"Unable to get size of GrB_Scalar with backend: {backend}")

def isequal(self, other, *, check_dtype=False):
"""Check for exact equality (including whether the value is missing).
Expand All @@ -191,7 +191,7 @@ def isequal(self, other, *, check_dtype=False):
----------
other : Scalar
Scalar to compare against
check_dtypes : bool, default=False
check_dtype : bool, default=False
If True, also checks that dtypes match

Returns
Expand Down
29 changes: 16 additions & 13 deletions graphblas/core/ss/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,23 @@ def register_new(name, jit_c_definition, left_type, right_type, ret_type):
else:
# We use `is_udt=True` to make dtype handling flexible and explicit.
rv = BinaryOp(name, is_udt=True)
from .jit_config import _compiling_c_source

gb_obj = ffi_new("GrB_BinaryOp*")
check_status_carg(
lib.GxB_BinaryOp_new(
gb_obj,
NULL,
ret_type._carg,
left_type._carg,
right_type._carg,
ffi_new("char[]", funcname.encode()),
ffi_new("char[]", jit_c_definition.encode()),
),
"BinaryOp",
gb_obj[0],
)
with _compiling_c_source(f"gb.binary.{name}"):
check_status_carg(
lib.GxB_BinaryOp_new(
gb_obj,
NULL,
ret_type._carg,
left_type._carg,
right_type._carg,
ffi_new("char[]", funcname.encode()),
ffi_new("char[]", jit_c_definition.encode()),
),
"BinaryOp",
gb_obj[0],
)
op = TypedJitBinaryOp(
rv, funcname, left_type, ret_type, gb_obj[0], jit_c_definition, dtype2=right_type
)
Expand Down
4 changes: 2 additions & 2 deletions graphblas/core/ss/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def get_descriptor(**opts):
Whether and how to compress the data for serialization.
The default is "zstd" with ``compression_level=1``
compression_level : int
1-9 for "lz4hc" compression: (1) is the fastest, (9) is the most compact (default 1).
1-9 for "lz4hc" compression: (1) is the fastest, (9) is the most compact (default 9).
1-19 for "zstd" compression: (1) is the fastest, (19) is the most compact (default 1).
Ignored for other compression methods.

Expand Down Expand Up @@ -212,7 +212,7 @@ def _set_compression(desc, compression, level):
comp = _str_to_compression[compression]
if level is not None:
if compression not in {"lz4hc", "zstd"}:
raise TypeError('level argument is only valid when using "lz4hc" compression')
raise TypeError('level argument is only valid when using "lz4hc" or "zstd" compression')
level = int(level)
upper = 9 if compression == "lz4hc" else 19
default = 9 if compression == "lz4hc" else 1
Expand Down
24 changes: 15 additions & 9 deletions graphblas/core/ss/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ def register_new(name, jit_c_definition, *, np_type=None):
raise ValueError(f"`name` argument must be a valid Python identifier; got: {name!r}")
if name in core.dtypes._registry or hasattr(dtypes.ss, name):
raise ValueError(f"{name!r} name for dtype is unavailable")
if len(name) > lib.GxB_MAX_NAME_LEN:
# GxB_MAX_NAME_LEN counts the terminating NUL, so SuiteSparse keeps one fewer
# character; a name that long would no longer match its typedef and would
# fail to compile.
if len(name) >= lib.GxB_MAX_NAME_LEN:
raise ValueError(
f"`name` argument is too large. Max size is {lib.GxB_MAX_NAME_LEN}; got {len(name)}"
"`name` argument is too large. "
f"Max size is {lib.GxB_MAX_NAME_LEN - 1}; got {len(name)}"
)
if name not in jit_c_definition:
raise ValueError("`name` argument must be same name as the typedef in `jit_c_definition`")
Expand All @@ -42,15 +46,17 @@ def register_new(name, jit_c_definition, *, np_type=None):

# Registering a type by its C typedef is a request to compile C, so it is
# a fair place to raise SuiteSparse's non-compiling default.
from .jit_config import _enable_jit_for_udt

_enable_jit_for_udt()
from .jit_config import _compiling_c_source

gb_obj = ffi.new("GrB_Type*")
status = lib.GxB_Type_new(
gb_obj, 0, ffi_new("char[]", name.encode()), ffi_new("char[]", jit_c_definition.encode())
)
check_status_carg(status, "Type", gb_obj[0])
with _compiling_c_source(f"gb.dtypes.ss.{name}"):
status = lib.GxB_Type_new(
gb_obj,
0,
ffi_new("char[]", name.encode()),
ffi_new("char[]", jit_c_definition.encode()),
)
check_status_carg(status, "Type", gb_obj[0])

# Let SuiteSparse:GraphBLAS determine the size (we gave 0 as size above)
size_ptr = ffi_new("size_t*")
Expand Down
31 changes: 17 additions & 14 deletions graphblas/core/ss/indexbinary.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,24 @@ def register_new(name, jit_c_definition, x_type, y_type, theta_type, ret_type):
)
else:
rv = IndexBinaryOp(name, is_udt=True)
from .jit_config import _compiling_c_source

gb_obj = ffi_new("GxB_IndexBinaryOp*")
check_status_carg(
lib.GxB_IndexBinaryOp_new(
gb_obj,
NULL,
ret_type._carg,
x_type._carg,
y_type._carg,
theta_type._carg,
ffi_new("char[]", funcname.encode()),
ffi_new("char[]", jit_c_definition.encode()),
),
"IndexBinaryOp",
gb_obj[0],
)
with _compiling_c_source(f"gb.indexbinary.{name}"):
check_status_carg(
lib.GxB_IndexBinaryOp_new(
gb_obj,
NULL,
ret_type._carg,
x_type._carg,
y_type._carg,
theta_type._carg,
ffi_new("char[]", funcname.encode()),
ffi_new("char[]", jit_c_definition.encode()),
),
"IndexBinaryOp",
gb_obj[0],
)
op = TypedJitIndexBinaryOp(
rv, funcname, x_type, ret_type, gb_obj[0], jit_c_definition, dtype2=theta_type
)
Expand Down
Loading
Loading