def test_incremental(self, name): # Test incremental construction of the triangulation chunks, opts = INCREMENTAL_DATASETS[name] points = np.concatenate(chunks, axis=0) obj = qhull.Delaunay(chunks[0], incremental=True, qhull_options=opts) for chunk in chunks[1:]: obj.add_points(chunk) obj2 = qhull.Delaunay(points) obj3 = qhull.Delaunay(chunks[0], incremental=True, qhull_options=opts) if len(chunks) > 1: obj3.add_points(np.concatenate(chunks[1:], axis=0), restart=True) # Check that the incremental mode agrees with upfront mode if name.startswith('pathological'): # XXX: These produce valid but different triangulations. # They look OK when plotted, but how to check them? assert_array_equal(np.unique(obj.simplices.ravel()), np.arange(points.shape[0])) assert_array_equal(np.unique(obj2.simplices.ravel()), np.arange(points.shape[0])) else: assert_unordered_tuple_list_equal(obj.simplices, obj2.simplices, tpl=sorted_tuple) assert_unordered_tuple_list_equal(obj2.simplices, obj3.simplices, tpl=sorted_tuple)
def test_more_barycentric_transforms(self): # Triangulate some "nasty" grids eps = np.finfo(float).eps npoints = {2: 70, 3: 11, 4: 5, 5: 3} for ndim in range(2, 6): # Generate an uniform grid in n-d unit cube x = np.linspace(0, 1, npoints[ndim]) grid = np.c_[list(map(np.ravel, np.broadcast_arrays(*np.ix_(*([x]*ndim)))))].T err_msg = "ndim=%d" % ndim # Check using regular grid tri = qhull.Delaunay(grid) self._check_barycentric_transforms(tri, err_msg=err_msg, unit_cube=True) # Check with eps-perturbations np.random.seed(1234) m = (np.random.rand(grid.shape[0]) < 0.2) grid[m,:] += 2*eps*(np.random.rand(*grid[m,:].shape) - 0.5) tri = qhull.Delaunay(grid) self._check_barycentric_transforms(tri, err_msg=err_msg, unit_cube=True, unit_cube_tol=2*eps) # Check with duplicated data tri = qhull.Delaunay(np.r_[grid, grid]) self._check_barycentric_transforms(tri, err_msg=err_msg, unit_cube=True, unit_cube_tol=2*eps)
def test_duplicate_points(self): x = np.array([0, 1, 0, 1], dtype=np.float64) y = np.array([0, 0, 1, 1], dtype=np.float64) xp = np.r_[x, x] yp = np.r_[y, y] # shouldn't fail on duplicate points qhull.Delaunay(np.c_[x, y]) qhull.Delaunay(np.c_[xp, yp])
def test_pathological(self): # both should succeed points = DATASETS['pathological-1'] tri = qhull.Delaunay(points) assert_equal(tri.points[tri.simplices].max(), points.max()) assert_equal(tri.points[tri.simplices].min(), points.min()) points = DATASETS['pathological-2'] tri = qhull.Delaunay(points) assert_equal(tri.points[tri.simplices].max(), points.max()) assert_equal(tri.points[tri.simplices].min(), points.min())
def test_2d_square(self): # simple smoke test: 2d square points = np.array([(0, 0), (0, 1), (1, 1), (1, 0)], dtype=np.double) tri = qhull.Delaunay(points) assert_equal(tri.simplices, [[1, 3, 2], [3, 1, 0]]) assert_equal(tri.neighbors, [[-1, -1, 1], [-1, -1, 0]])
def test_plane_distance(self): # Compare plane distance from hyperplane equations obtained from Qhull # to manually computed plane equations x = np.array([(0, 0), (1, 1), (1, 0), (0.99189033, 0.37674127), (0.99440079, 0.45182168)], dtype=np.double) p = np.array([0.99966555, 0.15685619], dtype=np.double) tri = qhull.Delaunay(x) z = tri.lift_points(x) pz = tri.lift_points(p) dist = tri.plane_distance(p) for j, v in enumerate(tri.simplices): x1 = z[v[0]] x2 = z[v[1]] x3 = z[v[2]] n = np.cross(x1 - x3, x2 - x3) n /= np.sqrt(np.dot(n, n)) n *= -np.sign(n[2]) d = np.dot(n, pz - x3) assert_almost_equal(dist[j], d)
def test_joggle(self): # Check that the option QJ indeed guarantees that all input points # occur as vertices of the triangulation points = np.random.rand(10, 2) points = np.r_[points, points] # duplicate input data tri = qhull.Delaunay(points, qhull_options="QJ Qbb Pp") assert_array_equal(np.unique(tri.simplices.ravel()), np.arange(len(points)))
def test_tri_input(self): # Test at single points x = np.array([(0, 0), (-0.5, -0.5), (-0.5, 0.5), (0.5, 0.5), (0.25, 0.3)], dtype=np.double) y = np.arange(x.shape[0], dtype=np.double) y = y - 3j * y tri = qhull.Delaunay(x) yi = interpnd.LinearNDInterpolator(tri, y)(x) assert_almost_equal(y, yi)
def test_convex_hull(self): # Simple check that the convex hull seems to works points = np.array([(0, 0), (0, 1), (1, 1), (1, 0)], dtype=np.double) tri = qhull.Delaunay(points) # +---+ # |\ 0| # | \ | # |1 \| # +---+ assert_equal(tri.convex_hull, [[3, 2], [1, 2], [1, 0], [3, 0]])
def test_tri_input_rescale(self): # Test at single points x = np.array([(0, 0), (-5, -5), (-5, 5), (5, 5), (2.5, 3)], dtype=np.double) y = np.arange(x.shape[0], dtype=np.double) y = y - 3j * y tri = qhull.Delaunay(x) match = ("Rescaling is not supported when passing a " "Delaunay triangulation as ``points``.") with pytest.raises(ValueError, match=match): interpnd.CloughTocher2DInterpolator(tri, y, rescale=True)(x)
def test_regression_2359(self): # Check regression --- for certain point sets, gradient # estimation could end up in an infinite loop points = np.load(data_file('estimate_gradients_hang.npy')) values = np.random.rand(points.shape[0]) tri = qhull.Delaunay(points) # This should not hang with suppress_warnings() as sup: sup.filter(interpnd.GradientEstimationWarning, "Gradient estimation did not converge") interpnd.estimate_gradients_2d_global(tri, values, maxiter=1)
def test_tripoints_input_rescale(self): # Test at single points x = np.array([(0, 0), (-5, -5), (-5, 5), (5, 5), (2.5, 3)], dtype=np.double) y = np.arange(x.shape[0], dtype=np.double) y = y - 3j * y tri = qhull.Delaunay(x) yi = interpnd.LinearNDInterpolator(tri.points, y)(x) yi_rescale = interpnd.LinearNDInterpolator(tri.points, y, rescale=True)(x) assert_almost_equal(yi, yi_rescale)
def test_coplanar(self): # Check that the coplanar point output option indeed works points = np.random.rand(10, 2) points = np.r_[points, points] # duplicate input data tri = qhull.Delaunay(points) assert_(len(np.unique(tri.simplices.ravel())) == len(points) // 2) assert_(len(tri.coplanar) == len(points) // 2) assert_(len(np.unique(tri.coplanar[:, 2])) == len(points) // 2) assert_(np.all(tri.vertex_to_simplex >= 0))
def test_nd_simplex(self): # simple smoke test: triangulate a n-dimensional simplex for nd in range(2, 8): points = np.zeros((nd+1, nd)) for j in range(nd): points[j,j] = 1.0 points[-1,:] = 1.0 tri = qhull.Delaunay(points) tri.simplices.sort() assert_equal(tri.simplices, np.arange(nd+1, dtype=int)[None, :]) assert_equal(tri.neighbors, -1 + np.zeros((nd+1), dtype=int)[None,:])
def test_hull_consistency_tri(self, name): # Check that a convex hull returned by qhull in ndim # and the hull constructed from ndim delaunay agree points = DATASETS[name] tri = qhull.Delaunay(points) hull = qhull.ConvexHull(points) assert_hulls_equal(points, tri.convex_hull, hull.simplices) # Check that the hull extremes are as expected if points.shape[1] == 2: assert_equal(np.unique(hull.simplices), np.sort(hull.vertices)) else: assert_equal(np.unique(hull.simplices), hull.vertices)
def test_degenerate_barycentric_transforms(self): # The triangulation should not produce invalid barycentric # transforms that stump the simplex finding data = np.load(os.path.join(os.path.dirname(__file__), 'data', 'degenerate_pointset.npz')) points = data['c'] data.close() tri = qhull.Delaunay(points) # Check that there are not too many invalid simplices bad_count = np.isnan(tri.transform[:,0,0]).sum() assert_(bad_count < 23, bad_count) # Check the transforms self._check_barycentric_transforms(tri)
def test_find_simplex(self): # Simple check that simplex finding works points = np.array([(0, 0), (0, 1), (1, 1), (1, 0)], dtype=np.double) tri = qhull.Delaunay(points) # +---+ # |\ 0| # | \ | # |1 \| # +---+ assert_equal(tri.simplices, [[1, 3, 2], [3, 1, 0]]) for p in [(0.25, 0.25, 1), (0.75, 0.75, 0), (0.3, 0.2, 1)]: i = tri.find_simplex(p[:2]) assert_equal(i, p[2], err_msg='%r' % (p, )) j = qhull.tsearch(tri, p[:2]) assert_equal(i, j)
def test_smoketest(self): x = np.array([(0, 0), (0, 2), (1, 0), (1, 2), (0.25, 0.75), (0.6, 0.8)], dtype=float) tri = qhull.Delaunay(x) # Should be exact for linear functions, independent of triangulation funcs = [(lambda x, y: 0 * x + 1, (0, 0)), (lambda x, y: 0 + x, (1, 0)), (lambda x, y: -2 + y, (0, 1)), (lambda x, y: 3 + 3 * x + 14.15 * y, (3, 14.15))] for j, (func, grad) in enumerate(funcs): z = func(x[:, 0], x[:, 1]) dz = interpnd.estimate_gradients_2d_global(tri, z, tol=1e-6) assert_equal(dz.shape, (6, 2)) assert_allclose(dz, np.array(grad)[None, :] + 0 * dz, rtol=1e-5, atol=1e-5, err_msg="item %d" % j)
def test_rectangle(self): points = np.array([(0, 0), (0, 1), (1, 1), (1, 0)], dtype=np.double) tri = qhull.Delaunay(points) self._check(tri)
def test_vertices_deprecation(self): tri = qhull.Delaunay([(0, 0), (0, 1), (1, 0)]) msg = ("Delaunay attribute 'vertices' is deprecated in favour of " "'simplices' and will be removed in Scipy 1.11.0.") with pytest.warns(DeprecationWarning, match=msg): tri.vertices
def test_furthest_site(self): points = [(0, 0), (0, 1), (1, 0), (0.5, 0.5), (1.1, 1.1)] tri = qhull.Delaunay(points, furthest_site=True) expected = np.array([(1, 4, 0), (4, 2, 0)]) # from Qhull assert_array_equal(tri.simplices, expected)
def test_complicated(self): points = np.array([(0, 0), (0, 1), (1, 1), (1, 0), (0.5, 0.5), (0.9, 0.5)], dtype=np.double) tri = qhull.Delaunay(points) self._check(tri)