1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
From: Stefano Rivera <stefanor@debian.org>
Date: Mon, 22 Nov 2021 13:38:59 -0400
Subject: Python 3.10 Support
In Python 3.10, NaNs no longer hash equal, instead they are hashed by
their object's address. (See: https://bugs.python.org/issue43475)
Replace that behaviour by passing the address of the object to the
hash() function.
This is all rather messy, I'm sure someone more familiar with C++
templates can do something a lot tidier...
Bug-Upstream: https://gitlab.kwant-project.org/kwant/tinyarray/-/issues/20
Forwarded: https://gitlab.kwant-project.org/kwant/tinyarray/-/merge_requests/14
---
src/array.cc | 24 +++++++++++++++---------
test_tinyarray.py | 2 +-
2 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/src/array.cc b/src/array.cc
index 12b700e..c4851c6 100644
--- a/src/array.cc
+++ b/src/array.cc
@@ -815,7 +815,7 @@ fail:
// the only documentation for this is in the Python sourcecode
const Py_hash_t HASH_IMAG = _PyHASH_IMAG;
-Py_hash_t hash(long x)
+Py_hash_t hash(void *inst, long x)
{
// For integers the hash is just the integer itself modulo _PyHASH_MODULUS
// except for the singular case of -1.
@@ -834,24 +834,28 @@ typedef long Py_hash_t;
typedef unsigned long Py_uhash_t;
const Py_hash_t HASH_IMAG = 1000003L;
-Py_hash_t hash(long x)
+Py_hash_t hash(void *inst, long x)
{
return x != -1 ? x : -2;
}
#endif
-Py_hash_t hash(double x)
+Py_hash_t hash(void *inst, double x)
{
// We used to have our own implementation of this, but the extra function
// call is quite negligible compared to the execution time of the function.
+#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 10
+ return _Py_HashDouble((PyObject *)inst, x);
+#else
return _Py_HashDouble(x);
+#endif
}
-Py_hash_t hash(Complex x)
+Py_hash_t hash(void *inst, Complex x)
{
// x.imag == 0 => hash(x.imag) == 0 => hash(x) == hash(x.real)
- return hash(x.real()) + HASH_IMAG * hash(x.imag());
+ return hash(inst, x.real()) + HASH_IMAG * hash(inst, x.imag());
}
// The following routine calculates the hash of a multi-dimensional array. The
@@ -875,7 +879,7 @@ Py_hash_t hash(PyObject *obj)
Array<T> *self = reinterpret_cast<Array<T> *>(obj);
self->ndim_shape(&ndim, &shape);
T *p = self->data();
- if (ndim == 0) return hash(*p);
+ if (ndim == 0) return hash(p, *p);
const Py_uhash_t mult_init = 1000003, r_init = 0x345678;
const Py_uhash_t mul_addend = 82520, r_addend = 97531;
@@ -891,7 +895,8 @@ Py_hash_t hash(PyObject *obj)
--i[d];
if (d == ndim) {
// Innermost loop body.
- r[d] = (r[d] ^ hash(*p++)) * mult[d];
+ r[d] = (r[d] ^ hash(p, *p)) * mult[d];
+ p++;
mult[d] += mul_addend + 2 * i[d];
} else {
// Entering a loop.
@@ -958,7 +963,7 @@ Py_hash_t hash(PyObject *obj)
Array<T> *self = reinterpret_cast<Array<T> *>(obj);
self->ndim_shape(&ndim, &shape);
T *p = self->data();
- if (ndim == 0) return hash(*p);
+ if (ndim == 0) return hash(p, *p);
Py_ssize_t i[max_ndim];
Py_uhash_t acc[max_ndim];
@@ -971,7 +976,8 @@ Py_hash_t hash(PyObject *obj)
if (i[d]) {
--i[d];
if (d == ndim) {
- _hash_inner_loop(acc[d], hash(*p++));
+ _hash_inner_loop(acc[d], hash(p, *p));
+ p++;
} else {
++d;
i[d] = shape[d];
diff --git a/test_tinyarray.py b/test_tinyarray.py
index ebacd4e..c78edf7 100644
--- a/test_tinyarray.py
+++ b/test_tinyarray.py
@@ -313,7 +313,7 @@ def test_hash_equality():
int_bits = (8 * ta.dtype_size[int]) - 1 # 8 bits per byte, minus 1 sign bit
maxint = 2**(int_bits)
- special = [float('nan'), float('inf'), float('-inf'),
+ special = [float('inf'), float('-inf'),
0, -1, -1.0, -1 + 0j,
303, -312424, -0.3, 1.7, 0.4j, -12.3j, 1 - 12.3j, 1.3 - 12.3j,
(), (-1,), (2,),
|