Skip to content

ENH: enable ILP64 linear algebra - #10784

Merged
esc merged 4 commits into
numba:mainfrom
ev-br:ilp64_
Aug 28, 2026
Merged

esc merged 4 commits into
numba:mainfrom
ev-br:ilp64_

Conversation

@ev-br

@ev-br ev-br commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Intends to close #10777 : enable using ILP64 LAPACK ABI (64-but integers) if scipy exports this ABI. (The latter requires that scipy is built from source.)

User-visible UI largely follows #10777 (comment) : a new NUMBA_LAPACK_ILP64 environment variable selects the target BLAS/LAPACK ABI; if not set, the build system queries the ABI that scipy.linalg.cython_blas exports; if scipy is not available, then falls back to backwards-compatible LP64.

The summary of precise behavior for various combinations is under the fold (Claude generated)

Details

Numba LP64/ILP64 build and runtime matrix

Table 1 — what Numba is built for

NUMBA_LAPACK_ILP64 (if set) overrides the build-environment scipy outright, so these collapse to one effective build width:

NUMBA_LAPACK_ILP64 scipy present at build time Numba built for
1 (or any value not in 0/false/no/empty) irrelevant ILP64
0 / false / no / empty irrelevant LP64
unset ILP64 ILP64
unset LP64 LP64
unset absent / unimportable LP64 (fallback)

Table 2 — what happens at runtime, given what Numba was built for

Checked lazily, only the first time a jitted function actually touches np.linalg/BLAS (ensure_lapack()/ensure_blas()) — plain import numba and non-linalg jitted code are unaffected in every row below.

Numba built for scipy at runtime Outcome
LP64 LP64 Works -- numba_*_32 symbols bound, widths match
LP64 ILP64 RuntimeError (clean refusal, no C call made)
LP64 absent ImportError: scipy 0.16+ is required for linear algebra
ILP64 LP64 RuntimeError (clean refusal, no C call made)
ILP64 ILP64 Works -- numba_*_64 symbols bound, widths match
ILP64 absent ImportError: scipy 0.16+ is required for linear algebra

Only the two diagonal cells (build width == runtime width) actually execute BLAS/LAPACK; every off-diagonal case fails loudly and safely before any C call, rather than corrupting memory.

Caveat

"scipy at runtime" here means what scipy.__config__ reports, which is scipy's own static self-description -- it's what the mismatch check in Table 2 actually reads.
That can itself be misleading for an MKL-SDL-linked scipy (libmkl_rt.so) if MKL_INTERFACE_LAYER is overridden in the environment: MKL's dynamic dispatch can be forced to a different actual width
than __config__ reports, and neither Numba nor scipy currently have visibility into that at runtime.
That failure mode sits outside what this table (or the current runtime check) can see.

The implementation also follows #10777 (comment) : the ABI is decided once at Numba build time, and at runtime the scipy-exported ABI is checked against the build-time expectation.
Internally, there two symbol sets, suffixed with _32 / _64, and the correct one is picked at runtime.

The patch is actually smaller than it looks: there's a large chunk of code moved verbatim from lapack.c to lapack_width.h to avoid code duplication for the two ABI modes. To ease review, the PR is organized into three commits:

  • the first one just moves code from lapack.c to lapack_width.h
  • the second one implements the details of selecting the integer width
  • and the third one adds the build time / run time selection

The patch is generated with heavy assistance from Claude. That said, there is a human in the loop (me), who read, modified and tweaked claude's generated code, is accountable for the contribution (per the linked AI policy) and is happy to discuss details or modify the PR per review requests.


What this PR does not fix are OP usage examples from #10777. Bear with me: if

  • scipy links to mkl_rt, and
  • the numba build mode is LP64, and
  • the user explicitly requests ILP64 via the env variable, MKL_INTERFACE_LAYER=ILP64,GNU

then the ABI choice is being made inside MKL itself, and numba has no knowledge of it, thus running into an ABI mismatch.

I believe this is a user error, and the way to find it is clearly visible from numba -s output, which reports LP64:

Command:
    MKL_INTERFACE_LAYER=ILP64,GNU python -m numba -s

Relevant section of the output:
    __SciPy / LAPACK Information__
    SciPy Version                                 : 1.18.0
    Numba Built For                               : LP64
    SciPy Runtime Provides                        : LP64
    LAPACK Integer Width Matches Build            : True

While this is a footgun, it requires a deliberate user action, and will not happen if scipy is built in the recommended way, against mkl-ilp64-... library, as done in e.g. https://github.com/scipy/scipy/blob/main/.github/workflows/linux_blas.yml#L117-L120

Unless the user manually fiddles with MKL_INTERFACE_LAYER, everything I believe either just works correctly, or fails with a clear RuntimeError.

@ev-br

ev-br commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Note that the CI run does not really probe this new ILP64 mode, since that needs scipy built from source. Am happy to add a CI run if wanted! Meanwhile to test it locally, one needs something like

$ install mkl-devel and clone scipy, install its build dependencies (meson-python, ninja, pythran, cython, pybind11)
$ export PKG_CONFIG_PATH=/path/to/conda/env/lib/pkgconfig
$ cd scipy
$ python -m build -Csetup-args="-Dblas=mkl-dynamic-ilp64-seq" -Csetup-args="-Duse-ilp64=true"  -wnx
$ pip install dist/scipy-2.<tab>
# and now can build numba

@guilhermeleobas

Copy link
Copy Markdown
Contributor

Thanks for the patch @ev-br.

The Numba team discussed this at the last internal meeting and it was decided to not rely on having scipy as a build-time dependency as this might require additional steps on our side. Could you adapt your patch to use only the env var NUMBA_LAPACK_ILP64?

@ev-br

ev-br commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Updated to the suggestion.

The updated variant does not inspect SciPy at build time, thus to use ILP64 linear algebra, users must both set NUMBA_LAPACK_ILP64=1 at numba build time, and have the ILP64-enabled SciPy at runtime.
Any other combination is an error at run time. For example,

$ python setup.py build_ext -i     # build numba, no env var
$ python -c"import scipy; scipy.show_config()" | grep "cython blas"   # scipy is ILP64
      "cython blas ilp64": true
$ python
>>> import numpy as np
>>> import numba
>>> numba.njit(lambda x: x@x)(np.eye(3))
Traceback (most recent call last):
...
RuntimeError: This copy of Numba was built for LP64 BLAS/LAPACK, but the scipy installed now provides ILP64 scipy.linalg.cython_blas/cython_lapack. Numba's compiled BLAS/LAPACK wrappers assume a fixed integer width and will produce wrong results or crash if called against the wrong one, so this is refused rather than attempted. Install a scipy matching what Numba was built for, or rebuild Numba with NUMBA_LAPACK_ILP64 set to match this scipy -- see https://numba.readthedocs.io/en/stable/user/installing.html#numba-source-install-env_vars.

# reinstall scipy from PyPI
$ pip install scipy
$ python
>>> import numpy as np
>>> import numba
>>> numba.njit(lambda x: x@x)(np.eye(3))
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

@seibert

seibert commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

I think this approach is more complex than it needs to be since we decided there is no need to compile both variants and select the backend dynamically. (Practically, no one can build a working ILP environment with wheels from PyPI, so there is no situation where dynamic detection is needed.) I think we instead want the existing _lapack.c but with #define F_INT set at build time to the requested width. We do still want the runtime verification and reporting of the integer width, because that will catch mistakes.

@ev-br

ev-br commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Updated to the suggestion: F_INT is now #define-d in _lapack.c with the bit width requested by an environment variable.

@guilhermeleobas guilhermeleobas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM @ev-br. Before we merge this one, you need to include a release notes file
https://github.com/numba/numba/blob/main/docs/upcoming_changes/README.rst

Comment thread numba/tests/test_linalg.py Outdated
Comment on lines +2225 to +2227
@needs_lapack
@unittest.skipIf(
not has_lapack or not _LAPACK_ILP64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor nit: no need for not has_lapack if @needs_lapack is used

Comment thread numba/tests/support.py
@guilhermeleobas

Copy link
Copy Markdown
Contributor

And sorry, I just realized my gh tool merged main to your PR :/

Evgeni Burovski and others added 3 commits August 25, 2026 08:27
To use ILP64 linear algebra, you *must* set `NUMBA_LAPACK_ILP64=1`
environment variable at build time, *and* have the ILP64-enabled
SciPy at runtime.

Any other combination is an error at run time:

- `NUMBA_LAPACK_ILP64=1 python setup.py build_ext -i` and SciPy is LP64
- `python setup.py build_ext -i` and SciPy is ILP64

both of will error out at run time.

Internally, typedef the F_INT variable to be either int32 or int64,
depending on the build mode. The rest is unchanged: numba pulls
pointers to lapack functions from scipy's cython_lapack.
These functions *must* have the matching ABI, thus check it once
and erorr out on the ABI mismatch.

While at it, fix a small latent bug: two workspace-size scratch variables
in rsyevd/cheevd were plain `int` instead of `F_INT`.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Guilherme Leobas <guilhermeleobas@gmail.com>
@ev-br

ev-br commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @guilhermeleobas , I added a tentative release note snippet, and addressed your review comments. And merged main again, so that the PR is squeaky clean :-).

@esc esc added 5 - Ready to merge Review and testing done, is ready to merge and removed 3 - Ready for Review labels Aug 28, 2026
@esc

esc commented Aug 28, 2026

Copy link
Copy Markdown
Member

Thank you for patch and review.

@esc
esc merged commit 731b36d into numba:main Aug 28, 2026
110 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5 - Ready to merge Review and testing done, is ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should we have scipy as a build-time dependency?

5 participants