From 54e2faf5b957e423fcd7f3b9bd8c4db0ebcf1408 Mon Sep 17 00:00:00 2001 From: Transurgeon Date: Fri, 25 Aug 2023 20:45:05 -0400 Subject: [PATCH 1/3] adding order=None to new() --- graphblas/core/expr.py | 2 +- graphblas/core/mask.py | 2 +- graphblas/core/matrix.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/graphblas/core/expr.py b/graphblas/core/expr.py index d803939a5..99a386e5c 100644 --- a/graphblas/core/expr.py +++ b/graphblas/core/expr.py @@ -486,7 +486,7 @@ def __init__(self, left, right): self.right = right self._expr = None - def new(self, dtype=None, *, mask=None, name=None, **opts): + def new(self, dtype=None, *, mask=None, order=None, name=None, **opts): if ( mask is None and self._expr is not None diff --git a/graphblas/core/mask.py b/graphblas/core/mask.py index 3bda2188a..cdbc975bf 100644 --- a/graphblas/core/mask.py +++ b/graphblas/core/mask.py @@ -31,7 +31,7 @@ def _repr_html_(self): def _carg(self): return self.parent.gb_obj[0] - def new(self, dtype=None, *, complement=False, mask=None, name=None, **opts): + def new(self, dtype=None, *, complement=False, mask=None, order=None, name=None, **opts): """Return a new object with True values determined by the mask(s). By default, the result is True wherever the mask(s) would have been applied, diff --git a/graphblas/core/matrix.py b/graphblas/core/matrix.py index d820ca424..e2a01fbe3 100644 --- a/graphblas/core/matrix.py +++ b/graphblas/core/matrix.py @@ -175,7 +175,7 @@ class Matrix(BaseType): _name_counter = itertools.count() __networkx_plugin__ = "graphblas" - def __new__(cls, dtype=FP64, nrows=0, ncols=0, *, name=None): + def __new__(cls, dtype=FP64, nrows=0, ncols=0, order=None, *, name=None): self = object.__new__(cls) self.dtype = lookup_dtype(dtype) nrows = _as_scalar(nrows, _INDEX, is_cscalar=True) @@ -3633,7 +3633,7 @@ def _repr_html_(self, collapse=False): return format_matrix_html(self, collapse=collapse) - def new(self, dtype=None, *, mask=None, name=None, **opts): + def new(self, dtype=None, *, mask=None, name=None, order=None, **opts): if dtype is None: dtype = self.dtype output = Matrix(dtype, self._nrows, self._ncols, name=name) From e1b00d66502a4a76cec05b263f6abda68e9f84bf Mon Sep 17 00:00:00 2001 From: Transurgeon Date: Fri, 25 Aug 2023 20:49:35 -0400 Subject: [PATCH 2/3] adding to correct classes --- graphblas/core/expr.py | 2 +- graphblas/core/infix.py | 2 +- graphblas/core/matrix.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/graphblas/core/expr.py b/graphblas/core/expr.py index 99a386e5c..d803939a5 100644 --- a/graphblas/core/expr.py +++ b/graphblas/core/expr.py @@ -486,7 +486,7 @@ def __init__(self, left, right): self.right = right self._expr = None - def new(self, dtype=None, *, mask=None, order=None, name=None, **opts): + def new(self, dtype=None, *, mask=None, name=None, **opts): if ( mask is None and self._expr is not None diff --git a/graphblas/core/infix.py b/graphblas/core/infix.py index 88fc52dbe..a37fb0517 100644 --- a/graphblas/core/infix.py +++ b/graphblas/core/infix.py @@ -271,7 +271,7 @@ class MatrixInfixExpr(InfixExprBase): _is_transposed = False __networkx_plugin__ = "graphblas" - def __init__(self, left, right): + def __init__(self, left, right, order=None): super().__init__(left, right) if left.ndim == 1: self._nrows = right._nrows diff --git a/graphblas/core/matrix.py b/graphblas/core/matrix.py index e2a01fbe3..a4ab39de2 100644 --- a/graphblas/core/matrix.py +++ b/graphblas/core/matrix.py @@ -3388,6 +3388,7 @@ def __init__( expr_repr=None, ncols=None, nrows=None, + order=None, ): super().__init__( method_name, @@ -3517,7 +3518,7 @@ class MatrixIndexExpr(AmbiguousAssignOrExtract): _is_transposed = False __networkx_plugin__ = "graphblas" - def __init__(self, parent, resolved_indexes, nrows, ncols): + def __init__(self, parent, resolved_indexes, nrows, ncols, order=None): super().__init__(parent, resolved_indexes) self._nrows = nrows self._ncols = ncols From b499666ee6d67dbf236d1bfe7968cf896868c22e Mon Sep 17 00:00:00 2001 From: Transurgeon Date: Mon, 4 Sep 2023 16:08:48 -0400 Subject: [PATCH 3/3] trying to implement column order --- graphblas/core/matrix.py | 13 +++++- graphblas/core/ss/matrix.py | 7 ++++ graphblas/tests/test_matrix.py | 74 ++++++++++++++++++---------------- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/graphblas/core/matrix.py b/graphblas/core/matrix.py index a4ab39de2..6d952218e 100644 --- a/graphblas/core/matrix.py +++ b/graphblas/core/matrix.py @@ -165,6 +165,8 @@ class Matrix(BaseType): Number of rows. ncols : int Number of columns. + order : str, optional + Orientation of the Matrix. name : str, optional Name to give the Matrix. This will be displayed in the ``__repr__``. """ @@ -188,6 +190,8 @@ def __new__(cls, dtype=FP64, nrows=0, ncols=0, order=None, *, name=None): self._parent = None if backend == "suitesparse": self.ss = ss(self) + if order is not None: + self.ss.orientation = get_order(order) return self @classmethod @@ -3518,10 +3522,11 @@ class MatrixIndexExpr(AmbiguousAssignOrExtract): _is_transposed = False __networkx_plugin__ = "graphblas" - def __init__(self, parent, resolved_indexes, nrows, ncols, order=None): + def __init__(self, parent, resolved_indexes, nrows, ncols, order): super().__init__(parent, resolved_indexes) self._nrows = nrows self._ncols = ncols + self.ss.orientation = order @property def ncols(self): @@ -3637,7 +3642,11 @@ def _repr_html_(self, collapse=False): def new(self, dtype=None, *, mask=None, name=None, order=None, **opts): if dtype is None: dtype = self.dtype - output = Matrix(dtype, self._nrows, self._ncols, name=name) + if order is None: + order = self.ss.orientation + else: + order = get_order(order) + output = Matrix(dtype, self._nrows, self._ncols, order, name=name) output(mask=mask, **opts) << self return output diff --git a/graphblas/core/ss/matrix.py b/graphblas/core/ss/matrix.py index 64914cf02..474b105a7 100644 --- a/graphblas/core/ss/matrix.py +++ b/graphblas/core/ss/matrix.py @@ -249,6 +249,13 @@ def orientation(self): return "columnwise" return "rowwise" + @orientation.setter + def orientation(self, order): + if order == "columnwise": + return "columnwise" + else: + return "rowwise" + def build_diag(self, vector, k=0, **opts): """ GxB_Matrix_diag. diff --git a/graphblas/tests/test_matrix.py b/graphblas/tests/test_matrix.py index cd70479cc..e97256504 100644 --- a/graphblas/tests/test_matrix.py +++ b/graphblas/tests/test_matrix.py @@ -143,7 +143,7 @@ def test_from_coo(): assert C4.isequal(C5, check_dtype=True) with pytest.raises( - ValueError, match="`rows` and `columns` and `values` lengths must match: 1, 2, 1" + ValueError, match="`rows` and `columns` and `values` lengths must match: 1, 2, 1" ): Matrix.from_coo([0], [1, 2], [0]) @@ -521,27 +521,27 @@ def test_extract_input_mask(): with pytest.raises(ValueError, match="Shape of `input_mask` does not match shape of input"): m(input_mask=MT.S) << A[0, [0, 1]] with pytest.raises( - ValueError, match="Size of `input_mask` Vector does not match ncols of Matrix" + ValueError, match="Size of `input_mask` Vector does not match ncols of Matrix" ): A[0, [0]].new(input_mask=expected.S) with pytest.raises( - ValueError, match="Size of `input_mask` Vector does not match ncols of Matrix" + ValueError, match="Size of `input_mask` Vector does not match ncols of Matrix" ): m(input_mask=expected.S) << A[0, [0]] with pytest.raises( - ValueError, match="Size of `input_mask` Vector does not match nrows of Matrix" + ValueError, match="Size of `input_mask` Vector does not match nrows of Matrix" ): A[[0], 0].new(input_mask=m.S) with pytest.raises( - ValueError, match="Size of `input_mask` Vector does not match nrows of Matrix" + ValueError, match="Size of `input_mask` Vector does not match nrows of Matrix" ): m(input_mask=m.S) << A[[0], 0] with pytest.raises( - TypeError, match="Got Vector `input_mask` when extracting a submatrix from a Matrix" + TypeError, match="Got Vector `input_mask` when extracting a submatrix from a Matrix" ): A[[0], [0]].new(input_mask=expected.S) with pytest.raises( - TypeError, match="Got Vector `input_mask` when extracting a submatrix from a Matrix" + TypeError, match="Got Vector `input_mask` when extracting a submatrix from a Matrix" ): A(input_mask=expected.S) << A[[0], [0]] with pytest.raises(ValueError, match="input_mask"): @@ -788,11 +788,11 @@ def test_assign_row_scalar(A, v): C[:, :](C.S) << 1 with pytest.raises( - TypeError, match="Unable to use Vector mask on Matrix assignment to a Matrix" + TypeError, match="Unable to use Vector mask on Matrix assignment to a Matrix" ): C[:, :](v.S) << 1 with pytest.raises( - TypeError, match="Unable to use Vector mask on single element assignment to a Matrix" + TypeError, match="Unable to use Vector mask on single element assignment to a Matrix" ): C[0, 0](v.S) << 1 @@ -905,7 +905,7 @@ def test_assign_row_col_matrix_mask(): assert C.isequal(result) with pytest.raises( - TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" + TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" ): C[0, :](B.S) << v2 @@ -921,7 +921,7 @@ def test_assign_row_col_matrix_mask(): assert C.isequal(result) with pytest.raises( - TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" + TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" ): C[:, 0](B.S) << v2 @@ -937,7 +937,7 @@ def test_assign_row_col_matrix_mask(): assert C.isequal(result) with pytest.raises( - TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" + TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" ): C[:, 0](B.S) << 100 @@ -953,7 +953,7 @@ def test_assign_row_col_matrix_mask(): assert C.isequal(result) with pytest.raises( - TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" + TypeError, match="Indices for subassign imply Vector submask, but got Matrix mask instead" ): C[:, 0](B.S) << 100 @@ -1422,7 +1422,7 @@ def test_reduce_agg(A): assert A.reduce_scalar(agg.count_nonzero).new() == 12 assert A.reduce_scalar(agg.count_zero).new() == 0 assert A.reduce_scalar(agg.sum_of_squares).new() == 245 - assert A.reduce_scalar(agg.hypot).new().isclose(245**0.5) + assert A.reduce_scalar(agg.hypot).new().isclose(245 ** 0.5) assert A.reduce_scalar(agg.logaddexp).new().isclose(8.6071076) assert A.reduce_scalar(agg.logaddexp2).new().isclose(9.2288187) assert A.reduce_scalar(agg.mean).new().isclose(47 / 12) @@ -1482,7 +1482,7 @@ def test_reduce_agg_argminmax(A): # reduce_scalar with pytest.raises( - ValueError, match="Aggregator argmin may not be used with Matrix.reduce_scalar" + ValueError, match="Aggregator argmin may not be used with Matrix.reduce_scalar" ): A.reduce_scalar(agg.ss.argmin) @@ -2422,12 +2422,12 @@ def test_ss_import_export_auto(A, do_iso, methods): "cooc", ]: for ( - sort, - raw, - import_format, - give_ownership, - take_ownership, - import_name, + sort, + raw, + import_format, + give_ownership, + take_ownership, + import_name, ) in itertools.product( [False, True], [False, True], @@ -2473,11 +2473,11 @@ def import_func(x, import_name, **kwargs): C_orig = C.dup() for format in ["fullr", "fullc"]: for raw, import_format, give_ownership, take_ownership, import_name in itertools.product( - [False, True], - [format, None], - [False, True], - [False, True], - ["any", format], + [False, True], + [format, None], + [False, True], + [False, True], + ["any", format], ): assert C.shape == (C.nrows, C.ncols) C2 = C.dup() if give_ownership or out_method == "unpack" else C @@ -2646,7 +2646,7 @@ def test_not_to_array(A): with pytest.raises(TypeError, match="Matrix can't be directly converted to a numpy array"): np.array(A) with pytest.raises( - TypeError, match="TransposedMatrix can't be directly converted to a numpy array" + TypeError, match="TransposedMatrix can't be directly converted to a numpy array" ): np.array(A.T) @@ -2745,14 +2745,14 @@ def test_ss_concat(A, v): assert B1.dtype == "FP64" expected = Matrix(A.dtype, nrows=A.nrows, ncols=2 * A.ncols) expected[:, : A.ncols] = A - expected[:, A.ncols :] = A + expected[:, A.ncols:] = A assert B1.isequal(expected) B2 = Matrix(A.dtype, nrows=2 * A.nrows, ncols=A.ncols) B2.ss.concat([[A], [A]]) expected = Matrix(A.dtype, nrows=2 * A.nrows, ncols=A.ncols) expected[: A.nrows, :] = A - expected[A.nrows :, :] = A + expected[A.nrows:, :] = A assert B2.isequal(expected) tiles = A.ss.split([4, 3]) @@ -2783,7 +2783,7 @@ def test_ss_concat(A, v): B4 = gb.ss.concat([[v], [v]]) expected = Matrix(v.dtype, nrows=2 * v.size, ncols=1) expected[: v.size, 0] = v - expected[v.size :, 0] = v + expected[v.size:, 0] = v assert B4.isequal(expected) B5 = gb.ss.concat([[A, v]]) @@ -3032,7 +3032,7 @@ def test_dup_expr(A): result = (A + A).dup(float, clear=True) assert result.isequal(A.dup(float, clear=True), check_dtype=True) result = (A * A).dup(mask=A.V) - assert result.isequal((A**2).new(mask=A.V)) + assert result.isequal((A ** 2).new(mask=A.V)) result = A[:, :].dup() assert result.isequal(A) result = A[:, :].dup(clear=True) @@ -3158,8 +3158,8 @@ def test_infix_sugar(A): if shouldhave(binary.numpy, "mod"): assert binary.numpy.mod(A, 2).isequal(A % 2) assert binary.numpy.mod(5, A).isequal(5 % A) - assert binary.pow(A, 2).isequal(A**2) - assert binary.pow(2, A).isequal(2**A) + assert binary.pow(A, 2).isequal(A ** 2) + assert binary.pow(2, A).isequal(2 ** A) assert binary.pow(A, 2).isequal(pow(A, 2)) assert unary.ainv(A).isequal(-A) assert unary.ainv(A.T).isequal(-A.T) @@ -3895,7 +3895,7 @@ def test_bool_as_mask(A): @pytest.mark.skipif("not suitesparse") def test_ss_serialize(A): for compression, level, nthreads in itertools.product( - [None, "none", "default", "lz4", "lz4hc", "zstd"], [None, 1, 5, 9], [None, -1, 1, 10] + [None, "none", "default", "lz4", "lz4hc", "zstd"], [None, 1, 5, 9], [None, -1, 1, 10] ): if level is not None and compression not in {"lz4hc", "zstd"}: with pytest.raises(TypeError, match="level argument"): @@ -4393,3 +4393,9 @@ def test_power(A): B = A[:2, :3].new() with pytest.raises(DimensionMismatch): B.power(2) + + +def test_setting_orientation(A): + A = gb.Matrix.from_csc([0, 2, 3, 3, 5], [1, 3, 7, 6, 20], [10, 20, 30, 40, 50]) + B = A.T.new(order="col") + assert B.ss.orientation == "columnwise"