numpy 2.x scalar types np.float32 and others are no longer convertible

未關閉
#511 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

評估

難度
4/5
預估耗時
3-5 天
新手友好度
42/100
Issue 類型
缺陷
描述清晰度
基本清楚
活躍度
停滯
技術堆疊
cpp, numpy
領域
api, backend

研究方向

先從 test_module.cpp 中的重現程式和列出的 Python 純量轉換範例開始,然後追蹤 Boost.Python 為 double、float、int 和 long 提供的內建 rvalue 轉換器,特別是其中的 PyFloat_Check 和 PyLong_Check 路徑。完成的標準是:重現程式不需要明確進行 Python 轉換即可接受 issue 中描述的 NumPy 純量型別,同時保留對內建值的現有行為。

由索引模型根據 Issue 內容生成。

描述

Summary

In NumPy 2.0, scalar types such as np.float32, np.int32, and np.int64 no longer subclass Python's built-in float or int (per
NEP 51). Boost.Python's built-in rvalue converters for C++ double, float, int, and long use PyFloat_Check / PyLong_Check, which return false for these NumPy scalars. The result is ArgumentError: Python argument types did not match C++ signature for any Boost.Python-wrapped function that previously accepted NumPy scalars via implicit conversion.

np.float64 is unaffected because it still subclasses Python float.

Reproducing

Put the following in test_module.cpp:

#include <boost/python.hpp>

double take_double(double x) { return x; }
float take_float(float x) { return x; }
int take_int(int x) { return x; }
long take_long(long x) { return x; }

BOOST_PYTHON_MODULE(test_module)
{
  using namespace boost::python;
  def("take_double", take_double);
  def("take_float", take_float);
  def("take_int", take_int);
  def("take_long", take_long);
}

Then do:

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh -b -p $PWD/mc3
source mc3/etc/profile.d/conda.sh
conda create -y -p $PWD/env -c conda-forge python=3.11 "numpy>=2.0" libboost-python-devel gxx_linux-64
conda activate $PWD/env
$CXX -shared -fPIC -o test_module.so test_module.cpp -Ienv/include/python3.11 -Ienv/include -Lenv/lib -lboost_python311 -Wl,-rpath,env/lib
python -c "import numpy as np; import test_module; test_module.take_double(np.float32(1.5))"

The python script at the bottom of the issue gives some more types that don't convert.

Workaround

Users can explicitly convert before passing to Boost.Python:

func(float(np.float32(1.5)))   # works
func(int(np.int32(42)))        # works
func(np.float32(1.5).item())   # also works

I have implemented this in a few places and I'm sure many others have as well. See https://github.com/cctbx/cctbx_project/issues/1084

Longer python reproducer

import numpy as np
import test_module

# Show which numpy scalars still subclass builtins (the root cause)
for np_type in [np.float64, np.float32, np.float16,
                np.int64, np.int32, np.int16, np.int8]:
  builtin = float if np.issubdtype(np_type, np.floating) else int
  print("issubclass(%s, %s): %s" % (
    np_type.__name__, builtin.__name__, issubclass(np_type, builtin)))
print()

scalar_values = [
  np.float64(1.5),
  np.float32(1.5),
  np.float16(1.5),
  np.int64(42),
  np.int32(42),
  np.int16(42),
  np.int8(42),
  np.uint64(42),
  np.uint32(42),
]

functions = [
  (test_module.take_double, "take_double"),
  (test_module.take_float,  "take_float"),
  (test_module.take_int,    "take_int"),
  (test_module.take_long,   "take_long"),
]

for func, func_name in functions:
  for scalar in scalar_values:
    try:
      result = func(scalar)
      print("Pass", func_name, type(scalar))
    except Exception as e:
      print("Fail", func_name, type(scalar))
主要語言
C++
星號
537
分支
223
平均合併
11 小時 22 分鐘
30 天內合併 PR
2

貢獻指南

這個儲存庫沒有索引到貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

boostorg/python 的其他 Issue

查看 boostorg/python 的全部 Issue

相似的 Issue

更多 C++ Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。