Skip to content

Commit

Permalink
Move get_order, normalize_chunks, and MatrixArray to `core.util…
Browse files Browse the repository at this point in the history
  • Loading branch information
eriknw authored Oct 27, 2022
1 parent 2fc650e commit 14d24e3
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 122 deletions.
111 changes: 6 additions & 105 deletions graphblas/core/ss/matrix.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import itertools
import warnings
from numbers import Integral, Number

import numba
import numpy as np
Expand All @@ -13,21 +12,23 @@
from ...dtypes import _INDEX, BOOL, INT64, _string_to_dtype, lookup_dtype
from ...exceptions import _error_code_lookup, check_status, check_status_carg
from .. import NULL, ffi, lib
from ..base import call, record_raw
from ..base import call
from ..scalar import Scalar, _as_scalar, _scalar_index
from ..utils import (
_CArray,
_MatrixArray,
_Pointer,
get_order,
get_shape,
ints_to_numpy_buffer,
libget,
normalize_chunks,
output_type,
values_to_numpy_buffer,
wrapdoc,
)
from .config import BaseConfig
from .descriptor import get_compression_descriptor, get_nthreads_descriptor
from .utils import get_order

ffi_new = ffi.new

Expand Down Expand Up @@ -177,96 +178,6 @@ def head(matrix, n=10, dtype=None, *, sort=False):
return rows, cols, vals


def normalize_chunks(chunks, shape):
"""Normalize chunks argument for use by `Matrix.ss.split`.
Examples
--------
>>> shape = (10, 20)
>>> normalize_chunks(10, shape)
[(10,), (10, 10)]
>>> normalize_chunks((10, 10), shape)
[(10,), (10, 10)]
>>> normalize_chunks([None, (5, 15)], shape)
[(10,), (5, 15)]
>>> normalize_chunks((5, (5, None)), shape)
[(5, 5), (5, 15)]
"""
if isinstance(chunks, (list, tuple)):
pass
elif isinstance(chunks, Number):
chunks = (chunks,) * len(shape)
elif isinstance(chunks, np.ndarray):
chunks = chunks.tolist()
else:
raise TypeError(
f"chunks argument must be a list, tuple, or numpy array; got: {type(chunks)}"
)
if len(chunks) != len(shape):
typ = "Vector" if len(shape) == 1 else "Matrix"
raise ValueError(
f"chunks argument must be of length {len(shape)} (one for each dimension of a {typ})"
)
chunksizes = []
for size, chunk in zip(shape, chunks):
if chunk is None:
cur_chunks = [size]
elif isinstance(chunk, Integral) or isinstance(chunk, float) and chunk.is_integer():
chunk = int(chunk)
if chunk < 0:
raise ValueError(f"Chunksize must be greater than 0; got: {chunk}")
div, mod = divmod(size, chunk)
cur_chunks = [chunk] * div
if mod:
cur_chunks.append(mod)
elif isinstance(chunk, (list, tuple)):
cur_chunks = []
none_index = None
for c in chunk:
if isinstance(c, Integral) or isinstance(c, float) and c.is_integer():
c = int(c)
if c < 0:
raise ValueError(f"Chunksize must be greater than 0; got: {c}")
elif c is None:
if none_index is not None:
raise TypeError(
'None value in chunks for "the rest" can only appear once per dimension'
)
none_index = len(cur_chunks)
c = 0
else:
raise TypeError(
"Bad type for element in chunks; expected int or None, but got: "
f"{type(chunks)}"
)
cur_chunks.append(c)
if none_index is not None:
fill = size - sum(cur_chunks)
if fill < 0:
raise ValueError(
"Chunks are too large; None value in chunks would need to be negative "
"to match size of input"
)
cur_chunks[none_index] = fill
elif isinstance(chunk, np.ndarray):
if not np.issubdtype(chunk.dtype, np.integer):
raise TypeError(f"numpy array for chunks must be integer dtype; got {chunk.dtype}")
if chunk.ndim != 1:
raise TypeError(
f"numpy array for chunks must be 1-dimension; got ndim={chunk.ndim}"
)
if (chunk < 0).any():
raise ValueError(f"Chunksize must be greater than 0; got: {chunk[chunk < 0]}")
cur_chunks = chunk.tolist()
else:
raise TypeError(
"Chunks for a dimension must be an integer, a list or tuple of integers, or None."
f" Got: {type(chunk)}"
)
chunksizes.append(cur_chunks)
return chunksizes


def _concat_mn(tiles, *, is_matrix=None):
"""Argument checking for `Matrix.ss.concat` and returns number of tiles in each dimension"""
from ..matrix import Matrix, TransposedMatrix
Expand Down Expand Up @@ -326,16 +237,6 @@ def _as_matrix(x):
return x._as_matrix() if hasattr(x, "_as_matrix") else x


class MatrixArray:
__slots__ = "_carg", "_exc_arg", "name"

def __init__(self, matrices, exc_arg=None, *, name):
self._carg = matrices
self._exc_arg = exc_arg
self.name = name
record_raw(f"GrB_Matrix {name}[{len(matrices)}];")


class MatrixConfig(BaseConfig):
"""Get and set configuration options for this Matrix.
Expand Down Expand Up @@ -539,7 +440,7 @@ def split(self, chunks, *, name=None):
call(
"GxB_Matrix_split",
[
MatrixArray(tiles, self._parent, name="tiles"),
_MatrixArray(tiles, self._parent, name="tiles"),
_as_scalar(m, _INDEX, is_cscalar=True),
_as_scalar(n, _INDEX, is_cscalar=True),
_CArray(tile_nrows),
Expand Down Expand Up @@ -580,7 +481,7 @@ def _concat(self, tiles, m, n):
"GxB_Matrix_concat",
[
self._parent,
MatrixArray(ctiles, name="tiles"),
_MatrixArray(ctiles, name="tiles"),
_as_scalar(m, _INDEX, is_cscalar=True),
_as_scalar(n, _INDEX, is_cscalar=True),
None,
Expand Down
11 changes: 0 additions & 11 deletions graphblas/core/ss/utils.py

This file was deleted.

18 changes: 13 additions & 5 deletions graphblas/core/ss/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@
from .. import NULL, ffi, lib
from ..base import call
from ..scalar import Scalar, _as_scalar
from ..utils import _CArray, ints_to_numpy_buffer, libget, values_to_numpy_buffer, wrapdoc
from ..utils import (
_CArray,
_MatrixArray,
get_order,
ints_to_numpy_buffer,
libget,
normalize_chunks,
values_to_numpy_buffer,
wrapdoc,
)
from .config import BaseConfig
from .descriptor import get_compression_descriptor, get_nthreads_descriptor
from .matrix import MatrixArray, _concat_mn, normalize_chunks
from .matrix import _concat_mn
from .prefix_scan import prefix_scan
from .utils import get_order

ffi_new = ffi.new

Expand Down Expand Up @@ -257,7 +265,7 @@ def split(self, chunks, *, name=None):
call(
"GxB_Matrix_split",
[
MatrixArray(tiles, parent, name="tiles"),
_MatrixArray(tiles, parent, name="tiles"),
_as_scalar(m, _INDEX, is_cscalar=True),
_as_scalar(1, _INDEX, is_cscalar=True),
_CArray(tile_nrows),
Expand Down Expand Up @@ -286,7 +294,7 @@ def _concat(self, tiles, m):
"GxB_Matrix_concat",
[
self._parent._as_matrix(),
MatrixArray(ctiles, name="tiles"),
_MatrixArray(ctiles, name="tiles"),
_as_scalar(m, _INDEX, is_cscalar=True),
_as_scalar(1, _INDEX, is_cscalar=True),
None,
Expand Down
117 changes: 117 additions & 0 deletions graphblas/core/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from numbers import Integral, Number

import numpy as np

from ..dtypes import _INDEX, lookup_dtype
Expand Down Expand Up @@ -109,6 +111,109 @@ def get_shape(nrows, ncols, dtype=None, **arrays):
return nrows, ncols


def get_order(order):
val = order.lower()
if val in {"c", "row", "rows", "rowwise"}:
return "rowwise"
elif val in {"f", "col", "cols", "column", "columns", "colwise", "columnwise"}:
return "columnwise"
else:
raise ValueError(
f"Bad value for order: {order!r}. "
'Expected "rowwise", "columnwise", "rows", "columns", "C", or "F"'
)


def normalize_chunks(chunks, shape):
"""Normalize chunks argument for use by `Matrix.ss.split`.
Examples
--------
>>> shape = (10, 20)
>>> normalize_chunks(10, shape)
[(10,), (10, 10)]
>>> normalize_chunks((10, 10), shape)
[(10,), (10, 10)]
>>> normalize_chunks([None, (5, 15)], shape)
[(10,), (5, 15)]
>>> normalize_chunks((5, (5, None)), shape)
[(5, 5), (5, 15)]
"""
if isinstance(chunks, (list, tuple)):
pass
elif isinstance(chunks, Number):
chunks = (chunks,) * len(shape)
elif isinstance(chunks, np.ndarray):
chunks = chunks.tolist()
else:
raise TypeError(
f"chunks argument must be a list, tuple, or numpy array; got: {type(chunks)}"
)
if len(chunks) != len(shape):
typ = "Vector" if len(shape) == 1 else "Matrix"
raise ValueError(
f"chunks argument must be of length {len(shape)} (one for each dimension of a {typ})"
)
chunksizes = []
for size, chunk in zip(shape, chunks):
if chunk is None:
cur_chunks = [size]
elif isinstance(chunk, Integral) or isinstance(chunk, float) and chunk.is_integer():
chunk = int(chunk)
if chunk < 0:
raise ValueError(f"Chunksize must be greater than 0; got: {chunk}")
div, mod = divmod(size, chunk)
cur_chunks = [chunk] * div
if mod:
cur_chunks.append(mod)
elif isinstance(chunk, (list, tuple)):
cur_chunks = []
none_index = None
for c in chunk:
if isinstance(c, Integral) or isinstance(c, float) and c.is_integer():
c = int(c)
if c < 0:
raise ValueError(f"Chunksize must be greater than 0; got: {c}")
elif c is None:
if none_index is not None:
raise TypeError(
'None value in chunks for "the rest" can only appear once per dimension'
)
none_index = len(cur_chunks)
c = 0
else:
raise TypeError(
"Bad type for element in chunks; expected int or None, but got: "
f"{type(chunks)}"
)
cur_chunks.append(c)
if none_index is not None:
fill = size - sum(cur_chunks)
if fill < 0:
raise ValueError(
"Chunks are too large; None value in chunks would need to be negative "
"to match size of input"
)
cur_chunks[none_index] = fill
elif isinstance(chunk, np.ndarray):
if not np.issubdtype(chunk.dtype, np.integer):
raise TypeError(f"numpy array for chunks must be integer dtype; got {chunk.dtype}")
if chunk.ndim != 1:
raise TypeError(
f"numpy array for chunks must be 1-dimension; got ndim={chunk.ndim}"
)
if (chunk < 0).any():
raise ValueError(f"Chunksize must be greater than 0; got: {chunk[chunk < 0]}")
cur_chunks = chunk.tolist()
else:
raise TypeError(
"Chunks for a dimension must be an integer, a list or tuple of integers, or None."
f" Got: {type(chunk)}"
)
chunksizes.append(cur_chunks)
return chunksizes


class class_property:
__slots__ = "classval", "member_property"

Expand Down Expand Up @@ -175,6 +280,18 @@ def name(self):
return f"&{name}"


class _MatrixArray:
__slots__ = "_carg", "_exc_arg", "name"

def __init__(self, matrices, exc_arg=None, *, name):
from .base import record_raw

self._carg = matrices
self._exc_arg = exc_arg
self.name = name
record_raw(f"GrB_Matrix {name}[{len(matrices)}];")


def _autogenerate_code(
filename,
text,
Expand Down
2 changes: 1 addition & 1 deletion graphblas/tests/test_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2527,7 +2527,7 @@ def test_diag(A, params):


def test_normalize_chunks():
from graphblas.core.ss.matrix import normalize_chunks
from graphblas.core.utils import normalize_chunks

shape = (20, 20)
assert normalize_chunks(10, shape) == [[10, 10], [10, 10]]
Expand Down

0 comments on commit 14d24e3

Please sign in to comment.