Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporate changes for numpy 2 #234

Closed
wants to merge 7 commits into from
Closed
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
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ jobs:
archs: "aarch64"

env:
CIBW_SKIP: cp27-* cp35-* cp36-* cp37-* pp* *-musllinux_aarch64 cp312-*_i686
CIBW_SKIP: cp36-* cp37-* cp38-* cp39-* pp* *-musllinux_aarch64 cp312-*_i686
CIBW_ARCHS: ${{matrix.archs}}
CIBW_ARCHS_MACOS: "x86_64 universal2 arm64"
CIBW_BEFORE_BUILD: python -c "print(('#'*130+'\n')*10)" && python -m pip install oldest-supported-numpy
CIBW_BEFORE_BUILD: python -c "print(('#'*130+'\n')*10)" && python -m pip install "numpy>=2.0,<3"
CIBW_TEST_REQUIRES: pytest pytest-cov
CIBW_TEST_COMMAND: "pytest {project}/tests"

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ authors = ["Michael Boyle <[email protected]>"]
homepage = "https://github.com/moble/quaternion"

[build-system]
requires = ["setuptools!=50.0", "wheel", "oldest-supported-numpy"]
requires = ["setuptools!=50.0", "wheel", "numpy>=2.0,<3"]
build-backend = "setuptools.build_meta"

[tool.pytest.ini_options]
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
numpy>=1.13, < 2.0
numpy>=1.13
scipy>=1.5.0
numba>=0.49.1
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
long_description_content_type="text/markdown",
ext_modules=extensions,
install_requires=[
"numpy>=1.13, < 2.0",
"numpy>=1.13",
# See also extras and :environment_marker specs below
],
extras_require={
Expand Down
60 changes: 34 additions & 26 deletions src/numpy_quaternion.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ typedef npy_intp NPY_INTP_CONST;
typedef npy_intp const NPY_INTP_CONST;
#endif

#if NPY_ABI_VERSION < 0x02000000
#define PyArray_DescrProto PyArray_Descr
#define PyDataType_ELSIZE(d) ((d)->elsize)
#define PyDataType_GetArrFuncs(d) ((d)->f)
#endif

// The following definitions, along with `#define NPY_PY3K 1`, can
// also be found in the header <numpy/npy_3kcompat.h>.
#if PY_MAJOR_VERSION >= 3
Expand Down Expand Up @@ -59,6 +65,7 @@ static PyTypeObject PyQuaternion_Type;
// built-in numpy data type. We will describe its features below.
PyArray_Descr* quaternion_descr;

PyArray_DescrProto quaternion_proto = {PyObject_HEAD_INIT(NULL)};

static NPY_INLINE int
PyQuaternion_Check(PyObject* object) {
Expand Down Expand Up @@ -260,7 +267,7 @@ pyquaternion_##fake_name##_array_operator(PyObject* a, PyObject* b) { \
} \
iternext = NpyIter_GetIterNext(iter, NULL); \
innerstride = NpyIter_GetInnerStrideArray(iter)[0]; \
itemsize = NpyIter_GetDescrArray(iter)[1]->elsize; \
itemsize = PyDataType_ELSIZE(NpyIter_GetDescrArray(iter)[1]); \
innersizeptr = NpyIter_GetInnerLoopSizePtr(iter); \
dataptrarray = NpyIter_GetDataPtrArray(iter); \
if(PyArray_EquivTypes(PyArray_DESCR((PyArrayObject*) b), quaternion_descr)) { \
Expand Down Expand Up @@ -927,10 +934,10 @@ QUATERNION_nonzero (char *ip, PyArrayObject *ap)
else {
PyArray_Descr *descr;
descr = PyArray_DescrFromType(NPY_DOUBLE);
descr->f->copyswap(&q.w, ip, !PyArray_ISNOTSWAPPED(ap), NULL);
descr->f->copyswap(&q.x, ip+8, !PyArray_ISNOTSWAPPED(ap), NULL);
descr->f->copyswap(&q.y, ip+16, !PyArray_ISNOTSWAPPED(ap), NULL);
descr->f->copyswap(&q.z, ip+24, !PyArray_ISNOTSWAPPED(ap), NULL);
PyDataType_GetArrFuncs(descr)->copyswap(&q.w, ip, !PyArray_ISNOTSWAPPED(ap), NULL);
PyDataType_GetArrFuncs(descr)->copyswap(&q.x, ip+8, !PyArray_ISNOTSWAPPED(ap), NULL);
PyDataType_GetArrFuncs(descr)->copyswap(&q.y, ip+16, !PyArray_ISNOTSWAPPED(ap), NULL);
PyDataType_GetArrFuncs(descr)->copyswap(&q.z, ip+24, !PyArray_ISNOTSWAPPED(ap), NULL);
Py_DECREF(descr);
}
return (npy_bool) !quaternion_equal(q, zero);
Expand All @@ -942,7 +949,7 @@ QUATERNION_copyswap(quaternion *dst, quaternion *src,
{
PyArray_Descr *descr;
descr = PyArray_DescrFromType(NPY_DOUBLE);
descr->f->copyswapn(dst, sizeof(double), src, sizeof(double), 4, swap, NULL);
PyDataType_GetArrFuncs(descr)->copyswapn(dst, sizeof(double), src, sizeof(double), 4, swap, NULL);
Py_DECREF(descr);
}

Expand All @@ -953,10 +960,10 @@ QUATERNION_copyswapn(quaternion *dst, npy_intp dstride,
{
PyArray_Descr *descr;
descr = PyArray_DescrFromType(NPY_DOUBLE);
descr->f->copyswapn(&dst->w, dstride, &src->w, sstride, n, swap, NULL);
descr->f->copyswapn(&dst->x, dstride, &src->x, sstride, n, swap, NULL);
descr->f->copyswapn(&dst->y, dstride, &src->y, sstride, n, swap, NULL);
descr->f->copyswapn(&dst->z, dstride, &src->z, sstride, n, swap, NULL);
PyDataType_GetArrFuncs(descr)->copyswapn(&dst->w, dstride, &src->w, sstride, n, swap, NULL);
PyDataType_GetArrFuncs(descr)->copyswapn(&dst->x, dstride, &src->x, sstride, n, swap, NULL);
PyDataType_GetArrFuncs(descr)->copyswapn(&dst->y, dstride, &src->y, sstride, n, swap, NULL);
PyDataType_GetArrFuncs(descr)->copyswapn(&dst->z, dstride, &src->z, sstride, n, swap, NULL);
Py_DECREF(descr);
}

Expand Down Expand Up @@ -1481,28 +1488,29 @@ PyMODINIT_FUNC initnumpy_quaternion(void) {
_PyQuaternion_ArrFuncs.fillwithscalar = (PyArray_FillWithScalarFunc*)QUATERNION_fillwithscalar;

// The quaternion array descr
quaternion_descr = PyObject_New(PyArray_Descr, &PyArrayDescr_Type);
quaternion_descr->typeobj = &PyQuaternion_Type;
quaternion_descr->kind = 'V';
quaternion_descr->type = 'q';
quaternion_descr->byteorder = '=';
quaternion_descr->flags = NPY_NEEDS_PYAPI | NPY_USE_GETITEM | NPY_USE_SETITEM;
quaternion_descr->type_num = 0; // assigned at registration
quaternion_descr->elsize = quaternion_elsize;
quaternion_descr->alignment = quaternion_alignment;
quaternion_descr->subarray = NULL;
quaternion_descr->fields = NULL;
quaternion_descr->names = NULL;
quaternion_descr->f = &_PyQuaternion_ArrFuncs;
quaternion_descr->metadata = NULL;
quaternion_descr->c_metadata = NULL;
Py_SET_TYPE(&quaternion_proto, &PyArrayDescr_Type);
quaternion_proto.typeobj = &PyQuaternion_Type;
quaternion_proto.kind = 'V';
quaternion_proto.type = 'q';
quaternion_proto.byteorder = '=';
quaternion_proto.flags = NPY_NEEDS_PYAPI | NPY_USE_GETITEM | NPY_USE_SETITEM;
quaternion_proto.type_num = 0; // assigned at registration
quaternion_proto.elsize = quaternion_elsize;
quaternion_proto.alignment = quaternion_alignment;
quaternion_proto.subarray = NULL;
quaternion_proto.fields = NULL;
quaternion_proto.names = NULL;
quaternion_proto.f = &_PyQuaternion_ArrFuncs;
quaternion_proto.metadata = NULL;
quaternion_proto.c_metadata = NULL;

Py_INCREF(&PyQuaternion_Type);
quaternionNum = PyArray_RegisterDataType(quaternion_descr);
quaternionNum = PyArray_RegisterDataType(&quaternion_proto);

if (quaternionNum < 0) {
INITERROR;
}
quaternion_descr = PyArray_DescrFromType(quaternionNum);

register_cast_function(NPY_BOOL, quaternionNum, (PyArray_VectorUnaryFunc*)BOOL_to_quaternion);
register_cast_function(NPY_BYTE, quaternionNum, (PyArray_VectorUnaryFunc*)BYTE_to_quaternion);
Expand Down
12 changes: 6 additions & 6 deletions src/quaternion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,9 +716,9 @@ def isclose(a, b, rtol=4*np.finfo(float).eps, atol=0.0, equal_nan=False):
Returns a boolean array where two arrays are element-wise equal within a
tolerance.

This function is essentially a copy of the `numpy.isclose` function,
with different default tolerances and one minor changes necessary to
deal correctly with quaternions.
This function is essentially a clone of the `numpy.isclose` function,
with different default tolerances and minor changes necessary to deal
correctly with quaternions.

The tolerance values are positive, typically very small numbers. The
relative difference (`rtol` * abs(`b`)) and the absolute difference
Expand Down Expand Up @@ -780,8 +780,8 @@ def within_tol(x, y, atol, rtol):
result = np.less_equal(abs(x-y), atol + rtol * abs(y))
return result[()]

x = np.array(a, copy=False, subok=True, ndmin=1)
y = np.array(b, copy=False, subok=True, ndmin=1)
x = np.array(a, subok=True, ndmin=1)
y = np.array(b, subok=True, ndmin=1)

# Make sure y is an inexact type to avoid bad behavior on abs(MIN_INT).
# This will cause casting of x later. Also, make sure to allow subclasses
Expand All @@ -790,7 +790,7 @@ def within_tol(x, y, atol, rtol):
dt = np.result_type(y, 1.)
except TypeError:
dt = np.dtype(np.quaternion)
y = np.array(y, dtype=dt, copy=False, subok=True)
y = np.array(y, dtype=dt, subok=True)

xfin = np.isfinite(x)
yfin = np.isfinite(y)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_quaternion.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ def test_as_euler_angles():
R1 = quaternion.from_euler_angles(alpha, beta, gamma)
R2 = quaternion.from_euler_angles(*list(quaternion.as_euler_angles(R1)))
d = quaternion.rotation_intrinsic_distance(R1, R2)
assert d < 6e3*eps, ((alpha, beta, gamma), R1, R2, d) # Can't use allclose here; we don't care about rotor sign
assert d < 5e4*eps, ((alpha, beta, gamma), R1, R2, d) # Can't use allclose here; we don't care about rotor sign
q0 = quaternion.quaternion(0, 0.6, 0.8, 0)
assert q0.norm() == 1.0
assert abs(q0 - quaternion.from_euler_angles(*list(quaternion.as_euler_angles(q0)))) < 1.e-15
Expand Down