You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Setting or propagating orientation (rowwise or columnwise) of a Matrix #492
The default orientation of matrices is rowwise. This can be changed globally in SuiteSparse:GraphBLAS with gb.ss.config["format"] = "by_col", and a single matrix can be changed via A.ss.config["format"] = "by_col".
Using matrices with non-default orientation may result in surprising behavior. For example, would users expect elementwise operations to result in the orientation changing (such as B = (A + 1).new())? More illustrations below:
Perhaps this isn't surprising, since a global default means just that: everything is predictable, and new objects by default will use the default orientation. If you want to control the orientation of the output, then this is possible by first creating the output object and setting its orientation.
Some questions:
should we consider ever propagating the orientation of input matrices to the automatically created result matrix?
should we consider a way to specify orientation (and other config?) when creating new objects?
e.g. B = (A + 1).new(order="col")
e.g. B = gb.ss.concat([[A, A]], order="F")
e.g. B = A[:4, :4].new(order="columnwise")
It's worth noting that the upcoming GraphBLAS 2.1 specification will introduce the ability to introspect a Matrix for its orientation.
Hey thanks for bringing this up Erik, I think it would be a great addition.
I also would prefer .new(order="columwise") but I think all three options are equally suitable for this.
How difficult would it be to propagate the orientation of the input matrices to the result matrix? I feel like this is how it should work (intuitively) anyways. (as in, if someone cared enough about orientation to set a matrix to col, it should probably propagate)
Could you provide some pointers on how one could get started on adding this? I would love to give it a go.
I think I would approach this as three separate tasks (and separate PRs):
Add order=None to .new() methods on matrix-like objects and also the constructors for which it makes sense
I would start by adding it to TransposedMatrix.new, which will also need it added to Matrix.__new__.
Other matrix-like classes with .new: MatrixInfixExpr, MatrixExpression, MatrixIndexExpr (and masks?)
Add matrix.orientation or matrix.order or whatever attribute that can also be assigned the order (or None)
Getting and setting it today should only work if backend == "suitesparse" by using A.ss.orientation, otherwise raise NotImplementedError (b/c we're waiting on the next version of the spec to be released)
Propagate the orientation of the input matrices
Again, I would begin small, such as Transposed.new
It may be a little trickier to do this elsewhere, but it may be best to determine the orientation to propagate when e.g. MatrixExpression is created.
Working on any of these would be a great learning experience. Please let us know how we can make contributing easier or the code easier to navigate. I'm happy to assist and teach, so please ping us/me if you have any questions or blockers.
Btw, our get_order utility function already handles all the variations of order=... that I shared.
Hey @eriknw , thanks for your explanations.
If I understand correctly, the first PR is just there to add an optional argument order to the new() constructor for various Matrix objects. It won't cover any implementation or actually setting the order.
P.S could you give a primer on masks? In my understanding they are used to select a subset of the output (given a certain condition). How come we would need to add order to masks?
P.S.S I noticed that I am traversing the repo quite inefficiently (when searching for classes for example), I know about grep but was wondering if you had any efficiency related tips & tricks.
I would recommend implementing and testing anything that gets added, so maybe the first two items should be swapped or done simultaneously.
Quick tour of classes! These all have .new
In [1]: importgraphblasasgbIn [2]: A=gb.Matrix(bool)
In [3]: type(A.T)
Out[3]: graphblas.core.matrix.TransposedMatrixIn [4]: type(A&A) # pass to monoid or binary operator gives MatrixExpression (see example below)Out[4]: graphblas.core.infix.MatrixEwiseMultExprIn [5]: type(A|A) # pass to monoid or binary operator gives MatrixExpression (see example below)Out[5]: graphblas.core.infix.MatrixEwiseAddExprIn [6]: type(A @ A) # pass to semiring operator gives MatrixExpression (see example below)Out[6]: graphblas.core.infix.MatrixMatMulExprIn [7]: type(A[:, :]) # used for assign and extractOut[7]: graphblas.core.matrix.MatrixIndexExpr
Masks also have .new, which results in boolean matrices. We don't have e.g. MatrixMask yet, but maybe we should?
We have a couple more objects that do not have .new methods. These are used to update a matrix in-place. Note that when I call A() below, one would typically pass a mask and/or and accumulator such as A("+") or A(B.S).
Activity
jim22k commentedon Aug 23, 2023
I prefer
.new(order="columnwise")Transurgeon commentedon Aug 23, 2023
Hey thanks for bringing this up Erik, I think it would be a great addition.
I also would prefer
.new(order="columwise")but I think all three options are equally suitable for this.How difficult would it be to propagate the orientation of the input matrices to the result matrix? I feel like this is how it should work (intuitively) anyways. (as in, if someone cared enough about orientation to set a matrix to col, it should probably propagate)
Could you provide some pointers on how one could get started on adding this? I would love to give it a go.
eriknw commentedon Aug 24, 2023
Great, thanks @Transurgeon!
I think I would approach this as three separate tasks (and separate PRs):
order=Noneto.new()methods on matrix-like objects and also the constructors for which it makes senseTransposedMatrix.new, which will also need it added toMatrix.__new__..new:MatrixInfixExpr,MatrixExpression,MatrixIndexExpr(and masks?)matrix.orientationormatrix.orderor whatever attribute that can also be assigned the order (or None)backend == "suitesparse"by usingA.ss.orientation, otherwise raiseNotImplementedError(b/c we're waiting on the next version of the spec to be released)Transposed.newMatrixExpressionis created.Working on any of these would be a great learning experience. Please let us know how we can make contributing easier or the code easier to navigate. I'm happy to assist and teach, so please ping us/me if you have any questions or blockers.
Btw, our
get_orderutility function already handles all the variations oforder=...that I shared.{"C", "row", "rows", "rowwise"} -> "rowwise"{"F", "col", "cols", "column", "columns", "colwise", "columnwise"} -> "columnwise"Transurgeon commentedon Aug 26, 2023
Hey @eriknw , thanks for your explanations.
If I understand correctly, the first PR is just there to add an optional argument
orderto the new() constructor for various Matrix objects. It won't cover any implementation or actually setting the order.P.S could you give a primer on masks? In my understanding they are used to select a subset of the output (given a certain condition). How come we would need to add order to masks?
P.S.S I noticed that I am traversing the repo quite inefficiently (when searching for classes for example), I know about
grepbut was wondering if you had any efficiency related tips & tricks.eriknw commentedon Aug 27, 2023
I would recommend implementing and testing anything that gets added, so maybe the first two items should be swapped or done simultaneously.
Quick tour of classes! These all have
.newMasks also have
.new, which results in boolean matrices. We don't have e.g.MatrixMaskyet, but maybe we should?MatrixExpressionis the bread-and-butter of expressions--they are just waiting on an output to update or.newto be called:We have a couple more objects that do not have
.newmethods. These are used to update a matrix in-place. Note that when I callA()below, one would typically pass a mask and/or and accumulator such asA("+")orA(B.S).Most implementation occurs in
graphblas.core. To begin, you are likely to need to poke around inmatrix,infix, andexprundergraphblas.core.I'll share more about masks soon.