warp-lang 1.8.1__py3-none-macosx_10_13_universal2.whl → 1.9.0__py3-none-macosx_10_13_universal2.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of warp-lang might be problematic. Click here for more details.
- warp/__init__.py +282 -103
- warp/__init__.pyi +482 -110
- warp/bin/libwarp-clang.dylib +0 -0
- warp/bin/libwarp.dylib +0 -0
- warp/build.py +93 -30
- warp/build_dll.py +47 -67
- warp/builtins.py +955 -137
- warp/codegen.py +312 -206
- warp/config.py +1 -1
- warp/context.py +1249 -784
- warp/examples/core/example_marching_cubes.py +1 -0
- warp/examples/core/example_render_opengl.py +100 -3
- warp/examples/fem/example_apic_fluid.py +98 -52
- warp/examples/fem/example_convection_diffusion_dg.py +25 -4
- warp/examples/fem/example_diffusion_mgpu.py +8 -3
- warp/examples/fem/utils.py +68 -22
- warp/fabric.py +1 -1
- warp/fem/cache.py +27 -19
- warp/fem/domain.py +2 -2
- warp/fem/field/nodal_field.py +2 -2
- warp/fem/field/virtual.py +264 -166
- warp/fem/geometry/geometry.py +5 -5
- warp/fem/integrate.py +129 -51
- warp/fem/space/restriction.py +4 -0
- warp/fem/space/shape/tet_shape_function.py +3 -10
- warp/jax_experimental/custom_call.py +1 -1
- warp/jax_experimental/ffi.py +2 -1
- warp/marching_cubes.py +708 -0
- warp/native/array.h +99 -4
- warp/native/builtin.h +82 -5
- warp/native/bvh.cpp +64 -28
- warp/native/bvh.cu +58 -58
- warp/native/bvh.h +2 -2
- warp/native/clang/clang.cpp +7 -7
- warp/native/coloring.cpp +8 -2
- warp/native/crt.cpp +2 -2
- warp/native/crt.h +3 -5
- warp/native/cuda_util.cpp +41 -10
- warp/native/cuda_util.h +10 -4
- warp/native/exports.h +1842 -1908
- warp/native/fabric.h +2 -1
- warp/native/hashgrid.cpp +37 -37
- warp/native/hashgrid.cu +2 -2
- warp/native/initializer_array.h +1 -1
- warp/native/intersect.h +2 -2
- warp/native/mat.h +1910 -116
- warp/native/mathdx.cpp +43 -43
- warp/native/mesh.cpp +24 -24
- warp/native/mesh.cu +26 -26
- warp/native/mesh.h +4 -2
- warp/native/nanovdb/GridHandle.h +179 -12
- warp/native/nanovdb/HostBuffer.h +8 -7
- warp/native/nanovdb/NanoVDB.h +517 -895
- warp/native/nanovdb/NodeManager.h +323 -0
- warp/native/nanovdb/PNanoVDB.h +2 -2
- warp/native/quat.h +331 -14
- warp/native/range.h +7 -1
- warp/native/reduce.cpp +10 -10
- warp/native/reduce.cu +13 -14
- warp/native/runlength_encode.cpp +2 -2
- warp/native/runlength_encode.cu +5 -5
- warp/native/scan.cpp +3 -3
- warp/native/scan.cu +4 -4
- warp/native/sort.cpp +10 -10
- warp/native/sort.cu +22 -22
- warp/native/sparse.cpp +8 -8
- warp/native/sparse.cu +13 -13
- warp/native/spatial.h +366 -17
- warp/native/temp_buffer.h +2 -2
- warp/native/tile.h +283 -69
- warp/native/vec.h +381 -14
- warp/native/volume.cpp +54 -54
- warp/native/volume.cu +1 -1
- warp/native/volume.h +2 -1
- warp/native/volume_builder.cu +30 -37
- warp/native/warp.cpp +150 -149
- warp/native/warp.cu +323 -192
- warp/native/warp.h +227 -226
- warp/optim/linear.py +736 -271
- warp/render/imgui_manager.py +289 -0
- warp/render/render_opengl.py +85 -6
- warp/sim/graph_coloring.py +2 -2
- warp/sparse.py +558 -175
- warp/tests/aux_test_module_aot.py +7 -0
- warp/tests/cuda/test_async.py +3 -3
- warp/tests/cuda/test_conditional_captures.py +101 -0
- warp/tests/geometry/test_marching_cubes.py +233 -12
- warp/tests/sim/test_coloring.py +6 -6
- warp/tests/test_array.py +56 -5
- warp/tests/test_codegen.py +3 -2
- warp/tests/test_context.py +8 -15
- warp/tests/test_enum.py +136 -0
- warp/tests/test_examples.py +2 -2
- warp/tests/test_fem.py +45 -2
- warp/tests/test_fixedarray.py +229 -0
- warp/tests/test_func.py +18 -15
- warp/tests/test_future_annotations.py +7 -5
- warp/tests/test_linear_solvers.py +30 -0
- warp/tests/test_map.py +1 -1
- warp/tests/test_mat.py +1518 -378
- warp/tests/test_mat_assign_copy.py +178 -0
- warp/tests/test_mat_constructors.py +574 -0
- warp/tests/test_module_aot.py +287 -0
- warp/tests/test_print.py +69 -0
- warp/tests/test_quat.py +140 -34
- warp/tests/test_quat_assign_copy.py +145 -0
- warp/tests/test_reload.py +2 -1
- warp/tests/test_sparse.py +71 -0
- warp/tests/test_spatial.py +140 -34
- warp/tests/test_spatial_assign_copy.py +160 -0
- warp/tests/test_struct.py +43 -3
- warp/tests/test_types.py +0 -20
- warp/tests/test_vec.py +179 -34
- warp/tests/test_vec_assign_copy.py +143 -0
- warp/tests/tile/test_tile.py +184 -18
- warp/tests/tile/test_tile_cholesky.py +605 -0
- warp/tests/tile/test_tile_load.py +169 -0
- warp/tests/tile/test_tile_mathdx.py +2 -558
- warp/tests/tile/test_tile_matmul.py +1 -1
- warp/tests/tile/test_tile_mlp.py +1 -1
- warp/tests/tile/test_tile_shared_memory.py +5 -5
- warp/tests/unittest_suites.py +6 -0
- warp/tests/walkthrough_debug.py +1 -1
- warp/thirdparty/unittest_parallel.py +108 -9
- warp/types.py +554 -264
- warp/utils.py +68 -86
- {warp_lang-1.8.1.dist-info → warp_lang-1.9.0.dist-info}/METADATA +28 -65
- {warp_lang-1.8.1.dist-info → warp_lang-1.9.0.dist-info}/RECORD +131 -121
- warp/native/marching.cpp +0 -19
- warp/native/marching.cu +0 -514
- warp/native/marching.h +0 -19
- {warp_lang-1.8.1.dist-info → warp_lang-1.9.0.dist-info}/WHEEL +0 -0
- {warp_lang-1.8.1.dist-info → warp_lang-1.9.0.dist-info}/licenses/LICENSE.md +0 -0
- {warp_lang-1.8.1.dist-info → warp_lang-1.9.0.dist-info}/top_level.txt +0 -0
warp/tests/cuda/test_async.py
CHANGED
|
@@ -426,13 +426,13 @@ def copy_template(test, src_ctor, dst_ctor, src_device, dst_device, n, params: C
|
|
|
426
426
|
if expected_error_type is not None:
|
|
427
427
|
# disable error output from Warp if we expect an exception
|
|
428
428
|
try:
|
|
429
|
-
saved_error_output_enabled = wp.context.runtime.core.
|
|
430
|
-
wp.context.runtime.core.
|
|
429
|
+
saved_error_output_enabled = wp.context.runtime.core.wp_is_error_output_enabled()
|
|
430
|
+
wp.context.runtime.core.wp_set_error_output_enabled(False)
|
|
431
431
|
with test.assertRaisesRegex(expected_error_type, expected_error_regex):
|
|
432
432
|
with Capturable(use_graph=params.use_graph, stream=stream):
|
|
433
433
|
wp.copy(dst, src, stream=stream_arg)
|
|
434
434
|
finally:
|
|
435
|
-
wp.context.runtime.core.
|
|
435
|
+
wp.context.runtime.core.wp_set_error_output_enabled(saved_error_output_enabled)
|
|
436
436
|
wp.synchronize()
|
|
437
437
|
|
|
438
438
|
# print(f"SUCCESSFUL ERROR PREDICTION: {expected_error_regex}")
|
|
@@ -979,6 +979,94 @@ def test_graph_debug_dot_print(test, device):
|
|
|
979
979
|
os.remove(dot_file)
|
|
980
980
|
|
|
981
981
|
|
|
982
|
+
# ================================================================================================================
|
|
983
|
+
# test exceptions
|
|
984
|
+
# ================================================================================================================
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
# body graphs with allocations are not supported
|
|
988
|
+
def body_with_alloc():
|
|
989
|
+
wp.zeros(10)
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
993
|
+
def test_error_alloc_if(test, device):
|
|
994
|
+
with wp.ScopedDevice(device):
|
|
995
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
996
|
+
with test.assertRaisesRegex(
|
|
997
|
+
RuntimeError, r"Conditional body graph contains an unsupported operation \(memory allocation\)"
|
|
998
|
+
):
|
|
999
|
+
with wp.ScopedCapture():
|
|
1000
|
+
wp.capture_if(condition=cond, on_true=body_with_alloc)
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1004
|
+
def test_error_alloc_else(test, device):
|
|
1005
|
+
with wp.ScopedDevice(device):
|
|
1006
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1007
|
+
with test.assertRaisesRegex(
|
|
1008
|
+
RuntimeError, r"Conditional body graph contains an unsupported operation \(memory allocation\)"
|
|
1009
|
+
):
|
|
1010
|
+
with wp.ScopedCapture():
|
|
1011
|
+
wp.capture_if(condition=cond, on_false=body_with_alloc)
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1015
|
+
def test_error_alloc_while(test, device):
|
|
1016
|
+
with wp.ScopedDevice(device):
|
|
1017
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1018
|
+
with test.assertRaisesRegex(
|
|
1019
|
+
RuntimeError, r"Conditional body graph contains an unsupported operation \(memory allocation\)"
|
|
1020
|
+
):
|
|
1021
|
+
with wp.ScopedCapture():
|
|
1022
|
+
wp.capture_while(condition=cond, while_body=body_with_alloc)
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1026
|
+
def test_error_alloc_if_subgraph(test, device):
|
|
1027
|
+
with wp.ScopedDevice(device):
|
|
1028
|
+
# capture body subgraph
|
|
1029
|
+
with wp.ScopedCapture() as body_capture:
|
|
1030
|
+
body_with_alloc()
|
|
1031
|
+
|
|
1032
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1033
|
+
with test.assertRaisesRegex(
|
|
1034
|
+
RuntimeError, r"Child graph contains an unsupported operation \(memory allocation\)"
|
|
1035
|
+
):
|
|
1036
|
+
with wp.ScopedCapture():
|
|
1037
|
+
wp.capture_if(condition=cond, on_true=body_capture.graph)
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1041
|
+
def test_error_alloc_else_subgraph(test, device):
|
|
1042
|
+
with wp.ScopedDevice(device):
|
|
1043
|
+
# capture body subgraph
|
|
1044
|
+
with wp.ScopedCapture() as body_capture:
|
|
1045
|
+
body_with_alloc()
|
|
1046
|
+
|
|
1047
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1048
|
+
with test.assertRaisesRegex(
|
|
1049
|
+
RuntimeError, r"Child graph contains an unsupported operation \(memory allocation\)"
|
|
1050
|
+
):
|
|
1051
|
+
with wp.ScopedCapture():
|
|
1052
|
+
wp.capture_if(condition=cond, on_false=body_capture.graph)
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1056
|
+
def test_error_alloc_while_subgraph(test, device):
|
|
1057
|
+
with wp.ScopedDevice(device):
|
|
1058
|
+
# capture body subgraph
|
|
1059
|
+
with wp.ScopedCapture() as body_capture:
|
|
1060
|
+
body_with_alloc()
|
|
1061
|
+
|
|
1062
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1063
|
+
with test.assertRaisesRegex(
|
|
1064
|
+
RuntimeError, r"Child graph contains an unsupported operation \(memory allocation\)"
|
|
1065
|
+
):
|
|
1066
|
+
with wp.ScopedCapture():
|
|
1067
|
+
wp.capture_while(condition=cond, while_body=body_capture.graph)
|
|
1068
|
+
|
|
1069
|
+
|
|
982
1070
|
devices = get_test_devices()
|
|
983
1071
|
cuda_devices = get_cuda_test_devices()
|
|
984
1072
|
|
|
@@ -1040,6 +1128,19 @@ add_function_test(
|
|
|
1040
1128
|
TestConditionalCaptures, "test_graph_debug_dot_print", test_graph_debug_dot_print, devices=cuda_devices
|
|
1041
1129
|
)
|
|
1042
1130
|
|
|
1131
|
+
add_function_test(TestConditionalCaptures, "test_error_alloc_if", test_error_alloc_if, devices=cuda_devices)
|
|
1132
|
+
add_function_test(TestConditionalCaptures, "test_error_alloc_else", test_error_alloc_else, devices=cuda_devices)
|
|
1133
|
+
add_function_test(TestConditionalCaptures, "test_error_alloc_while", test_error_alloc_while, devices=cuda_devices)
|
|
1134
|
+
add_function_test(
|
|
1135
|
+
TestConditionalCaptures, "test_error_alloc_if_subgraph", test_error_alloc_if_subgraph, devices=cuda_devices
|
|
1136
|
+
)
|
|
1137
|
+
add_function_test(
|
|
1138
|
+
TestConditionalCaptures, "test_error_alloc_else_subgraph", test_error_alloc_else_subgraph, devices=cuda_devices
|
|
1139
|
+
)
|
|
1140
|
+
add_function_test(
|
|
1141
|
+
TestConditionalCaptures, "test_error_alloc_while_subgraph", test_error_alloc_while_subgraph, devices=cuda_devices
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1043
1144
|
|
|
1044
1145
|
if __name__ == "__main__":
|
|
1045
1146
|
wp.clear_kernel_cache()
|
|
@@ -22,7 +22,9 @@ from warp.tests.unittest_utils import *
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@wp.kernel
|
|
25
|
-
def
|
|
25
|
+
def make_field_sphere_sdf(field: wp.array3d(dtype=float), center: wp.vec3, radius: float):
|
|
26
|
+
"""Make a sphere SDF for nodes on the integer domain with node coordinates 0,1,2,3,..."""
|
|
27
|
+
|
|
26
28
|
i, j, k = wp.tid()
|
|
27
29
|
|
|
28
30
|
p = wp.vec3(float(i), float(j), float(k))
|
|
@@ -32,31 +34,242 @@ def make_field(field: wp.array3d(dtype=float), center: wp.vec3, radius: float):
|
|
|
32
34
|
field[i, j, k] = d
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
@wp.kernel
|
|
38
|
+
def make_field_sphere_sdf_unit_domain(
|
|
39
|
+
field: wp.array3d(dtype=float), center: wp.vec3, radius: wp.array(dtype=wp.float32)
|
|
40
|
+
):
|
|
41
|
+
"""Makes a sphere SDF for nodes on the unit domain [-1, 1]^3."""
|
|
42
|
+
i, j, k = wp.tid()
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
nx, ny, nz = field.shape[0], field.shape[1], field.shape[2]
|
|
45
|
+
|
|
46
|
+
p = wp.vec3(
|
|
47
|
+
2.0 * wp.float32(i) / (wp.float32(nx) - 1.0) - 1.0,
|
|
48
|
+
2.0 * wp.float32(j) / (wp.float32(ny) - 1.0) - 1.0,
|
|
49
|
+
2.0 * wp.float32(k) / (wp.float32(nz) - 1.0) - 1.0,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
d = wp.length(p - center) - radius[0]
|
|
53
|
+
|
|
54
|
+
field[i, j, k] = d
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@wp.kernel
|
|
58
|
+
def compute_surface_area(
|
|
59
|
+
verts: wp.array(dtype=wp.vec3), faces: wp.array(dtype=wp.int32), out_area: wp.array(dtype=wp.float32)
|
|
60
|
+
):
|
|
61
|
+
tid = wp.tid()
|
|
62
|
+
vi = faces[3 * tid + 0]
|
|
63
|
+
vj = faces[3 * tid + 1]
|
|
64
|
+
vk = faces[3 * tid + 2]
|
|
65
|
+
|
|
66
|
+
p0 = verts[vi]
|
|
67
|
+
p1 = verts[vj]
|
|
68
|
+
p2 = verts[vk]
|
|
69
|
+
|
|
70
|
+
# Heron's formula for triangle area
|
|
71
|
+
a = wp.length(p1 - p0)
|
|
72
|
+
b = wp.length(p2 - p0)
|
|
73
|
+
c = wp.length(p2 - p1)
|
|
74
|
+
s = (a + b + c) / 2.0
|
|
75
|
+
area = wp.sqrt(s * (s - a) * (s - b) * (s - c))
|
|
41
76
|
|
|
42
|
-
|
|
77
|
+
wp.atomic_add(out_area, 0, area)
|
|
43
78
|
|
|
44
|
-
radius = dim / 4.0
|
|
45
79
|
|
|
46
|
-
|
|
80
|
+
def validate_marching_cubes_output(test, verts_np, faces_np, check_nonempty=True):
|
|
81
|
+
# check that the face array seems valid
|
|
82
|
+
if check_nonempty:
|
|
83
|
+
test.assertGreater(faces_np.shape[0], 0) # at least one face
|
|
84
|
+
test.assertEqual(faces_np.shape[1], 3) # all faces triangular
|
|
85
|
+
test.assertTrue((faces_np >= 0).all()) # all face inds nonnegative
|
|
86
|
+
test.assertTrue((faces_np < verts_np.shape[0]).all()) # all face inds are in-bounds on the vertex array
|
|
87
|
+
test.assertTrue((faces_np[:, 0] != faces_np[:, 1]).all()) # all faces have unique vertices
|
|
88
|
+
test.assertTrue((faces_np[:, 0] != faces_np[:, 2]).all()) # all faces have unique vertices
|
|
89
|
+
test.assertTrue((faces_np[:, 1] != faces_np[:, 2]).all()) # all faces have unique vertices
|
|
90
|
+
test.assertTrue(
|
|
91
|
+
(np.unique(faces_np.flatten()) == np.arange(verts_np.shape[0])).all()
|
|
92
|
+
) # all vertices are used in at least one face
|
|
93
|
+
|
|
94
|
+
# check that the vertex array seems valid
|
|
95
|
+
if check_nonempty:
|
|
96
|
+
test.assertGreater(verts_np.shape[0], 0) # at least one vertex
|
|
97
|
+
test.assertEqual(verts_np.shape[1], 3) # all vertices are 3D
|
|
98
|
+
test.assertTrue(np.isfinite(verts_np).all()) # all vertices are finite
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_marching_cubes(test, device):
|
|
102
|
+
"""Basic test of typical usage."""
|
|
103
|
+
node_dim = 64
|
|
104
|
+
cell_dim = node_dim - 1
|
|
105
|
+
field = wp.zeros(shape=(node_dim, node_dim, node_dim), dtype=float, device=device)
|
|
106
|
+
bounds_low = (0.0, 0.0, 0.0)
|
|
107
|
+
bounds_high = (float(cell_dim), float(cell_dim), float(cell_dim))
|
|
108
|
+
|
|
109
|
+
iso = wp.MarchingCubes(
|
|
110
|
+
nx=node_dim,
|
|
111
|
+
ny=node_dim,
|
|
112
|
+
nz=node_dim,
|
|
113
|
+
device=device,
|
|
114
|
+
domain_bounds_lower_corner=bounds_low,
|
|
115
|
+
domain_bounds_upper_corner=bounds_high,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
radius = node_dim / 4.0
|
|
119
|
+
|
|
120
|
+
wp.launch(
|
|
121
|
+
make_field_sphere_sdf,
|
|
122
|
+
dim=field.shape,
|
|
123
|
+
inputs=[field, wp.vec3(node_dim / 2, node_dim / 2, node_dim / 2), radius],
|
|
124
|
+
device=device,
|
|
125
|
+
)
|
|
47
126
|
|
|
48
127
|
iso.surface(field=field, threshold=0.0)
|
|
128
|
+
verts_np = iso.verts.numpy()
|
|
129
|
+
faces_np = iso.indices.numpy().reshape(-1, 3)
|
|
130
|
+
test.assertEqual(
|
|
131
|
+
iso.indices.dtype, wp.int32
|
|
132
|
+
) # make sure we are following Warp convention of using a flat array of indices
|
|
133
|
+
validate_marching_cubes_output(test, verts_np, faces_np)
|
|
49
134
|
|
|
50
135
|
# check that all returned vertices lie on the surface of the sphere
|
|
51
|
-
length = np.linalg.norm(
|
|
136
|
+
length = np.linalg.norm(verts_np - np.array([node_dim / 2, node_dim / 2, node_dim / 2]), axis=1)
|
|
52
137
|
error = np.abs(length - radius)
|
|
138
|
+
test.assertTrue(np.max(error) < 1.0)
|
|
139
|
+
|
|
140
|
+
iso.resize(nx=node_dim * 2, ny=node_dim * 2, nz=node_dim * 2) # smoke test for deprecated function
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_marching_cubes_functional(test, device):
|
|
144
|
+
"""Ensure the single-function interface works as expected."""
|
|
145
|
+
node_dim = 64
|
|
146
|
+
cell_dim = node_dim - 1
|
|
147
|
+
field = wp.zeros(shape=(node_dim, node_dim, node_dim), dtype=float, device=device)
|
|
148
|
+
bounds_low = (0.0, 0.0, 0.0)
|
|
149
|
+
bounds_high = (float(cell_dim), float(cell_dim), float(cell_dim))
|
|
150
|
+
|
|
151
|
+
radius = node_dim / 4.0
|
|
152
|
+
wp.launch(
|
|
153
|
+
make_field_sphere_sdf,
|
|
154
|
+
dim=field.shape,
|
|
155
|
+
inputs=[field, wp.vec3(node_dim / 2, node_dim / 2, node_dim / 2), radius],
|
|
156
|
+
device=device,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# call via the functional interface
|
|
160
|
+
verts, faces = wp.MarchingCubes.extract_surface_marching_cubes(
|
|
161
|
+
field, threshold=0.0, domain_bounds_lower_corner=bounds_low, domain_bounds_upper_corner=bounds_high
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
verts_np = verts.numpy()
|
|
165
|
+
faces_np = faces.numpy().reshape(-1, 3)
|
|
166
|
+
validate_marching_cubes_output(test, verts_np, faces_np)
|
|
53
167
|
|
|
168
|
+
# check that all returned vertices lie on the surface of the sphere
|
|
169
|
+
length = np.linalg.norm(verts_np - np.array([node_dim / 2, node_dim / 2, node_dim / 2]), axis=1)
|
|
170
|
+
error = np.abs(length - radius)
|
|
54
171
|
test.assertTrue(np.max(error) < 1.0)
|
|
55
172
|
|
|
56
|
-
|
|
173
|
+
|
|
174
|
+
def test_marching_cubes_nonuniform(test, device):
|
|
175
|
+
"""Test the logic for when the dimensions of the grid are not uniform."""
|
|
176
|
+
|
|
177
|
+
dimX = 64
|
|
178
|
+
dimY = 48
|
|
179
|
+
dimZ = 72
|
|
180
|
+
field = wp.zeros(shape=(dimX, dimY, dimZ), dtype=float, device=device)
|
|
181
|
+
|
|
182
|
+
bounds_low = wp.vec3(0.0, 0.0, 0.0)
|
|
183
|
+
bounds_high = wp.vec3(float(dimX), float(dimY), float(dimZ))
|
|
184
|
+
|
|
185
|
+
iso = wp.MarchingCubes(
|
|
186
|
+
nx=dimX,
|
|
187
|
+
ny=dimY,
|
|
188
|
+
nz=dimZ,
|
|
189
|
+
device=device,
|
|
190
|
+
domain_bounds_lower_corner=bounds_low,
|
|
191
|
+
domain_bounds_upper_corner=bounds_high,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
radius = dimX / 4.0
|
|
195
|
+
wp.launch(
|
|
196
|
+
make_field_sphere_sdf,
|
|
197
|
+
dim=field.shape,
|
|
198
|
+
inputs=[field, wp.vec3(dimX / 2, dimY / 2, dimZ / 2), radius],
|
|
199
|
+
device=device,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
iso.surface(field=field, threshold=0.0)
|
|
203
|
+
verts_np = iso.verts.numpy()
|
|
204
|
+
faces_np = iso.indices.numpy().reshape(-1, 3)
|
|
205
|
+
validate_marching_cubes_output(test, verts_np, faces_np)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_marching_cubes_empty_output(test, device):
|
|
209
|
+
"""Make sure we handle the empty-output case correctly."""
|
|
210
|
+
|
|
211
|
+
dim = 64
|
|
212
|
+
field = wp.zeros(shape=(dim, dim, dim), dtype=float, device=device)
|
|
213
|
+
|
|
214
|
+
iso = wp.MarchingCubes(nx=dim, ny=dim, nz=dim, device=device)
|
|
215
|
+
|
|
216
|
+
wp.launch(make_field_sphere_sdf, dim=field.shape, inputs=[field, wp.vec3(0.5, 0.5, 0.5), 0.25], device=device)
|
|
217
|
+
|
|
218
|
+
iso.surface(field=field, threshold=1000.0) # set threshold to a large value so that no vertices are generated
|
|
219
|
+
verts_np = iso.verts.numpy()
|
|
220
|
+
faces_np = iso.indices.numpy().reshape(-1, 3)
|
|
221
|
+
validate_marching_cubes_output(test, verts_np, faces_np, check_nonempty=False)
|
|
222
|
+
|
|
223
|
+
test.assertEqual(faces_np.shape[0], 0) # no faces
|
|
224
|
+
test.assertEqual(verts_np.shape[0], 0) # no vertices
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def test_marching_cubes_differentiable(test, device):
|
|
228
|
+
"""Check that marching cubes has reasonable gradients.
|
|
229
|
+
|
|
230
|
+
This test constructs an SDF of sphere, extracts a surface, computes its
|
|
231
|
+
surface area, and then differentiates the surface area with respect to
|
|
232
|
+
the sphere's radius.
|
|
233
|
+
"""
|
|
234
|
+
node_dim = 64
|
|
235
|
+
cell_dim = node_dim - 1
|
|
236
|
+
bounds_low = wp.vec3(-1.0, -1.0, -1.0)
|
|
237
|
+
bounds_high = wp.vec3(1.0, 1.0, 1.0)
|
|
238
|
+
|
|
239
|
+
radius = 0.5
|
|
240
|
+
radius_wp = wp.full((1,), value=0.5, dtype=wp.float32, device=device, requires_grad=True)
|
|
241
|
+
|
|
242
|
+
with wp.Tape() as tape:
|
|
243
|
+
field = wp.zeros(shape=(node_dim, node_dim, node_dim), dtype=float, device=device, requires_grad=True)
|
|
244
|
+
wp.launch(
|
|
245
|
+
make_field_sphere_sdf_unit_domain,
|
|
246
|
+
dim=field.shape,
|
|
247
|
+
inputs=[field, wp.vec3(0.0, 0.0, 0.0), radius_wp],
|
|
248
|
+
device=device,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# call via the functional interface
|
|
252
|
+
verts, faces = wp.MarchingCubes.extract_surface_marching_cubes(
|
|
253
|
+
field, threshold=0.0, domain_bounds_lower_corner=bounds_low, domain_bounds_upper_corner=bounds_high
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# compute surface area
|
|
257
|
+
area = wp.zeros(shape=(1,), dtype=float, device=device, requires_grad=True)
|
|
258
|
+
wp.launch(compute_surface_area, dim=faces.shape[0] // 3, inputs=[verts, faces, area], device=device)
|
|
259
|
+
|
|
260
|
+
# confirm surface area is correct vs. the analytical ground truth
|
|
261
|
+
area_np = area.numpy()[0]
|
|
262
|
+
test.assertTrue(np.abs(area_np - 4.0 * np.pi * radius * radius) < 1e-2)
|
|
263
|
+
|
|
264
|
+
# compute the gradient of the surface area with respect to the radius
|
|
265
|
+
tape.backward(area)
|
|
266
|
+
|
|
267
|
+
# confirm the gradient is correct vs. the analytical ground truth
|
|
268
|
+
grad_np = radius_wp.grad.numpy()[0]
|
|
269
|
+
test.assertTrue(np.abs(grad_np - 8.0 * np.pi * radius) < 1e-2)
|
|
57
270
|
|
|
58
271
|
|
|
59
|
-
devices =
|
|
272
|
+
devices = get_test_devices()
|
|
60
273
|
|
|
61
274
|
|
|
62
275
|
class TestMarchingCubes(unittest.TestCase):
|
|
@@ -67,6 +280,14 @@ class TestMarchingCubes(unittest.TestCase):
|
|
|
67
280
|
|
|
68
281
|
|
|
69
282
|
add_function_test(TestMarchingCubes, "test_marching_cubes", test_marching_cubes, devices=devices)
|
|
283
|
+
add_function_test(TestMarchingCubes, "test_marching_cubes_functional", test_marching_cubes_functional, devices=devices)
|
|
284
|
+
add_function_test(TestMarchingCubes, "test_marching_cubes_nonuniform", test_marching_cubes_nonuniform, devices=devices)
|
|
285
|
+
add_function_test(
|
|
286
|
+
TestMarchingCubes, "test_marching_cubes_empty_output", test_marching_cubes_empty_output, devices=devices
|
|
287
|
+
)
|
|
288
|
+
add_function_test(
|
|
289
|
+
TestMarchingCubes, "test_marching_cubes_differentiable", test_marching_cubes_differentiable, devices=devices
|
|
290
|
+
)
|
|
70
291
|
|
|
71
292
|
|
|
72
293
|
if __name__ == "__main__":
|
warp/tests/sim/test_coloring.py
CHANGED
|
@@ -150,7 +150,7 @@ def test_coloring_trimesh(test, device):
|
|
|
150
150
|
edge_indices_cpu = wp.array(model.edge_indices.numpy()[:, 2:], dtype=int, device="cpu")
|
|
151
151
|
|
|
152
152
|
# coloring without bending
|
|
153
|
-
num_colors_greedy = wp.context.runtime.core.
|
|
153
|
+
num_colors_greedy = wp.context.runtime.core.wp_graph_coloring(
|
|
154
154
|
model.particle_count,
|
|
155
155
|
edge_indices_cpu.__ctype__(),
|
|
156
156
|
ColoringAlgorithm.GREEDY.value,
|
|
@@ -163,7 +163,7 @@ def test_coloring_trimesh(test, device):
|
|
|
163
163
|
device="cpu",
|
|
164
164
|
)
|
|
165
165
|
|
|
166
|
-
num_colors_mcs = wp.context.runtime.core.
|
|
166
|
+
num_colors_mcs = wp.context.runtime.core.wp_graph_coloring(
|
|
167
167
|
model.particle_count,
|
|
168
168
|
edge_indices_cpu.__ctype__(),
|
|
169
169
|
ColoringAlgorithm.MCS.value,
|
|
@@ -178,13 +178,13 @@ def test_coloring_trimesh(test, device):
|
|
|
178
178
|
|
|
179
179
|
# coloring with bending
|
|
180
180
|
edge_indices_cpu_with_bending = construct_trimesh_graph_edges(model.edge_indices, True)
|
|
181
|
-
num_colors_greedy = wp.context.runtime.core.
|
|
181
|
+
num_colors_greedy = wp.context.runtime.core.wp_graph_coloring(
|
|
182
182
|
model.particle_count,
|
|
183
183
|
edge_indices_cpu_with_bending.__ctype__(),
|
|
184
184
|
ColoringAlgorithm.GREEDY.value,
|
|
185
185
|
particle_colors.__ctype__(),
|
|
186
186
|
)
|
|
187
|
-
wp.context.runtime.core.
|
|
187
|
+
wp.context.runtime.core.wp_balance_coloring(
|
|
188
188
|
model.particle_count,
|
|
189
189
|
edge_indices_cpu_with_bending.__ctype__(),
|
|
190
190
|
num_colors_greedy,
|
|
@@ -198,13 +198,13 @@ def test_coloring_trimesh(test, device):
|
|
|
198
198
|
device="cpu",
|
|
199
199
|
)
|
|
200
200
|
|
|
201
|
-
num_colors_mcs = wp.context.runtime.core.
|
|
201
|
+
num_colors_mcs = wp.context.runtime.core.wp_graph_coloring(
|
|
202
202
|
model.particle_count,
|
|
203
203
|
edge_indices_cpu_with_bending.__ctype__(),
|
|
204
204
|
ColoringAlgorithm.MCS.value,
|
|
205
205
|
particle_colors.__ctype__(),
|
|
206
206
|
)
|
|
207
|
-
max_min_ratio = wp.context.runtime.core.
|
|
207
|
+
max_min_ratio = wp.context.runtime.core.wp_balance_coloring(
|
|
208
208
|
model.particle_count,
|
|
209
209
|
edge_indices_cpu_with_bending.__ctype__(),
|
|
210
210
|
num_colors_mcs,
|
warp/tests/test_array.py
CHANGED
|
@@ -2902,10 +2902,8 @@ def test_direct_from_numpy(test, device):
|
|
|
2902
2902
|
|
|
2903
2903
|
|
|
2904
2904
|
@wp.kernel
|
|
2905
|
-
def kernel_array_from_ptr(
|
|
2906
|
-
ptr
|
|
2907
|
-
):
|
|
2908
|
-
arr = wp.array(ptr=ptr, shape=(2, 3), dtype=wp.float32)
|
|
2905
|
+
def kernel_array_from_ptr(arr_orig: wp.array2d(dtype=wp.float32)):
|
|
2906
|
+
arr = wp.array(ptr=arr_orig.ptr, shape=(2, 3), dtype=wp.float32)
|
|
2909
2907
|
arr[0, 0] = 1.0
|
|
2910
2908
|
arr[0, 1] = 2.0
|
|
2911
2909
|
arr[0, 2] = 3.0
|
|
@@ -2913,7 +2911,56 @@ def kernel_array_from_ptr(
|
|
|
2913
2911
|
|
|
2914
2912
|
def test_kernel_array_from_ptr(test, device):
|
|
2915
2913
|
arr = wp.zeros(shape=(2, 3), dtype=wp.float32, device=device)
|
|
2916
|
-
wp.launch(kernel_array_from_ptr, dim=(1,), inputs=(arr
|
|
2914
|
+
wp.launch(kernel_array_from_ptr, dim=(1,), inputs=(arr,), device=device)
|
|
2915
|
+
assert_np_equal(arr.numpy(), np.array(((1.0, 2.0, 3.0), (0.0, 0.0, 0.0))))
|
|
2916
|
+
|
|
2917
|
+
|
|
2918
|
+
@wp.struct
|
|
2919
|
+
class MyStruct:
|
|
2920
|
+
a: wp.float32
|
|
2921
|
+
b: wp.float32
|
|
2922
|
+
c: wp.float32
|
|
2923
|
+
|
|
2924
|
+
|
|
2925
|
+
@wp.kernel
|
|
2926
|
+
def kernel_array_from_ptr_struct(arr_orig: wp.array(dtype=MyStruct)):
|
|
2927
|
+
arr = wp.array(ptr=arr_orig.ptr, shape=(2,), dtype=MyStruct)
|
|
2928
|
+
arr[0].a = 1.0
|
|
2929
|
+
arr[0].b = 2.0
|
|
2930
|
+
arr[0].c = 3.0
|
|
2931
|
+
arr[1].a = 4.0
|
|
2932
|
+
arr[1].b = 5.0
|
|
2933
|
+
arr[1].c = 6.0
|
|
2934
|
+
|
|
2935
|
+
|
|
2936
|
+
def test_kernel_array_from_ptr_struct(test, device):
|
|
2937
|
+
arr = wp.zeros(shape=(2,), dtype=MyStruct, device=device)
|
|
2938
|
+
wp.launch(kernel_array_from_ptr_struct, dim=(1,), inputs=(arr,), device=device)
|
|
2939
|
+
arr_np = arr.numpy()
|
|
2940
|
+
expected = np.zeros_like(arr_np)
|
|
2941
|
+
expected[0] = (1.0, 2.0, 3.0)
|
|
2942
|
+
expected[1] = (4.0, 5.0, 6.0)
|
|
2943
|
+
assert_np_equal(arr_np, expected)
|
|
2944
|
+
|
|
2945
|
+
|
|
2946
|
+
@wp.kernel
|
|
2947
|
+
def kernel_array_from_ptr_variable_shape(
|
|
2948
|
+
ptr: wp.uint64,
|
|
2949
|
+
shape_x: int,
|
|
2950
|
+
shape_y: int,
|
|
2951
|
+
):
|
|
2952
|
+
arr = wp.array(ptr=ptr, shape=(shape_x, shape_y), dtype=wp.float32)
|
|
2953
|
+
arr[0, 0] = 1.0
|
|
2954
|
+
arr[0, 1] = 2.0
|
|
2955
|
+
if shape_y > 2:
|
|
2956
|
+
arr[0, 2] = 3.0
|
|
2957
|
+
|
|
2958
|
+
|
|
2959
|
+
def test_kernel_array_from_ptr_variable_shape(test, device):
|
|
2960
|
+
arr = wp.zeros(shape=(2, 3), dtype=wp.float32, device=device)
|
|
2961
|
+
wp.launch(kernel_array_from_ptr_variable_shape, dim=(1,), inputs=(arr.ptr, 2, 2), device=device)
|
|
2962
|
+
assert_np_equal(arr.numpy(), np.array(((1.0, 2.0, 0.0), (0.0, 0.0, 0.0))))
|
|
2963
|
+
wp.launch(kernel_array_from_ptr_variable_shape, dim=(1,), inputs=(arr.ptr, 2, 3), device=device)
|
|
2917
2964
|
assert_np_equal(arr.numpy(), np.array(((1.0, 2.0, 3.0), (0.0, 0.0, 0.0))))
|
|
2918
2965
|
|
|
2919
2966
|
|
|
@@ -3185,6 +3232,10 @@ add_function_test(TestArray, "test_array_inplace_diff_ops", test_array_inplace_d
|
|
|
3185
3232
|
add_function_test(TestArray, "test_array_inplace_non_diff_ops", test_array_inplace_non_diff_ops, devices=devices)
|
|
3186
3233
|
add_function_test(TestArray, "test_direct_from_numpy", test_direct_from_numpy, devices=["cpu"])
|
|
3187
3234
|
add_function_test(TestArray, "test_kernel_array_from_ptr", test_kernel_array_from_ptr, devices=devices)
|
|
3235
|
+
add_function_test(TestArray, "test_kernel_array_from_ptr_struct", test_kernel_array_from_ptr_struct, devices=devices)
|
|
3236
|
+
add_function_test(
|
|
3237
|
+
TestArray, "test_kernel_array_from_ptr_variable_shape", test_kernel_array_from_ptr_variable_shape, devices=devices
|
|
3238
|
+
)
|
|
3188
3239
|
|
|
3189
3240
|
add_function_test(TestArray, "test_array_from_int32_domain", test_array_from_int32_domain, devices=devices)
|
|
3190
3241
|
add_function_test(TestArray, "test_array_from_int64_domain", test_array_from_int64_domain, devices=devices)
|
warp/tests/test_codegen.py
CHANGED
|
@@ -756,6 +756,7 @@ def test_multiple_return_values(test, device):
|
|
|
756
756
|
test_multiple_return_values_quat_to_axis_angle_kernel,
|
|
757
757
|
dim=1,
|
|
758
758
|
inputs=(q, expected_axis, expected_angle),
|
|
759
|
+
device=device,
|
|
759
760
|
)
|
|
760
761
|
|
|
761
762
|
# fmt: off
|
|
@@ -791,9 +792,9 @@ def test_multiple_return_values(test, device):
|
|
|
791
792
|
|
|
792
793
|
test.assertAlmostEqual(V[0][0], expected_V[0][0], places=5)
|
|
793
794
|
test.assertAlmostEqual(V[0][1], expected_V[0][1], places=5)
|
|
794
|
-
test.assertAlmostEqual(V[0][2], expected_V[0][2], places=
|
|
795
|
+
test.assertAlmostEqual(V[0][2], expected_V[0][2], places=4) # precision issue on ARM64 (GH-905)
|
|
795
796
|
test.assertAlmostEqual(V[1][0], expected_V[1][0], places=5)
|
|
796
|
-
test.assertAlmostEqual(V[1][1], expected_V[1][1], places=
|
|
797
|
+
test.assertAlmostEqual(V[1][1], expected_V[1][1], places=4) # precision issue on ARM64 (GH-905)
|
|
797
798
|
test.assertAlmostEqual(V[1][2], expected_V[1][2], places=5)
|
|
798
799
|
test.assertAlmostEqual(V[2][0], expected_V[2][0], places=5)
|
|
799
800
|
test.assertAlmostEqual(V[2][1], expected_V[2][1], places=5)
|
warp/tests/test_context.py
CHANGED
|
@@ -17,24 +17,17 @@ import unittest
|
|
|
17
17
|
from typing import List, Tuple
|
|
18
18
|
|
|
19
19
|
import warp as wp
|
|
20
|
-
from warp.tests.unittest_utils import *
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_context_type_str(test, device):
|
|
24
|
-
assert wp.context.type_str(List[int]) == "List[int]"
|
|
25
|
-
assert wp.context.type_str(List[float]) == "List[float]"
|
|
26
|
-
|
|
27
|
-
assert wp.context.type_str(Tuple[int]) == "Tuple[int]"
|
|
28
|
-
assert wp.context.type_str(Tuple[float]) == "Tuple[float]"
|
|
29
|
-
assert wp.context.type_str(Tuple[int, float]) == "Tuple[int, float]"
|
|
30
|
-
assert wp.context.type_str(Tuple[int, ...]) == "Tuple[int, ...]"
|
|
31
20
|
|
|
32
21
|
|
|
33
22
|
class TestContext(unittest.TestCase):
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
def test_context_type_str(self):
|
|
24
|
+
self.assertEqual(wp.context.type_str(List[int]), "List[int]")
|
|
25
|
+
self.assertEqual(wp.context.type_str(List[float]), "List[float]")
|
|
26
|
+
|
|
27
|
+
self.assertEqual(wp.context.type_str(Tuple[int]), "Tuple[int]")
|
|
28
|
+
self.assertEqual(wp.context.type_str(Tuple[float]), "Tuple[float]")
|
|
29
|
+
self.assertEqual(wp.context.type_str(Tuple[int, float]), "Tuple[int, float]")
|
|
30
|
+
self.assertEqual(wp.context.type_str(Tuple[int, ...]), "Tuple[int, ...]")
|
|
38
31
|
|
|
39
32
|
|
|
40
33
|
if __name__ == "__main__":
|