diff --git a/benchmarks/test_java_to_python_conversions.py b/benchmarks/test_java_to_python_conversions.py new file mode 100644 index 0000000000000000000000000000000000000000..6fd35c9b5d65faff7afb47d1fc580e123c5a831b --- /dev/null +++ b/benchmarks/test_java_to_python_conversions.py @@ -0,0 +1,26 @@ +import jpype as jp +import numpy as np +import pytest + +import pyjapc + + +@pytest.mark.parametrize("shape, dtype", [ + ((10000, 10000), np.int32), + ((10000, 10000), np.float32), + ((10000, 10000), np.float64), + ((1, 1), np.int32), + ((1, 1), np.float32), + ((1, 1), np.float64), + ((1_000_000, ), np.int32), + ((1_000_000, ), np.float32), + ((1_000_000, ), np.float64), +]) +def test_big_array(benchmark, shape, dtype): + japc = pyjapc.PyJapc() + data = np.linspace(0, 1000, np.prod(shape)).astype(dtype) + + DoubleArrayValue = jp.JClass("cern.japc.value.spi.value.simple.FloatArrayValue") + v = DoubleArrayValue(jp.JArray(jp.JFloat)(data)) + + benchmark(japc._convertSimpleValToPy, v) diff --git a/pyjapc/_japc.py b/pyjapc/_japc.py index ef44f68204c27831ce6195df4a8e4e10f0f6991d..bcc0e2451a1031387fc40a56784cf957561b8c00 100644 --- a/pyjapc/_japc.py +++ b/pyjapc/_japc.py @@ -1318,36 +1318,6 @@ class PyJapc(object): parValNew = self._simpleParameterValueFactory.newValue(jVal) return parValNew - def _convert2DJarrayToNumpy(self, jArr): - """Faster conversion of 2D JArrays to numpy arrays. - - If jArr is 2D then jArr[:] will be a list of 1D JArrays. 1D JArrays can - be converted fast to numpy with 1djArr[:]. - - Due to a `bug <https://github.com/originell/jpype/issues/133>`_ this - will return a list on Windows and a numpy array on Linux. - """ - if len(jArr) > 0 and len(jArr[0]) > 0: - arrType = np.array(jArr[0][0]).dtype - else: - # Workaround for empty arrays. - return np.array(jArr) - - if arrType.kind in {'U', 'S'}: - # Workaround for str arrays which cannot benefit - # from this optimisation due to us needing to know the length - # of the longest string upfront in order to define the dtype - # (since numpy strings have a length). - return np.array(jArr) - - arrShape = (len(jArr), len(jArr[0])) - resultArray = np.empty(arrShape, dtype=arrType) - - for i, cols in enumerate(jArr[:]): - # This should work on Win (list) and Linux (numpy array) - resultArray[i, :] = cols[:] - return resultArray - def _convertSimpleValToPy(self, val): """Convert the Java JAPC SimpleParameterValue object to a Python type or numpy array we do getXXX() for primitives and array2D.getXXXArray2D() @@ -1405,7 +1375,6 @@ class PyJapc(object): for knownType in knownTypes: if tStr.startswith(knownType): # We found a type match! - # Check if it is not an array (tStr does not contain any []) # Then we can use the simple .getInt() function if tStr.find("[]") == -1: @@ -1417,13 +1386,7 @@ class PyJapc(object): jArr = getattr(val.getArray2D(), getFunctionName)() - # Safest but _EEXTREMELY_ slow - # npArr = np.atleast_1d(np.array(jArr).squeeze()) - - # Fastest but n-D array not supported by JPype yet - # npArr = np.atleast_1d(jArr[:].squeeze()) - - npArr = np.atleast_1d(self._convert2DJarrayToNumpy(jArr).squeeze()) + npArr = np.atleast_1d(np.array(jArr).squeeze()) # If the array only contains one value, return the naked value if npArr.size == 1: diff --git a/pyjapc/tests/test_pyjapc.py b/pyjapc/tests/test_pyjapc.py index 4cceb34b70867d066341c100d3acdcda6b47aee0..736890d5aa4a639b41108815f2ed8223cc4813b5 100644 --- a/pyjapc/tests/test_pyjapc.py +++ b/pyjapc/tests/test_pyjapc.py @@ -55,16 +55,27 @@ def test_convert_py_to_val_int(japc, simple_descriptor): assert back == val -def test_convert_py_to_val_float(japc, simple_descriptor): - japc_type = jp.JClass("cern.japc.value.spi.value.simple.DoubleValue") +def round_trip_double(japc, value): + simple_descriptor = jp.JClass("cern.japc.core.spi.value.SimpleDescriptorImpl") value_descriptor = simple_descriptor(jp.JClass("cern.japc.value.ValueType").DOUBLE) + java_val = japc._convertPyToVal(value, value_descriptor) + round_tripped_value = japc._convertValToPy(java_val) + return java_val, round_tripped_value - val = 3.14 - r = japc._convertPyToVal(val, value_descriptor) - assert r == japc_type(val) - back = japc._convertValToPy(r) - assert back == val +@pytest.mark.parametrize(['value'], [[3.14], [-np.inf], [np.inf]]) +def test_convert_py_to_val_float(japc, value): + japc_type = jp.JClass("cern.japc.value.spi.value.simple.DoubleValue") + java_val, py_val = round_trip_double(japc, value) + assert java_val == japc_type(value) + assert py_val == value + + +def test_convert_py_to_val_nan(japc): + japc_type = jp.JClass("cern.japc.value.spi.value.simple.DoubleValue") + java_val, py_val = round_trip_double(japc, np.nan) + assert java_val == japc_type(np.nan) + assert np.isnan(py_val) @pytest.mark.parametrize(['jprimitive', 'expectation'], [ @@ -310,13 +321,23 @@ def test_array_conversions(japc, np_array, arr_val_type): def test_double_array_to_py_conversion(japc): - arr = np.array([1.2, 2.4, 4.8], dtype=np.double) + arr = np.array([1.2, 2.4, 4.8], dtype=np.float64) DoubleArrayValue = jp.JClass("cern.japc.value.spi.value.simple.DoubleArrayValue") v = DoubleArrayValue(jp.JArray(jp.JDouble)(arr)) r = japc._convertSimpleValToPy(v) assert isinstance(r, np.ndarray) np.testing.assert_array_equal(r, arr) - assert r.dtype == np.double + assert r.dtype == np.float64 + + +def test_float_array_to_py_conversion(japc): + arr = np.array([1.2, 2.4, 4.8], dtype=np.float32) + FloatArrayValue = jp.JClass("cern.japc.value.spi.value.simple.FloatArrayValue") + v = FloatArrayValue(jp.JArray(jp.JFloat)(arr)) + r = japc._convertSimpleValToPy(v) + assert isinstance(r, np.ndarray) + np.testing.assert_array_equal(r, arr) + assert r.dtype == np.float32 def test_py_to_simple_discrete_function(japc, simple_descriptor):