From 9c190a20364ac2d17dd8aa7387adadb3f8c802d6 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Fri, 31 Mar 2023 13:41:59 +0200 Subject: [PATCH 1/6] make cle_array[::1] throw no error --- pyclesperanto_prototype/_tier0/_array_operators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyclesperanto_prototype/_tier0/_array_operators.py b/pyclesperanto_prototype/_tier0/_array_operators.py index c45d9135..cb7d95b3 100644 --- a/pyclesperanto_prototype/_tier0/_array_operators.py +++ b/pyclesperanto_prototype/_tier0/_array_operators.py @@ -249,6 +249,8 @@ def __setitem__(self, index, value): def __getitem__(self, index): result = None + if isinstance(index, slice): + index = (index,) if isinstance(index, list): index = tuple(index) if isinstance(index, (tuple, np.ndarray)) and index[0] is not None and isinstance(index[0], (tuple, list, np.ndarray)): From 3f1948b32fb2d59ae128701b35fd5ed3b2fc49e9 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Fri, 31 Mar 2023 13:42:22 +0200 Subject: [PATCH 2/6] make cle_array[::-1] and friends work --- pyclesperanto_prototype/_tier1/_range.py | 72 +++++++++++++++++++----- 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/pyclesperanto_prototype/_tier1/_range.py b/pyclesperanto_prototype/_tier1/_range.py index 05a0eb81..b8767d6c 100644 --- a/pyclesperanto_prototype/_tier1/_range.py +++ b/pyclesperanto_prototype/_tier1/_range.py @@ -35,37 +35,78 @@ def range(source : Image, ------- destination """ - - if start_x is None: - start_x = 0 - if stop_x is None: - stop_x = source.shape[-1] if step_x is None: step_x = 1 - if start_y is None: - start_y = 0 - if stop_y is None: - stop_y = source.shape[-2] + if start_x is None: + if step_x >= 0: + start_x = 0 + else: + start_x = source.shape[-1]-1 + if stop_x is None: + if step_x >= 0: + stop_x = source.shape[-1] + else: + stop_x = -1 + if step_y is None: step_y = 1 + if start_y is None: + if step_y >= 0: + start_y = 0 + else: + start_y = source.shape[-2]-1 + if stop_y is None: + if step_y >= 0: + stop_y = source.shape[-2] + else: + stop_y = -1 if len(source.shape) > 2: - if start_z is None: - start_z = 0 - if stop_z is None: - stop_z = source.shape[0] if step_z is None: step_z = 1 + if start_z is None: + if step_z >= 0: + start_z = 0 + else: + start_z = source.shape[-3] - 1 + if stop_z is None: + if step_z >= 0: + stop_z = source.shape[-3] + else: + stop_z = -1 + else: start_z = 0 stop_z = 1 step_z = 1 + # check if ranges make sense + if start_x < 0: + start_x = source.shape[-1] - start_x + if start_y < 0: + start_y = source.shape[-1] - start_x + if start_x > source.shape[-1] - 1: + start_x = source.shape[-1] - 1 + if start_y > source.shape[-2] - 1: + start_y = source.shape[-2] - 1 + if (start_x > stop_x and step_x > 0) or (start_x < stop_x and step_x < 0): + stop_x = start_x + if (start_y > stop_y and step_y > 0) or (start_y < stop_y and step_y < 0): + stop_y = start_y + + if len(source.shape) > 2: + if start_z < 0: + start_z = source.shape[-2] - start_z + if start_z > source.shape[-3] - 1: + start_z = source.shape[-3] - 1 + if (start_z > stop_z and step_z > 0) or (start_z < stop_z and step_z < 0): + stop_z = start_z + if destination is None: if len(source.shape) > 2: - destination = create((stop_z - start_z, stop_y - start_y, stop_x - start_x), source.dtype) + destination = create((abs(stop_z - start_z), abs(stop_y - start_y), abs(stop_x - start_x)), source.dtype) else: - destination = create((stop_y - start_y, stop_x - start_x), source.dtype) + destination = create((abs(stop_y - start_y), abs(stop_x - start_x)), source.dtype) parameters = { "dst":destination, @@ -79,4 +120,5 @@ def range(source : Image, } execute(__file__, 'range_x.cl', 'range', destination.shape, parameters) + return destination From 6eef973ec407029bd31d47c63c0a4465c1d2f833 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Fri, 31 Mar 2023 13:42:40 +0200 Subject: [PATCH 3/6] accept two empty arrays as equal --- pyclesperanto_prototype/_tier5/_array_equal.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyclesperanto_prototype/_tier5/_array_equal.py b/pyclesperanto_prototype/_tier5/_array_equal.py index ef5a96aa..ea8cdcf1 100644 --- a/pyclesperanto_prototype/_tier5/_array_equal.py +++ b/pyclesperanto_prototype/_tier5/_array_equal.py @@ -29,5 +29,8 @@ def array_equal(source1: Image, source2: Image) -> bool: from .._tier4 import mean_squared_error if not np.array_equal(source1.shape, source2.shape): return False + if np.prod(source1.shape) == 0 and np.prod(source2.shape) == 0: + # both empty arrays + return True return mean_squared_error(source1, source2) == 0 From df660bb283a4c3ca1d6c776e51c22360dbbd434e Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Fri, 31 Mar 2023 15:09:57 +0200 Subject: [PATCH 4/6] make exceeding ranges wor --- pyclesperanto_prototype/_tier1/_range.py | 104 ++++++++++------------- 1 file changed, 43 insertions(+), 61 deletions(-) diff --git a/pyclesperanto_prototype/_tier1/_range.py b/pyclesperanto_prototype/_tier1/_range.py index b8767d6c..953d02eb 100644 --- a/pyclesperanto_prototype/_tier1/_range.py +++ b/pyclesperanto_prototype/_tier1/_range.py @@ -35,73 +35,15 @@ def range(source : Image, ------- destination """ - if step_x is None: - step_x = 1 - if start_x is None: - if step_x >= 0: - start_x = 0 - else: - start_x = source.shape[-1]-1 - if stop_x is None: - if step_x >= 0: - stop_x = source.shape[-1] - else: - stop_x = -1 - - if step_y is None: - step_y = 1 - if start_y is None: - if step_y >= 0: - start_y = 0 - else: - start_y = source.shape[-2]-1 - if stop_y is None: - if step_y >= 0: - stop_y = source.shape[-2] - else: - stop_y = -1 - + start_x, stop_x, step_x = correct_range(start_x, stop_x, step_x, source.shape[-1]) + start_y, stop_y, step_y = correct_range(start_y, stop_y, step_y, source.shape[-2]) if len(source.shape) > 2: - if step_z is None: - step_z = 1 - if start_z is None: - if step_z >= 0: - start_z = 0 - else: - start_z = source.shape[-3] - 1 - if stop_z is None: - if step_z >= 0: - stop_z = source.shape[-3] - else: - stop_z = -1 - + start_z, stop_z, step_z = correct_range(start_z, stop_z, step_z, source.shape[-3]) else: start_z = 0 stop_z = 1 step_z = 1 - # check if ranges make sense - if start_x < 0: - start_x = source.shape[-1] - start_x - if start_y < 0: - start_y = source.shape[-1] - start_x - if start_x > source.shape[-1] - 1: - start_x = source.shape[-1] - 1 - if start_y > source.shape[-2] - 1: - start_y = source.shape[-2] - 1 - if (start_x > stop_x and step_x > 0) or (start_x < stop_x and step_x < 0): - stop_x = start_x - if (start_y > stop_y and step_y > 0) or (start_y < stop_y and step_y < 0): - stop_y = start_y - - if len(source.shape) > 2: - if start_z < 0: - start_z = source.shape[-2] - start_z - if start_z > source.shape[-3] - 1: - start_z = source.shape[-3] - 1 - if (start_z > stop_z and step_z > 0) or (start_z < stop_z and step_z < 0): - stop_z = start_z - if destination is None: if len(source.shape) > 2: destination = create((abs(stop_z - start_z), abs(stop_y - start_y), abs(stop_x - start_x)), source.dtype) @@ -122,3 +64,43 @@ def range(source : Image, execute(__file__, 'range_x.cl', 'range', destination.shape, parameters) return destination + + +def correct_range(start, stop, step, size): + # set in case not set (passed None) + if step is None: + step = 1 + if start is None: + if step >= 0: + start = 0 + else: + start = size - 1 + + if stop is None: + if step >= 0: + stop = size + else: + stop = -1 + + # Check if ranges make sense + if start >= size: + if step >= 0: + start = size + else: + start = size - 1 + if start < -size + 1: + start = -size + 1 + if stop > size: + stop = size + if stop < -size: + if start > 0: + stop = 0 - 1 + else: + stop = -size + + if start < 0: + start = size - start + if (start > stop and step > 0) or (start < stop and step < 0): + stop = start + + return start, stop, step From 41b0e762d7f07061a3e3c6f94e84bc93024863c0 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Fri, 31 Mar 2023 15:11:05 +0200 Subject: [PATCH 5/6] add tests --- tests/test_range.py | 91 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tests/test_range.py b/tests/test_range.py index 99f3cdbd..af698d8f 100644 --- a/tests/test_range.py +++ b/tests/test_range.py @@ -400,3 +400,94 @@ def test_types_2(): print(result) assert np.allclose(reference, result, 0.0001) + +def test_negative_step_2d(): + import pyclesperanto_prototype as cle + import numpy as np + + numbers = np.reshape(np.asarray([[i] for i in range(0, 20)]), (2, 10)) + + cle_numbers = cle.asarray(numbers) + + assert cle.array_equal( numbers[::-1], + cle_numbers[::-1]) + assert cle.array_equal( numbers[1::-1], + cle_numbers[1::-1]) + assert cle.array_equal( numbers[:5:-1], + cle_numbers[:5:-1]) + assert cle.array_equal( numbers[5:1:-1], + cle_numbers[5:1:-1]) + assert cle.array_equal( numbers[100:], + cle_numbers[100:]) + assert cle.array_equal( numbers[100::-1], + cle_numbers[100::-1]) + assert cle.array_equal( numbers[100:-50:-1], + cle_numbers[100:-50:-1]) + + assert cle.array_equal( numbers[::,::-1], + cle_numbers[::,::-1]) + assert cle.array_equal( numbers[::,1::-1], + cle_numbers[::,1::-1]) + assert cle.array_equal( numbers[::,:5:-1], + cle_numbers[::,:5:-1]) + assert cle.array_equal( numbers[::,5:1:-1], + cle_numbers[::,5:1:-1]) + assert cle.array_equal( numbers[::,100:], + cle_numbers[::,100:]) + assert cle.array_equal( numbers[::,100::-1], + cle_numbers[::,100::-1]) + assert cle.array_equal( numbers[::,100:-50:-1], + cle_numbers[::,100:-50:-1]) + +def test_negative_step_3d(): + import pyclesperanto_prototype as cle + import numpy as np + + numbers = np.reshape(np.asarray([[i] for i in range(0,60)]),(3,4,5)) + + cle_numbers = cle.asarray(numbers) + + assert cle.array_equal( numbers[::-1], + cle_numbers[::-1]) + assert cle.array_equal( numbers[1::-1], + cle_numbers[1::-1]) + assert cle.array_equal( numbers[:5:-1], + cle_numbers[:5:-1]) + assert cle.array_equal( numbers[5:1:-1], + cle_numbers[5:1:-1]) + assert cle.array_equal( numbers[100:], + cle_numbers[100:]) + assert cle.array_equal( numbers[100::-1], + cle_numbers[100::-1]) + assert cle.array_equal( numbers[100:-50:-1], + cle_numbers[100:-50:-1]) + + assert cle.array_equal( numbers[::, ::-1], + cle_numbers[::, ::-1]) + assert cle.array_equal( numbers[::, 1::-1], + cle_numbers[::, 1::-1]) + assert cle.array_equal( numbers[::, :5:-1], + cle_numbers[::, :5:-1]) + assert cle.array_equal( numbers[::, 5:1:-1], + cle_numbers[::, 5:1:-1]) + assert cle.array_equal( numbers[::, 100:], + cle_numbers[::, 100:]) + assert cle.array_equal( numbers[::, 100::-1], + cle_numbers[::, 100::-1]) + assert cle.array_equal( numbers[::, 100:-50:-1], + cle_numbers[::, 100:-50:-1]) + + assert cle.array_equal( numbers[::, ::, ::-1], + cle_numbers[::, ::, ::-1]) + assert cle.array_equal( numbers[::, ::, 1::-1], + cle_numbers[::, ::, 1::-1]) + assert cle.array_equal( numbers[::, ::, :5:-1], + cle_numbers[::, ::, :5:-1]) + assert cle.array_equal( numbers[::, ::, 5:1:-1], + cle_numbers[::, ::, 5:1:-1]) + assert cle.array_equal( numbers[::, ::, 100:], + cle_numbers[::, ::, 100:]) + assert cle.array_equal( numbers[::, ::, 100::-1], + cle_numbers[::, ::, 100::-1]) + assert cle.array_equal( numbers[::, ::, 100:-50:-1], + cle_numbers[::, ::, 100:-50:-1]) From 486a000032624e37b11c31089128c2e1543f27d0 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Fri, 31 Mar 2023 15:12:49 +0200 Subject: [PATCH 6/6] bump version --- pyclesperanto_prototype/__init__.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyclesperanto_prototype/__init__.py b/pyclesperanto_prototype/__init__.py index 2a764db6..48e40e46 100644 --- a/pyclesperanto_prototype/__init__.py +++ b/pyclesperanto_prototype/__init__.py @@ -10,5 +10,5 @@ from ._tier10 import * from ._tier11 import * -__version__ = "0.23.6" +__version__ = "0.24.0" __common_alias__ = "cle" diff --git a/setup.cfg b/setup.cfg index 5255a0a2..a7ccbb84 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pyclesperanto_prototype -version = 0.23.6 +version = 0.24.0 author = Robert Haase author_email = robert.haase@tu-dresden.de url = https://github.com/clEsperanto/pyclesperanto_prototype