SciPy 1.3.2 维护版本全解析:Python 3.8 支持与 14 项关键 Bug 修复
【免费下载链接】scipySciPy library main repository项目地址: https://gitcode.com/gh_mirrors/sc/scipy
SciPy 1.3.2 是 1.3.x 系列的一个重要 bug-fix 与维护版本,其核心使命是为当时新发布的 Python 3.8 提供正式支持,并集中修复了信号处理、优化、稀疏矩阵、统计等多个子模块中报告的十余个缺陷。阅读本文后,你将全面了解该版本修复的每一个具体问题、对应的底层根因、以及这些修复在 SciPy 源码中的落点,能够据此评估升级收益、规避同类陷阱,并为排查相关问题提供直接线索。
本文内容以官方发布说明 doc/source/release/1.3.2-notes.rst 为主体骨架,结合当前仓库源码与测试用例进行深度印证与扩充。
一、版本定位:Bug-fix 与维护更新
根据官方发布说明,SciPy 1.3.2 的定位非常明确:
SciPy 1.3.2 is a bug-fix and maintenance release that adds support for Python 3.8.
即:这是一个纯缺陷修复与维护型版本,不带入任何新功能,唯一的"新能力"是对 Python 3.8 的正式支持。对于仍停留在 1.3.x 分支的存量用户而言,该版本相当于一个"安全补丁包 + 新 Python 版本适配包",升级风险极低,收益明确。
从维护内容看,该版本横跨了以下子模块与工程维度:
| 维度 | 涉及模块 / 工程 | 典型问题 |
|---|---|---|
| 数值计算正确性 | scipy.stats、scipy.sparse、scipy.linalg | 非中心 F 分布方差公式错误、Floyd-Warshall 丢失零权边、复数子空间角 |
| 求解器行为 | scipy.optimize | NaN 最小值仍报告 success、root_scalar与lru_cache不兼容 |
| 输入类型兼容 | scipy.sparse.linalg、scipy.sparse | expm拒绝np.matrix、CSR 矩阵花式索引赋值 |
| 构建与 CI | pyproject.toml、Travis CI、SuperLU | Python 3.8 适配、macOS 构建失败 |
二、scipy.signal:unique_roots 根分组修复
2.1 问题背景(Issue #4915)
scipy.signal.unique_roots是一个从根列表中确定"唯一根及其重数"的实用函数,在部分分式展开(residue/invres)等场景中承担着关键角色。Issue #4915 报告了该函数在特定根组合下的分组错误:当多个根相互靠近(距离小于容差tol)时,函数的输出与预期不符。
2.2 源码落点与修复方式(PR #10961)
修复后的实现位于 scipy/signal/_signaltools.py。其核心算法在修复后稳定采用"基于cKDTree的贪心分组"策略:
- 将复数根
p拆分为实部与虚部,构造二维点集; - 使用
cKDTree构建空间索引; - 对每个尚未使用的根,调用
tree.query_ball_point(points[i], tol)找出其容差范围内的邻域根集合; - 按
rtype指定的归约规则(max/min/avg)合并该组根,并统计重数。
实现中有两点值得注意的设计细节:
- 分组不具传递性:文档中明确说明,若
a与b接近、b与c接近,不代表a与c接近,因此分组结果依赖输入顺序,采用"按输入顺序贪心分组"; - 复数比较规则:当
rtype为max/min时,复数根先按实部比较、再按虚部比较。
unique_roots的完整签名及参数含义如下:
unique_roots(p, tol=1e-3, rtype='min')| 参数 | 默认值 | 含义 |
|---|---|---|
p | 必填 | 根列表(array_like) |
tol | 1e-3 | 判定两个根"相等"的距离容差 |
rtype | 'min' | 组内代表根的选取方式:'max'/'maximum'取最大、'min'/'minimum'取最小、'avg'/'mean'取平均 |
官方文档中的典型用法:
>>> from scipy import signal >>> vals = [0, 1.3, 1.31, 2.8, 1.25, 2.2, 10.3] >>> uniq, mult = signal.unique_roots(vals, tol=2e-2, rtype='avg') >>> uniq[mult > 1] array([1.305])三、scipy.optimize:三个关键行为修复
3.1 NaN 最小值不再误报成功(Issue #5161,PR #10756)
Issue #5161 报告了一个隐蔽且危险的缺陷:当目标函数的最小值本身是 NaN 时,部分优化器仍然返回success=True,导致用户在完全无效的数值上继续下游计算。PR #10756 修复了该问题,核心改动是确保求解器在目标函数值为 NaN 时以success=False退出,避免对 NaN 结果做出"成功"的误判。
该修复直接关乎 scipy/optimize 下众多求解器的终止条件判断逻辑。在数值优化实践中,这一改动具有重要意义:它把"算法达到收敛条件"与"计算结果数值有效"两个维度区分开来,NaN 结果一律不得被视为优化成功。
3.2 root_scalar 与 functools.lru_cache 的兼容性(Issue #10846,PR #10891)
Issue #10846 报告:当用户向root_scalar传入一个被functools.lru_cache装饰的目标函数时,求解会失败。根因在于 C 层实现的标量求根器在每次调用f时复用同一个args元组对象,而有状态的lru_cache装饰器会持有对该元组的引用,导致缓存键混乱。
修复落点位于 scipy/optimize/_zerosmodule.c。修复后,C 层代码在每次调用目标函数前都会重新构造一个全新的args元组(将当前迭代点x作为第一个参数、其余参数依次复制),源码注释明确写道:
Need to create a new 'args' tuple on each call in case 'f' is stateful and keeps references to it (e.g. functools.lru_cache)
这一改动对brentq、brenth、ridder、bisect、toms748等所有 C 实现的标量求根器均生效。对应的回归测试位于 scipy/optimize/tests/test_zeros.py:
@lru_cache def f_lrucached(x): return x @pytest.mark.parametrize('method', bracket_methods) def test_lru_cached_individual(self, method): # check that https://github.com/scipy/scipy/issues/10846 is fixed a, b = -1, 1 root, r = method(f_lrucached, a, b, full_output=True) assert r.converged assert_allclose(root, 0)实战启示:在使用lru_cache对目标函数做记忆化加速时,SciPy 的 C 层求根器从 1.3.2 起可以安全配合使用。
3.3 linprog revised simplex 文档测试修复(Issue #10124,PR #10498)
Issue #10124 指出linprog(method='revised simplex')的 doctest 存在缺陷——当用户以"disp": True方式开启显示输出时,修订单纯形法的行为与文档示例不一致。PR #10498(TST: optimize: fixed linprog "disp": True bug)修复了该问题,属于测试与文档一致性的修正,相关实现位于 scipy/optimize/_linprog.py 及其修订单纯形法模块 scipy/optimize/_linprog_simplex.py。
四、scipy.sparse 与稀疏线性代数:四项底层修复
4.1 expm 支持 np.matrix 输入(Issue #5546,PR #10906)
Issue #5546 报告:scipy.sparse.linalg.expm在接收np.matrix类型输入时抛出ValueError。修复(BUG: sparse/linalg: fix expm for np.matrix inputs)位于 scipy/sparse/linalg/_matfuncs.py。expm采用 Al-Mohy 与 Higham(2009)的 Scaling and Squaring 算法(算法 6.1,即 5.1 的简化版),此前对np.matrix的维度/类型校验过于严格。修复后该函数可正确接受np.matrix、密集数组与稀疏数组等多种 2D 输入:
from scipy.sparse import csc_array from scipy.sparse.linalg import expm A = csc_array([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) Aexp = expm(A) # 返回 CSC 稀疏数组4.2 CSR 矩阵花式索引赋值修复(Issue #10695,PR #10700)
Issue #10695 报告了csr_matrix在使用花式索引(fancy indexing)赋值时出现的错误。修复(BUG: sparse: Loosen checks on sparse fancy assignment)适当放宽了对稀疏数组赋值的检查逻辑,相关实现位于 scipy/sparse/_csr.py 与底层索引机制 scipy/sparse/_index.py。
4.3 Floyd-Warshall 支持零权边(Issue #10609,PR #10709)
Issue #10609 揭示了一个影响图算法正确性的缺陷:scipy.sparse.csgraph中基于 Floyd-Warshall 的最短路算法会把显式的零权边错误地当作"无边"处理并丢弃,导致最短路结果失真。
修复(BUG: Fix floyd_warshall to support zero-weight edges)位于 scipy/sparse/csgraph/_shortest_path.pyx。该模块中的shortest_path与floyd_warshall是图论计算的底层支撑,零权边(如"免费通行"的路径)在建模中非常常见,此修复保证了权重为零的边与"无连接"(稀疏矩阵中的缺失项)被严格区分。
4.4 ARPACK 复数 Hermitian 求解修复(PR #10882)
PR #10882(BUG: sparse/arpack: fix incorrect code for complex hermitian...)修复了 ARPACK 特征值求解器在复数 Hermitian 问题上的不正确代码路径。这属于稀疏特征值计算 scipy/sparse/linalg/_eigen 相关实现中的底层修正,直接影响eigsh对复数 Hermitian 矩阵的求解正确性。
五、scipy.stats:非中心 F 分布方差公式修正
5.1 问题背景(Issue #10658,PR #10671)
Issue #10658 报告:非中心 F 分布(noncentral F distribution)的方差公式存在错误。作为参数估计、假设检验等统计推断的基础,方差公式错误会直接污染置信区间与检验统计量的计算。
修复(BUG: stats: Fix the formula for the variance of the noncentral F distribution)更新了 scipy/stats/_continuous_distns.py 中ncf_gen分布的方差计算实现。
5.2 回归测试佐证
当前仓库中保留了针对该修复的回归测试 scipy/stats/tests/test_distributions.py:
def test_ncf_variance(): # Regression test for gh-10658 (incorrect variance formula for ncf). # The correct value of ncf.var(2, 6, 4), 42.75, can be verified with, # for example, Wolfram Alpha with the expression # Variance[NoncentralFRatioDistribution[2, 6, 4]] # or with the implementation of the noncentral F distribution in the C++ # library Boost. v = stats.ncf.var(2, 6, 4) assert_allclose(v, 42.75, rtol=1e-14)测试表明,修复后ncf.var(2, 6, 4)的精确期望值为42.75(与 Wolfram Alpha、Boost C++ 库实现交叉验证)。这是 1.3.2 中为数不多的"数值结果可精确断言"的修复,充分体现了数值正确性修复的严谨性。
5.3 文档拼写修正(Issue #10976,PR #10977)
PR #10977(DOC: Fix typo in sp.stats.wilcoxon docstring)修正了scipy.stats.wilcoxon文档字符串中的拼写错误。尽管是小修,但体现了该版本对 API 文档质量的维护。
六、scipy.linalg:subspace_angles 支持复数输入
PR #10833(BUG: Fix subspace_angles for complex values)修复了scipy.linalg.subspace_angles在复数矩阵输入下的计算错误。该函数用于计算两个矩阵列空间之间的主夹角,实现在 scipy/linalg/_decomp_svd.py,基于 SVD 分解计算,返回形状为(min(N, K),)的降序夹角数组:
def subspace_angles(A, B): """ Compute the subspace angles between two matrices. ... angles : ndarray, shape (min(N, K),) The subspace angles between the column spaces of `A` and `B` in descending order. """复数版本的修复对量子计算、信号子空间分析等依赖复数子空间度量的领域尤为关键。
七、Python 3.8 支持与构建维护
1.3.2 版本的另一条主线是 Python 3.8 适配,涉及从 CI 到构建配置的一揽子改动:
| PR | 类型 | 内容 |
|---|---|---|
| #10536 | CI | 在 Travis 中增加3.8-dev构建 |
| #10930 | MAINT | Python 3.8 相关维护性更新 |
| #10938 | MAINT | 在 pyproject.toml 中登记 Python 3.8 |
| #10943 | BLD | 将 Cython 版本升级至0.29.13(首个官方支持 Python 3.8 的 Cython 版本线) |
| #10971 | CI | 在 CI 中改用 3.8 稳定版 |
同时修复了两个平台相关构建问题:
- Issue #10902 / PR #10917:Travis 上的 macOS(osx)构建失败,通过 CI 配置修正解决;
- Issue #10967:macOS 上SuperLU的构建失败(发生于 maintenance/1.3.x 分支),属于平台相关的底层库编译问题。
此外 PR #11025 对 scipy/signal/_peak_finding.py 进行了更新,完善了峰值查找相关代码。
八、完整修复清单:Issues 与 Pull Requests
8.1 本版本关闭的 Issues
| Issue | 主题 |
|---|---|
| #4915 | scipy.signal.unique_roots的根分组 Bug |
| #5161 | 优化器在最小值为 NaN 时错误报告成功 |
| #5546 | scipy.sparse.linalg.expm收到np.matrix输入时抛出ValueError |
| #10124 | linprog(method='revised simplex')doctest Bug |
| #10609 | Floyd-Warshall 最短路会丢弃显式零权边 |
| #10658 | 非中心 F 分布方差公式错误 |
| #10695 | csr_matrix花式索引赋值问题 |
| #10846 | root_scalar处理functools.lru_cache装饰函数时失败 |
| #10902 | Travis osx 构建失败 |
| #10967 | macOS 上 SuperLU 构建失败(maintenance/1.3.x) |
| #10976 | sp.stats.wilcoxon文档字符串拼写错误 |
8.2 本版本合入的 Pull Requests
| PR | 类型 | 内容 |
|---|---|---|
| #10498 | TST | 修复linprog的"disp": TrueBug |
| #10536 | CI | Travis 增加 3.8-dev |
| #10671 | BUG | 修复非中心 F 分布方差公式 |
| #10693 | BUG | ScalarFunction保存原始数组 |
| #10700 | BUG | 放宽稀疏花式赋值的检查 |
| #10709 | BUG | 修复floyd_warshall对零权边的支持 |
| #10756 | BUG | 确保求解器对 NaN 结果以success=False退出 |
| #10833 | BUG | 修复复数输入的subspace_angles |
| #10882 | BUG | 修复 ARPACK 复数 Hermitian 代码 |
| #10891 | BUG | 让 C 实现的求根器兼容functools.lru_cache |
| #10906 | BUG | 修复expm对np.matrix输入的处理 |
| #10917 | CI | 修复 Travis osx CI |
| #10930 | MAINT | Python 3.8 适配更新 |
| #10938 | MAINT | 在pyproject.toml中添加 Python 3.8 |
| #10943 | BLD | 升级 Cython 至 0.29.13 |
| #10961 | BUG | 修复signal.unique_roots |
| #10971 | CI | CI 改用 3.8 稳定版 |
| #10977 | DOC | 修复sp.stats.wilcoxon文档字符串拼写 |
| #11025 | DOC | 更新_peak_finding.py |
九、版本贡献者与升级建议
9.1 贡献者名单
本版本共有14 位贡献者参与(名单由工具自动生成,可能不完全):CJ Carey、Dany Vohl、Martin Gauch(+)、Ralf Gommers、Matt Haberland、Eric Larson、Nikolay Mayorov、Sam McCormack(+)、Andrew Nelson、Tyler Reddy、Pauli Virtanen、Huize Wang(+)、Warren Weckesser、Joseph Weston(+)。其中带"+"标记的 4 位(Martin Gauch、Sam McCormack、Huize Wang、Joseph Weston)是首次贡献补丁的新贡献者。
9.2 升级建议
综合本版本内容,以下几点值得关注:
- Python 3.8 用户应尽快升级:1.3.2 是 1.3.x 系列中首个正式支持 Python 3.8 的版本,配套的 Cython 0.29.13 也完成了适配;
- 依赖数值正确性的场景优先升级:非中心 F 分布方差公式修正(涉及假设检验与置信区间)、Floyd-Warshall 零权边支持(涉及图算法)、复数
subspace_angles(涉及子空间分析)都属于静默性数值错误,在旧版本上不会报错但结果有误,应优先升级并回归验证; - 求解器可靠性提升:NaN 不再误报 success、
root_scalar支持lru_cache装饰函数,使优化代码在极端输入下更安全、更易集成记忆化加速。
从仓库结构看,本版本的修复痕迹完整保留在 scipy/signal/_signaltools.py、scipy/sparse/csgraph/_shortest_path.pyx、scipy/sparse/linalg/_matfuncs.py、scipy/optimize/_zerosmodule.c、scipy/stats/_continuous_distns.py 等文件中,对应回归测试则分布于 scipy/optimize/tests/test_zeros.py 与 scipy/stats/tests/test_distributions.py 等测试套件中,可作为验证升级正确性的参考锚点。
【免费下载链接】scipySciPy library main repository项目地址: https://gitcode.com/gh_mirrors/sc/scipy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考