From d487b32774805d14249a15fc030aeac148025b67 Mon Sep 17 00:00:00 2001 From: Karl Nelson Date: Sun, 16 Jun 2024 21:47:03 -0700 Subject: [PATCH 1/2] Changes for numpy 2.0 to fix test matrix. --- doc/CHANGELOG.rst | 5 ++ native/common/jp_convert.cpp | 76 ++++++++++++++++++++++++++ test/jpypetest/test_conversionInt.py | 4 +- test/jpypetest/test_conversionLong.py | 4 +- test/jpypetest/test_conversionShort.py | 4 +- test/jpypetest/test_jboolean.py | 4 +- test/jpypetest/test_jbyte.py | 4 +- test/jpypetest/test_jdouble.py | 16 +++++- test/jpypetest/test_jfloat.py | 16 +++++- 9 files changed, 119 insertions(+), 14 deletions(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 5848e7d66..217dd8320 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -19,6 +19,11 @@ Latest Changes: For editable installs, ``python setup.py --enable-tracing develop`` must now be done with ``python setup.py develop --enable-tracing``. + - Update for tests for numpy 2.0. + + - Support of np.float16 conversion with arrays. + + - **1.5.0 - 2023-04-03** - Support for Python 3.12 diff --git a/native/common/jp_convert.cpp b/native/common/jp_convert.cpp index 919417461..5d288e044 100644 --- a/native/common/jp_convert.cpp +++ b/native/common/jp_convert.cpp @@ -14,10 +14,61 @@ See NOTICE file for details. *****************************************************************************/ #include "jpype.h" +#include +#include namespace { +template +class Half +{ +public: + static jvalue convert(void* c) + { + uint16_t i = *(uint16_t*) c; + uint32_t sign = (i&0x8000)>>15; + uint32_t man = (i&0x7C00)>>10; + uint32_t frac = (i&0x03ff); + uint32_t k = sign<<31; + + if (man == 0) + { + // subnormal numbers + if (frac != 0) + { + frac = frac | (frac >> 1); + frac = frac | (frac >> 2); + frac = frac | (frac >> 4); + frac = frac | (frac >> 8); + int zeros = std::bitset<32>(~frac).count(); + man = 127-zeros+7; + man <<= 23; + frac <<= zeros-8; + frac &= 0x7fffff; + k |= man | frac; + } + } + else if (man < 31) + { + // normal numbers + man = man-15+127; + man <<= 23; + frac <<= 13; + k |= man | frac; + } + else + { + // to infinity and beyond! + if (frac == 0) + k |= 0x7f800000; + else + k |= 0x7f800001 | ((frac&0x200)<<12); + } + return func(&k); + } +}; + template class Convert { @@ -385,6 +436,31 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'd': return &Convert::toD; } break; + case 'e': + if (reverse) switch (to[0]) + { + case 'z': return &Reverse::toZ>::convert>::call4; + case 'b': return &Reverse::toB>::convert>::call4; + case 'c': return &Reverse::toC>::convert>::call4; + case 's': return &Reverse::toS>::convert>::call4; + case 'i': return &Reverse::toI>::convert>::call4; + case 'j': return &Reverse::toJ>::convert>::call4; + case 'f': return &Reverse::toF>::convert>::call4; + case 'd': return &Reverse::toD>::convert>::call4; + } + else switch (to[0]) + { + case 'z': return &Half::toZ>::convert; + case 'b': return &Half::toB>::convert; + case 'c': return &Half::toC>::convert; + case 's': return &Half::toS>::convert; + case 'i': return &Half::toI>::convert; + case 'j': return &Half::toJ>::convert; + case 'f': return &Half::toF>::convert; + case 'd': return &Half::toD>::convert; + } + break; + case 'n': if (reverse) switch (to[0]) { diff --git a/test/jpypetest/test_conversionInt.py b/test/jpypetest/test_conversionInt.py index bb94ca025..8c9721c51 100644 --- a/test/jpypetest/test_conversionInt.py +++ b/test/jpypetest/test_conversionInt.py @@ -73,10 +73,10 @@ def testIntFromFloat(self): self.Test.callInt(float(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testIntFromNPFloat(self): + def testIntFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callInt(np.float_(2)) + self.Test.callInt(np.float16(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPFloat32(self): diff --git a/test/jpypetest/test_conversionLong.py b/test/jpypetest/test_conversionLong.py index ae3f31313..256b4ed0c 100644 --- a/test/jpypetest/test_conversionLong.py +++ b/test/jpypetest/test_conversionLong.py @@ -73,10 +73,10 @@ def testLongFromFloat(self): self.Test.callLong(float(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testLongFromNPFloat(self): + def testLongFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callLong(np.float_(2)) + self.Test.callLong(np.float16(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPFloat32(self): diff --git a/test/jpypetest/test_conversionShort.py b/test/jpypetest/test_conversionShort.py index d3fd81ffa..0c3d072c8 100644 --- a/test/jpypetest/test_conversionShort.py +++ b/test/jpypetest/test_conversionShort.py @@ -73,10 +73,10 @@ def testShortFromFloat(self): self.Test.callShort(float(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testShortFromNPFloat(self): + def testShortFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callShort(np.float_(2)) + self.Test.callShort(np.float16(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPFloat32(self): diff --git a/test/jpypetest/test_jboolean.py b/test/jpypetest/test_jboolean.py index ed0f6bd99..b27c63fd0 100644 --- a/test/jpypetest/test_jboolean.py +++ b/test/jpypetest/test_jboolean.py @@ -100,10 +100,10 @@ def testBooleanFromFloat(self): self.Test.callBoolean(float(2)) @common.requireNumpy - def testBooleanFromNPFloat(self): + def testBooleanFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callBoolean(np.float_(2)) + self.Test.callBoolean(np.float16(2)) @common.requireNumpy def testBooleanFromNPFloat32(self): diff --git a/test/jpypetest/test_jbyte.py b/test/jpypetest/test_jbyte.py index 55bfd4ec8..a4b995c9b 100644 --- a/test/jpypetest/test_jbyte.py +++ b/test/jpypetest/test_jbyte.py @@ -108,10 +108,10 @@ def testByteFromFloat(self): self.fixture.callByte(float(2)) @common.requireNumpy - def testByteFromNPFloat(self): + def testByteFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): - self.fixture.callByte(np.float_(2)) + self.fixture.callByte(np.float16(2)) @common.requireNumpy def testByteFromNPFloat32(self): diff --git a/test/jpypetest/test_jdouble.py b/test/jpypetest/test_jdouble.py index 09c03351b..cf3b89799 100644 --- a/test/jpypetest/test_jdouble.py +++ b/test/jpypetest/test_jdouble.py @@ -374,8 +374,8 @@ def testArraySetFromNPDouble(self): self.assertElementsAlmostEqual(a, jarr) @common.requireNumpy - def testArrayInitFromNPFloat(self): - a = np.random.random(100).astype(np.float_) + def testArrayInitFromNPFloat16(self): + a = np.random.random(100).astype(np.float16) jarr = JArray(JDouble)(a) self.assertElementsAlmostEqual(a, jarr) @@ -436,3 +436,15 @@ def __len__(self): def testCastBoolean(self): self.assertEqual(JDouble._canConvertToJava(JBoolean(True)), "none") + + @common.requireNumpy + def testNPFloat16(self): + v= [0.000000e+00, 5.960464e-08, 1.788139e-07, 1.788139e-07, 4.172325e-07, 8.940697e-07, 1.847744e-06, 3.755093e-06, 7.569790e-06, 1.519918e-05, 3.045797e-05, 6.097555e-05, 6.103516e-05, 3.332520e-01, 1.000000e+00, 6.550400e+04, np.inf, -np.inf] + a = np.array(v, dtype=np.float16) + jarr = JArray(JDouble)(a) + for v1,v2 in zip(a, jarr): + self.assertEqual(v1,v2) + a = np.array([np.nan], dtype=np.float16) + jarr = JArray(JDouble)(a) + self.assertTrue(np.isnan(jarr[0])) + diff --git a/test/jpypetest/test_jfloat.py b/test/jpypetest/test_jfloat.py index 4fbce3591..eb63fe168 100644 --- a/test/jpypetest/test_jfloat.py +++ b/test/jpypetest/test_jfloat.py @@ -382,8 +382,8 @@ def testArraySetFromNPDouble(self): self.assertElementsAlmostEqual(a, jarr) @common.requireNumpy - def testArrayInitFromNPFloat(self): - a = np.random.random(100).astype(np.float_) + def testArrayInitFromNPFloat16(self): + a = np.random.random(100).astype(np.float16) jarr = JArray(JFloat)(a) self.assertElementsAlmostEqual(a, jarr) @@ -441,3 +441,15 @@ def __len__(self): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a + + @common.requireNumpy + def testNPFloat16(self): + v= [0.000000e+00, 5.960464e-08, 1.788139e-07, 1.788139e-07, 4.172325e-07, 8.940697e-07, 1.847744e-06, 3.755093e-06, 7.569790e-06, 1.519918e-05, 3.045797e-05, 6.097555e-05, 6.103516e-05, 3.332520e-01, 1.000000e+00, 6.550400e+04, np.inf, -np.inf] + a = np.array(v, dtype=np.float16) + jarr = JArray(JFloat)(a) + for v1,v2 in zip(a, jarr): + self.assertEqual(v1,v2) + a = np.array([np.nan], dtype=np.float16) + jarr = JArray(JFloat)(a) + self.assertTrue(np.isnan(jarr[0])) + From f6849c5412d506e10a89e49aad42d13cc70f350c Mon Sep 17 00:00:00 2001 From: Karl Nelson Date: Sun, 23 Jun 2024 09:21:59 -0700 Subject: [PATCH 2/2] Trying to test tests working --- test/jpypetest/test_buffer.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/test/jpypetest/test_buffer.py b/test/jpypetest/test_buffer.py index 0da865232..3882511df 100644 --- a/test/jpypetest/test_buffer.py +++ b/test/jpypetest/test_buffer.py @@ -315,11 +315,11 @@ def testMemoryByte(self): jtype = jpype.JByte[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) - for dtype in ("s", "p", "P", "e"): + for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) @@ -328,11 +328,12 @@ def testMemoryInt(self): jtype = jpype.JInt[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + for dtype in "c?bBhHiIlLqQfdnN": + print(dtype) jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) - for dtype in ("s", "p", "P", "e"): + for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) @@ -341,11 +342,11 @@ def testMemoryShort(self): jtype = jpype.JShort[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) - for dtype in ("s", "p", "P", "e"): + for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) @@ -354,11 +355,11 @@ def testMemoryLong(self): jtype = jpype.JLong[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) - for dtype in ("s", "p", "P", "e"): + for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) @@ -367,11 +368,11 @@ def testMemoryFloat(self): jtype = jpype.JFloat[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) - for dtype in ("s", "p", "P", "e"): + for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) @@ -380,11 +381,11 @@ def testMemoryDouble(self): jtype = jpype.JDouble[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) - for dtype in ("s", "p", "P", "e"): + for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) @@ -393,11 +394,11 @@ def testMemoryBoolean(self): jtype = jpype.JBoolean[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) - for dtype in ("s", "p", "P", "e"): + for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) @@ -406,7 +407,7 @@ def testMemoryChar(self): jtype = jpype.JChar[:] # Simple checks - for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "n", "N"): + for dtype in "c?bBhHiIlLqQnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype))