Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/build-statsmodels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/packages/statsmodels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
From 31c88b884dc8121d00daf79518915788290cc607 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
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):
Loading