Skip to content

Commit 469102e

Browse files
authored
Improve dist array tests and protect against segfault. (#179)
* protect from seg and add more tests * fixify * improve tests
1 parent 3b630a0 commit 469102e

File tree

2 files changed

+110
-21
lines changed

2 files changed

+110
-21
lines changed

distopia/pydistopia.pyx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,9 @@ def calc_self_distance_array_no_box(
771771

772772
dims[0] = <ssize_t > final_size
773773

774+
# return early, will seg
775+
if nvals0 == 0:
776+
return np.array([])
774777

775778
if results is None:
776779
if floating is float:
@@ -823,6 +826,11 @@ def calc_self_distance_array_ortho(
823826

824827
dims[0] = <ssize_t > final_size
825828

829+
830+
# return early, will seg
831+
if nvals0 == 0:
832+
return np.array([])
833+
826834
if results is None:
827835
if floating is float:
828836
results = cnp.PyArray_EMPTY(1, dims, cnp.NPY_FLOAT32, 0)
@@ -874,6 +882,10 @@ def calc_self_distance_array_triclinic(
874882
dims[0] = <ssize_t > final_size
875883

876884

885+
# return early, will seg
886+
if nvals0 == 0:
887+
return np.array([])
888+
877889
if results is None:
878890
if floating is float:
879891
results = cnp.PyArray_EMPTY(1, dims, cnp.NPY_FLOAT32, 0)

distopia/tests/test_distopia.py

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,17 @@ def test_calc_bonds_triclinic_all_zero(self, N, use_result_buffer, dtype):
6565
box = np.asarray([[30, 0, 0], [-2.6146722, 29.885841, 0], [-10.260604, 9.402112, 26.576687]], dtype=dtype)
6666
result = distopia.calc_bonds_triclinic(c0, c1, box, results=result_buffer)
6767
assert_allclose(result, np.zeros(N))
68-
# check dtype of result
69-
assert result.dtype == dtype
68+
69+
def test_calc_bonds_inplace_results(self):
70+
N = 100
71+
dtype = np.float32
72+
c0 = self.arange_input(N, dtype)
73+
c1 = self.arange_input(N, dtype) + 1
74+
result_buffer = np.empty(N, dtype=dtype)
75+
result = distopia.calc_bonds_no_box(c0, c1, results=result_buffer)
76+
assert_allclose(result, result_buffer)
77+
assert_allclose(result, np.linalg.norm(c0 - c1, axis=1))
78+
7079

7180

7281

@@ -193,6 +202,12 @@ def test_triclinic_bad_result_or_input_shape(self):
193202

194203
class TestDistanceArray:
195204

205+
def result_shim(self, make_arr, X, Y, dtype):
206+
if not make_arr:
207+
return None
208+
else:
209+
return np.empty((X, Y), dtype=dtype)
210+
196211

197212
def test_no_box_bad_result(self):
198213
c0 = np.zeros(6, dtype=np.float32).reshape(2, 3)
@@ -216,21 +231,59 @@ def test_triclinic_bad_result(self):
216231
distopia.calc_distance_array_triclinic(c0, c1, box, results=np.empty((1,1), dtype=np.float32))
217232

218233

219-
def test_distance_array_arange(self):
220-
c0 = np.ones(9, dtype=np.float32).reshape(3, 3)
221-
c1 = np.ones(9, dtype=np.float32).reshape(3, 3)
222-
results = distopia.calc_distance_array_no_box(c0, c1)
223-
assert_almost_equal(results, np.zeros((3,3), dtype=np.float32))
234+
@pytest.mark.parametrize("X, Y", ((0, 0), (10, 20), (1000, 100), (200, 1000)))
235+
@pytest.mark.parametrize("dtype", (np.float32, np.float64))
236+
@pytest.mark.parametrize("use_result_buffer", (True, False))
237+
def test_distance_array_const(self, X, Y, dtype, use_result_buffer):
238+
result_buffer = self.result_shim(use_result_buffer, X, Y, dtype)
239+
c0 = np.ones(3 * X, dtype=dtype).reshape(X, 3) * 2
240+
c1 = np.ones(3 * Y, dtype=dtype).reshape(Y, 3) * 3
241+
results = distopia.calc_distance_array_no_box(c0, c1, results=result_buffer)
242+
# equilateral triangle, edge is 3**(1/2)
243+
expected = np.ones((X, Y), dtype=dtype) * 3**(1/2)
244+
assert_almost_equal(results, expected)
224245

225-
def test_distance_array_results(self):
226-
c0 = np.ones(9, dtype=np.float32).reshape(3, 3)
227-
c1 = np.ones(9, dtype=np.float32).reshape(3, 3)
228-
results = distopia.calc_distance_array_no_box(c0, c1, results=np.empty((3,3), dtype=np.float32))
229-
assert_almost_equal(results, np.zeros((3,3), dtype=np.float32))
246+
247+
@pytest.mark.parametrize("X, Y", ((0, 0), (10, 20), (1000, 100), (200, 1000)))
248+
@pytest.mark.parametrize("dtype", (np.float32, np.float64))
249+
@pytest.mark.parametrize("use_result_buffer", (True, False))
250+
def test_distance_array_const_ortho(self, X, Y, dtype, use_result_buffer):
251+
result_buffer = self.result_shim(use_result_buffer, X, Y, dtype)
252+
c0 = np.ones(3 * X, dtype=dtype).reshape(X, 3) * 2
253+
c1 = np.ones(3 * Y, dtype=dtype).reshape(Y, 3) * 3
254+
box = np.array([2.5, 2.5, 2.5], dtype=dtype)
255+
results = distopia.calc_distance_array_ortho(c0, c1, box=box, results=result_buffer)
256+
# equilateral triangle, edge is 3**(1/2)
257+
expected = np.ones((X, Y), dtype=dtype) * 3**(1/2)
258+
assert_almost_equal(results, expected)
259+
260+
261+
262+
@pytest.mark.parametrize("X, Y", ((0, 0), (10, 20), (1000, 100), (200, 1000)))
263+
@pytest.mark.parametrize("dtype", (np.float32, np.float64))
264+
@pytest.mark.parametrize("use_result_buffer", (True, False))
265+
def test_distance_const_tric(self, X, Y, dtype, use_result_buffer):
266+
result_buffer = self.result_shim(use_result_buffer, X, Y, dtype)
267+
c0 = np.ones(3 * X, dtype=dtype).reshape(X, 3) * 2
268+
c1 = np.ones(3 * Y, dtype=dtype).reshape(Y, 3) * 3
269+
box = np.array([[2.5, 0, 0], [0, 2.5, 0], [0, 0, 2.5]], dtype=dtype)
270+
results = distopia.calc_distance_array_triclinic(c0, c1, box=box, results=result_buffer)
271+
# equilateral triangle, edge is 3**(1/2)
272+
expected = np.ones((X, Y), dtype=dtype) * 3**(1/2)
273+
assert_almost_equal(results, expected)
230274

231275

232276
class TestSelfDistanceArray:
233277

278+
279+
def result_shim(self, make_arr, N, dtype):
280+
if not make_arr:
281+
return None
282+
else:
283+
size = N * (N - 1) // 2 # reduced triangular matrix
284+
return np.empty(size, dtype=dtype)
285+
286+
234287
def test_no_box_bad_result(self):
235288
c0 = np.zeros(12, dtype=np.float32).reshape(4, 3)
236289
with pytest.raises(ValueError, match="results must be"):
@@ -248,17 +301,41 @@ def test_triclinic_bad_result(self):
248301
with pytest.raises(ValueError, match="results must be"):
249302
distopia.calc_self_distance_array_triclinic(c0, box, results=np.empty(1, dtype=np.float32))
250303

251-
def test_self_distance_ones(self):
252-
c0 = np.ones(9, dtype=np.float32).reshape(3, 3)
253-
results = distopia.calc_self_distance_array_no_box(c0)
254-
assert_almost_equal(results, np.zeros(3, dtype=np.float32))
304+
305+
@pytest.mark.parametrize("N", (0, 10, 1000, 10000))
306+
@pytest.mark.parametrize("dtype", (np.float32, np.float64))
307+
@pytest.mark.parametrize("use_result_buffer", (True, False))
308+
def test_self_distance_const(self, N, dtype, use_result_buffer):
309+
result_buffer = self.result_shim(use_result_buffer, N, dtype)
310+
c0 = np.ones(3 * N, dtype=dtype).reshape(N, 3)
311+
expected_size = N * (N - 1) // 2
312+
results = distopia.calc_self_distance_array_no_box(c0, results=result_buffer)
313+
assert_almost_equal(results, np.zeros(expected_size, dtype=dtype))
314+
255315

256316

257-
def test_self_distance_ones_result(self):
258-
c0 = np.ones(9, dtype=np.float32).reshape(3, 3)
259-
# n(n-1) //2
260-
results = distopia.calc_self_distance_array_no_box(c0, results=np.empty(3, dtype=np.float32))
261-
assert_almost_equal(results, np.zeros(3, dtype=np.float32))
317+
@pytest.mark.parametrize("N", (0, 10, 1000, 10000))
318+
@pytest.mark.parametrize("dtype", (np.float32, np.float64))
319+
@pytest.mark.parametrize("use_result_buffer", (True, False))
320+
def test_self_distance_const_ortho(self, N, dtype, use_result_buffer):
321+
result_buffer = self.result_shim(use_result_buffer, N, dtype)
322+
c0 = np.ones(3 * N, dtype=dtype).reshape(N, 3)
323+
expected_size = N * (N - 1) // 2
324+
box = np.array([10, 10, 10], dtype=dtype)
325+
results = distopia.calc_self_distance_array_ortho(c0, box=box, results=result_buffer)
326+
assert_almost_equal(results, np.zeros(expected_size, dtype=dtype))
327+
328+
329+
@pytest.mark.parametrize("N", (0, 10, 1000, 10000))
330+
@pytest.mark.parametrize("dtype", (np.float32, np.float64))
331+
@pytest.mark.parametrize("use_result_buffer", (True, False))
332+
def test_self_distance_const_tric(self, N, dtype, use_result_buffer):
333+
result_buffer = self.result_shim(use_result_buffer, N, dtype)
334+
c0 = np.ones(3 * N, dtype=dtype).reshape(N, 3)
335+
expected_size = N * (N - 1) // 2
336+
box = np.array([[10, 0, 0], [0, 10, 0], [0, 0, 10]], dtype=dtype)
337+
results = distopia.calc_self_distance_array_triclinic(c0, box=box, results=result_buffer)
338+
assert_almost_equal(results, np.zeros(expected_size, dtype=dtype))
262339

263340
class TestMDA:
264341
"""

0 commit comments

Comments
 (0)