Skip to content

Conversation

@Zeroto521
Copy link
Contributor

@Zeroto521 Zeroto521 commented Jan 16, 2026

closes to #1153
np.add.reduce is the inner function for np.ndarray.sum, np.ndarray.mean, and more.

Implements the __array_ufunc__ method in MatrixExpr to handle numpy ufuncs, specifically enabling correct behavior for reductions like np.add.reduce by delegating to the sum method. This improves compatibility with numpy operations.
Moved the sum computation logic from MatrixExpr.sum and __array_ufunc__ into a new _core_sum function for better code reuse and maintainability.
Introduces tests to compare the performance of the mean operation on matrix variables and checks the return types of mean with and without axis argument.
Documented the speed improvement for MatrixExpr.add.reduce using quicksum in the changelog.
The sum method was removed from the MatrixExpr class, consolidating summation logic in the _core_sum function. The docstring for _core_sum was expanded to include detailed parameter and return value descriptions, improving code clarity and maintainability.
def __array_ufunc__(self, ufunc, method, *args, **kwargs):
if method == "reduce":
if ufunc is np.add and isinstance(args[0], MatrixExpr):
return _core_sum(args[0], **kwargs)
Copy link
Contributor Author

@Zeroto521 Zeroto521 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can't use MatrixExpr.sum directly.
Or it will crush the Python kernel.
Use Matrix.sum the calling chain like:

  1. x.mean(1)
  2. x.sum(1)
  3. np.add.reduce
  4. __array_ufunc__
  5. x.sum(1)
  6. __array_ufunc__
  7. np.add.reduce
  8. ...

Deleted the type stub for the sum method in MatrixExpr, likely to reflect changes in the underlying implementation or to correct type information.
Enhanced the __array_ufunc__ method in MatrixExpr to ensure proper array conversion and consistent return types. Added the _ensure_array helper for argument handling. Also removed an unused include of matrix.pxi from expr.pxi.
# which should, in princple, modify the expr. However, since we do not implement __isub__, __sub__
# gets called (I guess) and so a copy is returned.
# Modifying the expression directly would be a bug, given that the expression might be re-used by the user. </pre>
include "matrix.pxi"
Copy link
Contributor Author

@Zeroto521 Zeroto521 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scip.pyi also has this line.
cdef function (_ensure_array) can't be added twice.

Updated MatrixExpr.__matmul__ to return the correct type when the result is not an ndarray. Adjusted tests to reflect the expected return type for 1D matrix multiplication and improved performance test timing logic.
Simplifies timing measurement in matrix sum performance tests by directly calculating elapsed time instead of storing start and end times separately. This improves code readability and reduces variable usage.
Changed the expected type of 1D @ 1D matrix multiplication from MatrixExpr to Expr in test_matrix_matmul_return_type to reflect updated behavior.
Updated tests to use x.view(MatrixExpr) and x.view(np.ndarray) instead of direct subclass method calls. This clarifies the intent and ensures the correct method resolution for sum and mean operations in performance and result comparison tests.
end_orig = time()
# Original sum via `np.ndarray.sum`
start = time()
x.view(np.ndarray).sum()
Copy link
Contributor Author

@Zeroto521 Zeroto521 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np.ndarray.sum is MatrixExpr.sum now. They will both call __array_ufunc__ protocol. So it has to convert MatrixVariable to a normal np.ndarray.


np_res = a.sum(axis, keepdims=keepdims)
scip_res = MatrixExpr.sum(a, axis, keepdims=keepdims)
scip_res = a.view(MatrixExpr).sum(axis, keepdims=keepdims)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np.ndarray.sum is MatrixExpr.sum now.
MatrixExpr.sum(a) will return np.ndarray(..., dtype=int) not MatrixExpr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant