From 456153ed6f02bae07925663b39663a1f48c3f5a8 Mon Sep 17 00:00:00 2001 From: hmacdope Date: Thu, 24 Oct 2024 11:30:04 +1100 Subject: [PATCH 1/3] protect from seg and add more tests --- distopia/pydistopia.pyx | 12 ++++ distopia/tests/test_distopia.py | 106 ++++++++++++++++++++++++++------ 2 files changed, 99 insertions(+), 19 deletions(-) diff --git a/distopia/pydistopia.pyx b/distopia/pydistopia.pyx index 83741e2..ea7dae6 100644 --- a/distopia/pydistopia.pyx +++ b/distopia/pydistopia.pyx @@ -771,6 +771,9 @@ def calc_self_distance_array_no_box( dims[0] = final_size + # return early, will seg + if nvals0 == 0: + return np.array([]) if results is None: if floating is float: @@ -823,6 +826,11 @@ def calc_self_distance_array_ortho( dims[0] = final_size + + # return early, will seg + if nvals0 == 0: + return np.array([]) + if results is None: if floating is float: results = cnp.PyArray_EMPTY(1, dims, cnp.NPY_FLOAT32, 0) @@ -874,6 +882,10 @@ def calc_self_distance_array_triclinic( dims[0] = final_size + # return early, will seg + if nvals0 == 0: + return np.array([]) + if results is None: if floating is float: results = cnp.PyArray_EMPTY(1, dims, cnp.NPY_FLOAT32, 0) diff --git a/distopia/tests/test_distopia.py b/distopia/tests/test_distopia.py index 93aea8c..3525b27 100644 --- a/distopia/tests/test_distopia.py +++ b/distopia/tests/test_distopia.py @@ -193,6 +193,12 @@ def test_triclinic_bad_result_or_input_shape(self): class TestDistanceArray: + def result_shim(self, make_arr, X, Y, dtype): + if not make_arr: + return None + else: + return np.empty((X, Y), dtype=dtype) + def test_no_box_bad_result(self): c0 = np.zeros(6, dtype=np.float32).reshape(2, 3) @@ -216,21 +222,59 @@ def test_triclinic_bad_result(self): distopia.calc_distance_array_triclinic(c0, c1, box, results=np.empty((1,1), dtype=np.float32)) - def test_distance_array_arange(self): - c0 = np.ones(9, dtype=np.float32).reshape(3, 3) - c1 = np.ones(9, dtype=np.float32).reshape(3, 3) - results = distopia.calc_distance_array_no_box(c0, c1) - assert_almost_equal(results, np.zeros((3,3), dtype=np.float32)) + @pytest.mark.parametrize("X, Y", ((0, 0), (10, 20), (1000, 100), (200, 1000))) + @pytest.mark.parametrize("dtype", (np.float32, np.float64)) + @pytest.mark.parametrize("use_result_buffer", (True, False)) + def test_distance_array_const(self, X, Y, dtype, use_result_buffer): + result_buffer = self.result_shim(use_result_buffer, X, Y, dtype) + c0 = np.ones(3 * X, dtype=dtype).reshape(X, 3) * 2 + c1 = np.ones(3 * Y, dtype=dtype).reshape(Y, 3) * 3 + results = distopia.calc_distance_array_no_box(c0, c1, results=result_buffer) + # equilateral triangle, edge is 3**(1/2) + expected = np.ones((X, Y), dtype=dtype) * 3**(1/2) + assert_almost_equal(results, expected) + + + @pytest.mark.parametrize("X, Y", ((0, 0), (10, 20), (1000, 100), (200, 1000))) + @pytest.mark.parametrize("dtype", (np.float32, np.float64)) + @pytest.mark.parametrize("use_result_buffer", (True, False)) + def test_distance_array_const_ortho(self, X, Y, dtype, use_result_buffer): + result_buffer = self.result_shim(use_result_buffer, X, Y, dtype) + c0 = np.ones(3 * X, dtype=dtype).reshape(X, 3) * 2 + c1 = np.ones(3 * Y, dtype=dtype).reshape(Y, 3) * 3 + box = np.array([2.5, 2.5, 2.5], dtype=dtype) + results = distopia.calc_distance_array_ortho(c0, c1, box=box, results=result_buffer) + # equilateral triangle, edge is 3**(1/2) + expected = np.ones((X, Y), dtype=dtype) * 3**(1/2) + assert_almost_equal(results, expected) - def test_distance_array_results(self): - c0 = np.ones(9, dtype=np.float32).reshape(3, 3) - c1 = np.ones(9, dtype=np.float32).reshape(3, 3) - results = distopia.calc_distance_array_no_box(c0, c1, results=np.empty((3,3), dtype=np.float32)) - assert_almost_equal(results, np.zeros((3,3), dtype=np.float32)) + + + @pytest.mark.parametrize("X, Y", ((0, 0), (10, 20), (1000, 100), (200, 1000))) + @pytest.mark.parametrize("dtype", (np.float32, np.float64)) + @pytest.mark.parametrize("use_result_buffer", (True, False)) + def test_distance_const_tric(self, X, Y, dtype, use_result_buffer): + result_buffer = self.result_shim(use_result_buffer, X, Y, dtype) + c0 = np.ones(3 * X, dtype=dtype).reshape(X, 3) * 2 + c1 = np.ones(3 * Y, dtype=dtype).reshape(Y, 3) * 3 + box = np.array([[2.5, 0, 0], [0, 2.5, 0], [0, 0, 2.5]], dtype=dtype) + results = distopia.calc_distance_array_triclinic(c0, c1, box=box, results=result_buffer) + # equilateral triangle, edge is 3**(1/2) + expected = np.ones((X, Y), dtype=dtype) * 3**(1/2) + assert_almost_equal(results, expected) class TestSelfDistanceArray: + + def result_shim(self, make_arr, N, dtype): + if not make_arr: + return None + else: + size = N * (N - 1) // 2 # reduced triangular matrix + return np.empty(size, dtype=dtype) + + def test_no_box_bad_result(self): c0 = np.zeros(12, dtype=np.float32).reshape(4, 3) with pytest.raises(ValueError, match="results must be"): @@ -248,17 +292,41 @@ def test_triclinic_bad_result(self): with pytest.raises(ValueError, match="results must be"): distopia.calc_self_distance_array_triclinic(c0, box, results=np.empty(1, dtype=np.float32)) - def test_self_distance_ones(self): - c0 = np.ones(9, dtype=np.float32).reshape(3, 3) - results = distopia.calc_self_distance_array_no_box(c0) - assert_almost_equal(results, np.zeros(3, dtype=np.float32)) + + @pytest.mark.parametrize("N", (0, 10, 1000, 10000)) + @pytest.mark.parametrize("dtype", (np.float32, np.float64)) + @pytest.mark.parametrize("use_result_buffer", (True, False)) + def test_self_distance_const(self, N, dtype, use_result_buffer): + result_buffer = self.result_shim(use_result_buffer, N, dtype) + c0 = np.ones(3 * N, dtype=dtype).reshape(N, 3) + expected_size = N * (N - 1) // 2 + results = distopia.calc_self_distance_array_no_box(c0, results=result_buffer) + assert_almost_equal(results, np.zeros(expected_size, dtype=dtype)) + + + + @pytest.mark.parametrize("N", (0, 10, 1000, 10000)) + @pytest.mark.parametrize("dtype", (np.float32, np.float64)) + @pytest.mark.parametrize("use_result_buffer", (True, False)) + def test_self_distance_const_ortho(self, N, dtype, use_result_buffer): + result_buffer = self.result_shim(use_result_buffer, N, dtype) + c0 = np.ones(3 * N, dtype=dtype).reshape(N, 3) + expected_size = N * (N - 1) // 2 + box = np.array([10, 10, 10], dtype=dtype) + results = distopia.calc_self_distance_array_ortho(c0, box=box, results=result_buffer) + assert_almost_equal(results, np.zeros(expected_size, dtype=dtype)) - def test_self_distance_ones_result(self): - c0 = np.ones(9, dtype=np.float32).reshape(3, 3) - # n(n-1) //2 - results = distopia.calc_self_distance_array_no_box(c0, results=np.empty(3, dtype=np.float32)) - assert_almost_equal(results, np.zeros(3, dtype=np.float32)) + @pytest.mark.parametrize("N", (0, 10, 1000, 10000)) + @pytest.mark.parametrize("dtype", (np.float32, np.float64)) + @pytest.mark.parametrize("use_result_buffer", (True, False)) + def test_self_distance_const_tric(self, N, dtype, use_result_buffer): + result_buffer = self.result_shim(use_result_buffer, N, dtype) + c0 = np.ones(3 * N, dtype=dtype).reshape(N, 3) + expected_size = N * (N - 1) // 2 + box = np.array([[10, 0, 0], [0, 10, 0], [0, 0, 10]], dtype=dtype) + results = distopia.calc_self_distance_array_triclinic(c0, box=box, results=result_buffer) + assert_almost_equal(results, np.zeros(expected_size, dtype=dtype)) class TestMDA: """ From 50ce92fb36b6b3b6608f8feb969a76471aac65a5 Mon Sep 17 00:00:00 2001 From: hmacdope Date: Thu, 24 Oct 2024 14:19:40 +1100 Subject: [PATCH 2/3] fixify --- distopia/pydistopia.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distopia/pydistopia.pyx b/distopia/pydistopia.pyx index ea7dae6..074070b 100644 --- a/distopia/pydistopia.pyx +++ b/distopia/pydistopia.pyx @@ -205,7 +205,7 @@ def calc_bonds_no_box(floating[:, ::1] coords0, CalcBondsNoBox(& coords0[0][0], & coords1[0][0], nvals, & results_view[0]) - return np.array(results) + return results @cython.boundscheck(False) @@ -252,7 +252,7 @@ def calc_bonds_ortho(floating[:, ::1] coords0, CalcBondsOrtho(& coords0[0][0], & coords1[0][0], nvals, & box[0], & results_view[0]) - return np.array(results) + return results @cython.boundscheck(False) @@ -298,7 +298,7 @@ def calc_bonds_triclinic(floating[:, ::1] coords0, CalcBondsTriclinic(& coords0[0][0], & coords1[0][0], nvals, & box[0][0], & results_view[0]) - return np.array(results) + return results @cython.boundscheck(False) @cython.wraparound(False) From 1cadcb31e25d6f1051c20cae53756283bde32168 Mon Sep 17 00:00:00 2001 From: hmacdope Date: Thu, 24 Oct 2024 14:56:34 +1100 Subject: [PATCH 3/3] improve tests --- distopia/pydistopia.pyx | 6 +++--- distopia/tests/test_distopia.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/distopia/pydistopia.pyx b/distopia/pydistopia.pyx index 074070b..ea7dae6 100644 --- a/distopia/pydistopia.pyx +++ b/distopia/pydistopia.pyx @@ -205,7 +205,7 @@ def calc_bonds_no_box(floating[:, ::1] coords0, CalcBondsNoBox(& coords0[0][0], & coords1[0][0], nvals, & results_view[0]) - return results + return np.array(results) @cython.boundscheck(False) @@ -252,7 +252,7 @@ def calc_bonds_ortho(floating[:, ::1] coords0, CalcBondsOrtho(& coords0[0][0], & coords1[0][0], nvals, & box[0], & results_view[0]) - return results + return np.array(results) @cython.boundscheck(False) @@ -298,7 +298,7 @@ def calc_bonds_triclinic(floating[:, ::1] coords0, CalcBondsTriclinic(& coords0[0][0], & coords1[0][0], nvals, & box[0][0], & results_view[0]) - return results + return np.array(results) @cython.boundscheck(False) @cython.wraparound(False) diff --git a/distopia/tests/test_distopia.py b/distopia/tests/test_distopia.py index 3525b27..04f54e8 100644 --- a/distopia/tests/test_distopia.py +++ b/distopia/tests/test_distopia.py @@ -65,8 +65,17 @@ def test_calc_bonds_triclinic_all_zero(self, N, use_result_buffer, dtype): box = np.asarray([[30, 0, 0], [-2.6146722, 29.885841, 0], [-10.260604, 9.402112, 26.576687]], dtype=dtype) result = distopia.calc_bonds_triclinic(c0, c1, box, results=result_buffer) assert_allclose(result, np.zeros(N)) - # check dtype of result - assert result.dtype == dtype + + def test_calc_bonds_inplace_results(self): + N = 100 + dtype = np.float32 + c0 = self.arange_input(N, dtype) + c1 = self.arange_input(N, dtype) + 1 + result_buffer = np.empty(N, dtype=dtype) + result = distopia.calc_bonds_no_box(c0, c1, results=result_buffer) + assert_allclose(result, result_buffer) + assert_allclose(result, np.linalg.norm(c0 - c1, axis=1)) +