Skip to content

Conversation

@ev-br
Copy link
Member

@ev-br ev-br commented Jan 10, 2026

WIP until 2025.12 is released.

  • dask does not have eig, thus xfail the tests
  • numpy needs a shim for return dtype stability
  • cupy needs an xfail until the min cupy version is <=13

data-apis/array-api-tests#404 is the matching test PR

ev-br added 2 commits January 10, 2026 19:32
We need a wrapper because numpy currently returns `float|complex`.

Implementation-wise, follow `linalg.solve` and copy-paste relevant
numpy code with minimal required modifications.
Copilot AI review requested due to automatic review settings January 10, 2026 19:45
@ev-br ev-br marked this pull request as draft January 10, 2026 19:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds compatibility shims for the eig and eigvals linear algebra functions to support the 2025.12 array API standard. The shims ensure dtype stability for NumPy by always returning complex dtypes, as required by the Array API specification, while also handling xfails for libraries that don't yet support these functions.

Changes:

  • Added EigResult namedtuple class to common/_linalg.py
  • Implemented eig() and eigvals() shim functions in numpy/linalg.py with dtype conversion logic
  • Added xfail entries for dask test suite (dask doesn't have these functions yet)

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
dask-xfails.txt Added xfail entries for test_eig and test_eigvals under 2025.12 support section
array_api_compat/common/_linalg.py Added EigResult namedtuple to match the pattern of other result types
array_api_compat/numpy/linalg.py Implemented eig() and eigvals() shims with dtype stability fixes and imported EigResult

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +103 to +139
def eig(x: Array, /) -> tuple[Array, Array]:
try:
from numpy.linalg._linalg import ( # type: ignore[attr-defined]
_assert_stacked_square,
_assert_finite,
_commonType,
_makearray,
_raise_linalgerror_eigenvalues_nonconvergence,
isComplexType,
_complexType,
)
except ImportError:
from numpy.linalg.linalg import ( # type: ignore[attr-defined]
_assert_stacked_square,
_assert_finite,
_commonType,
_makearray,
_raise_linalgerror_eigenvalues_nonconvergence,
isComplexType,
_complexType,
)
from numpy.linalg import _umath_linalg

x, wrap = _makearray(x)
_assert_stacked_square(x)
_assert_finite(x)
t, result_t = _commonType(x)

signature = 'D->DD' if isComplexType(t) else 'd->DD'
with np.errstate(call=_raise_linalgerror_eigenvalues_nonconvergence,
invalid='call', over='ignore', divide='ignore',
under='ignore'):
w, vt = _umath_linalg.eig(x, signature=signature)

result_t = _complexType(result_t)
vt = vt.astype(result_t, copy=False)
return EigResult(w.astype(result_t, copy=False), wrap(vt))
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

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

The return type annotation for the eig function indicates it returns a tuple[Array, Array], but the implementation returns an EigResult (a NamedTuple). This inconsistency should be corrected to return EigResult instead of tuple[Array, Array] to match the actual behavior and to be consistent with similar functions like eigh which properly returns EighResult.

Copilot uses AI. Check for mistakes.
)
from numpy.linalg import _umath_linalg

x, wrap = _makearray(x)
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

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

The variable 'wrap' returned from _makearray is not used in the eigvals function, but is assigned. This suggests either it should be used (similar to how it's used in the eig function to wrap the result) or should be replaced with an underscore to indicate it's intentionally unused.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +177
def eig(x: Array, /) -> tuple[Array, Array]:
try:
from numpy.linalg._linalg import ( # type: ignore[attr-defined]
_assert_stacked_square,
_assert_finite,
_commonType,
_makearray,
_raise_linalgerror_eigenvalues_nonconvergence,
isComplexType,
_complexType,
)
except ImportError:
from numpy.linalg.linalg import ( # type: ignore[attr-defined]
_assert_stacked_square,
_assert_finite,
_commonType,
_makearray,
_raise_linalgerror_eigenvalues_nonconvergence,
isComplexType,
_complexType,
)
from numpy.linalg import _umath_linalg

x, wrap = _makearray(x)
_assert_stacked_square(x)
_assert_finite(x)
t, result_t = _commonType(x)

signature = 'D->DD' if isComplexType(t) else 'd->DD'
with np.errstate(call=_raise_linalgerror_eigenvalues_nonconvergence,
invalid='call', over='ignore', divide='ignore',
under='ignore'):
w, vt = _umath_linalg.eig(x, signature=signature)

result_t = _complexType(result_t)
vt = vt.astype(result_t, copy=False)
return EigResult(w.astype(result_t, copy=False), wrap(vt))


def eigvals(x: Array, /) -> Array:
try:
from numpy.linalg._linalg import ( # type: ignore[attr-defined]
_assert_stacked_square,
_assert_finite,
_commonType,
_makearray,
_raise_linalgerror_eigenvalues_nonconvergence,
isComplexType,
_complexType,
)
except ImportError:
from numpy.linalg.linalg import ( # type: ignore[attr-defined]
_assert_stacked_square,
_assert_finite,
_commonType,
_makearray,
_raise_linalgerror_eigenvalues_nonconvergence,
isComplexType,
_complexType,
)
from numpy.linalg import _umath_linalg

x, wrap = _makearray(x)
_assert_stacked_square(x)
_assert_finite(x)
t, result_t = _commonType(x)

signature = 'D->D' if isComplexType(t) else 'd->D'
with np.errstate(call=_raise_linalgerror_eigenvalues_nonconvergence,
invalid='call', over='ignore', divide='ignore',
under='ignore'):
w = _umath_linalg.eigvals(x, signature=signature)

result_t = _complexType(result_t)
return w.astype(result_t, copy=False)
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

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

There is significant code duplication between the eig and eigvals functions. Both functions import the same set of helper functions from numpy.linalg, perform the same validation steps (_assert_stacked_square, _assert_finite), and use similar dtype handling logic. Consider extracting the common logic into a shared helper function to improve maintainability and reduce duplication.

Copilot uses AI. Check for mistakes.
Comment on lines +132 to +135
# 2025.12 support
array_api_tests/test_linalg.py::test_eig
array_api_tests/test_linalg.py::test_eigvals

Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

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

According to the PR description, CuPy needs xfail entries for eig and eigvals tests until the minimum CuPy version is >=13. However, these xfail entries are missing from cupy-xfails.txt. The dask-xfails.txt file includes the necessary entries (lines 133-134), but cupy-xfails.txt should also have corresponding entries under a "# 2025.12 support" section similar to the pattern used in dask-xfails.txt.

Copilot uses AI. Check for mistakes.
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