diff --git a/.github/workflows/build-statsmodels.yml b/.github/workflows/build-statsmodels.yml index 4a34640650..633ca97126 100644 --- a/.github/workflows/build-statsmodels.yml +++ b/.github/workflows/build-statsmodels.yml @@ -82,9 +82,11 @@ jobs: CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} # PIP_ONLY_BINARY keeps numpy/scipy/pandas on our registry's riscv64 wheels # instead of source-building the newer releases PyPI offers; setuptools_scm - # is pinned because the shallow tag checkout carries no version history. + # is pinned because the patched tree is dirty at the tag. statsmodels' + # meson.build calls setuptools_scm.get_version() directly with no dist_name, + # so only the unscoped var (not _FOR_STATSMODELS) is honored. CIBW_ENVIRONMENT: >- - SETUPTOOLS_SCM_PRETEND_VERSION_FOR_STATSMODELS=${{ env.STATSMODELS_VERSION }} + SETUPTOOLS_SCM_PRETEND_VERSION=${{ env.STATSMODELS_VERSION }} PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ PIP_ONLY_BINARY=numpy,scipy,pandas MKL_NUM_THREADS=1 diff --git a/docs/packages/statsmodels.yaml b/docs/packages/statsmodels.yaml index 4d7733b3a9..90260ba840 100644 --- a/docs/packages/statsmodels.yaml +++ b/docs/packages/statsmodels.yaml @@ -12,3 +12,4 @@ versions: - filename: statsmodels-0.14.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl sha256: d19c3b8befc7037d9fa9136b12c32b696417a4ea3e93c86632ddd0acd36932c8 requires-python: '>=3.9' +- version: 0.15.0 diff --git a/patches/statsmodels/0.15.0/0001-BUG-use-eigh-for-the-symmetric-matrix-in-_design_kno.patch b/patches/statsmodels/0.15.0/0001-BUG-use-eigh-for-the-symmetric-matrix-in-_design_kno.patch new file mode 100644 index 0000000000..20e656af02 --- /dev/null +++ b/patches/statsmodels/0.15.0/0001-BUG-use-eigh-for-the-symmetric-matrix-in-_design_kno.patch @@ -0,0 +1,41 @@ +From 31c88b884dc8121d00daf79518915788290cc607 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Fri, 18 Sep 2026 06:38:25 +0000 +Subject: [PATCH] BUG: use eigh for the symmetric matrix in + _design_knockoff_equi + +`xcov` is `exog.T @ exog`, so it is symmetric positive semi-definite and its +eigenvalues are real. `np.linalg.eig` calls LAPACK's general `dgeev`, which +returns a complex array whenever rounding leaves any imaginary part non-zero +-- what happens depends on the BLAS kernels, and OpenBLAS's riscv64_generic +ones do it for these matrices where the x86_64 and aarch64 ones do not. +`evmin` is then complex, `min(2 * evmin, 1)` picks it over the real 1, and +`_get_knmat` dies on the first in-place multiply: + + ash *= -np.outer(sl, sl) + numpy._core._exceptions._UFuncOutputCastingError: Cannot cast ufunc + 'multiply' output from dtype('complex128') to dtype('float64') with + casting rule 'same_kind' + +`eigh` uses `dsyevd`, is real by construction on every platform, and agrees +with `eig` to 3e-15 on the same inputs. This is 13 failures in +stats/tests/test_knockoff.py on riscv64 -- every `equi` parametrisation. + +Upstream-Status: To upstream [not yet submitted; pending review of the riscv64 port that found it] +--- + statsmodels/stats/_knockoff.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/statsmodels/stats/_knockoff.py b/statsmodels/stats/_knockoff.py +index cbbbe9f04..a2de99a2e 100644 +--- a/statsmodels/stats/_knockoff.py ++++ b/statsmodels/stats/_knockoff.py +@@ -238,7 +238,7 @@ def _design_knockoff_equi(exog, rng): + exog = exog / xnm + + xcov = np.dot(exog.T, exog) +- ev, _ = np.linalg.eig(xcov) ++ ev, _ = np.linalg.eigh(xcov) + + if np.iscomplexobj(ev): + if np.all(ev.imag == 0):