warp-lang 1.3.0__py3-none-macosx_10_13_universal2.whl → 1.3.2__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/autograd.py +6 -6
- warp/bin/libwarp-clang.dylib +0 -0
- warp/bin/libwarp.dylib +0 -0
- warp/builtins.py +46 -43
- warp/codegen.py +27 -38
- warp/config.py +1 -1
- warp/context.py +160 -111
- warp/examples/fem/example_mixed_elasticity.py +33 -23
- warp/fem/field/nodal_field.py +1 -1
- warp/fem/quadrature/quadrature.py +1 -0
- warp/native/builtin.h +3 -3
- warp/native/bvh.h +1 -1
- warp/native/svd.h +22 -7
- warp/native/warp.cpp +1 -0
- warp/native/warp.cu +5 -0
- warp/native/warp.h +1 -0
- warp/sim/collide.py +1 -1
- warp/sim/model.py +16 -3
- warp/sim/utils.py +1 -1
- warp/stubs.py +112 -112
- warp/tape.py +3 -3
- warp/tests/test_array.py +11 -0
- warp/tests/test_async.py +3 -1
- warp/tests/test_bvh.py +33 -8
- warp/tests/test_codegen.py +25 -0
- warp/tests/test_compile_consts.py +15 -0
- warp/tests/test_examples.py +6 -1
- warp/tests/test_fem.py +51 -0
- warp/tests/test_grad_debug.py +2 -1
- warp/tests/test_model.py +55 -0
- warp/tests/test_point_triangle_closest_point.py +143 -0
- warp/tests/test_reload.py +28 -0
- warp/tests/test_struct.py +48 -30
- warp/types.py +4 -2
- {warp_lang-1.3.0.dist-info → warp_lang-1.3.2.dist-info}/METADATA +14 -14
- {warp_lang-1.3.0.dist-info → warp_lang-1.3.2.dist-info}/RECORD +39 -38
- {warp_lang-1.3.0.dist-info → warp_lang-1.3.2.dist-info}/WHEEL +1 -1
- {warp_lang-1.3.0.dist-info → warp_lang-1.3.2.dist-info}/LICENSE.md +0 -0
- {warp_lang-1.3.0.dist-info → warp_lang-1.3.2.dist-info}/top_level.txt +0 -0
warp/tests/test_bvh.py
CHANGED
|
@@ -45,19 +45,19 @@ def aabb_overlap(a_lower, a_upper, b_lower, b_upper):
|
|
|
45
45
|
return 1
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def intersect_ray_aabb(start,
|
|
49
|
-
l1 = (lower[0] - start[0]) *
|
|
50
|
-
l2 = (upper[0] - start[0]) *
|
|
48
|
+
def intersect_ray_aabb(start, rcp_dir, lower, upper):
|
|
49
|
+
l1 = (lower[0] - start[0]) * rcp_dir[0]
|
|
50
|
+
l2 = (upper[0] - start[0]) * rcp_dir[0]
|
|
51
51
|
lmin = min(l1, l2)
|
|
52
52
|
lmax = max(l1, l2)
|
|
53
53
|
|
|
54
|
-
l1 = (lower[1] - start[1]) *
|
|
55
|
-
l2 = (upper[1] - start[1]) *
|
|
54
|
+
l1 = (lower[1] - start[1]) * rcp_dir[1]
|
|
55
|
+
l2 = (upper[1] - start[1]) * rcp_dir[1]
|
|
56
56
|
lmin = max(min(l1, l2), lmin)
|
|
57
57
|
lmax = min(max(l1, l2), lmax)
|
|
58
58
|
|
|
59
|
-
l1 = (lower[2] - start[2]) *
|
|
60
|
-
l2 = (upper[2] - start[2]) *
|
|
59
|
+
l1 = (lower[2] - start[2]) * rcp_dir[2]
|
|
60
|
+
l2 = (upper[2] - start[2]) * rcp_dir[2]
|
|
61
61
|
lmin = max(min(l1, l2), lmin)
|
|
62
62
|
lmax = min(max(l1, l2), lmax)
|
|
63
63
|
|
|
@@ -108,7 +108,7 @@ def test_bvh(test, type, device):
|
|
|
108
108
|
if type == "AABB":
|
|
109
109
|
host_intersected = aabb_overlap(lower, upper, query_lower, query_upper)
|
|
110
110
|
else:
|
|
111
|
-
host_intersected = intersect_ray_aabb(query_start, query_dir, lower, upper)
|
|
111
|
+
host_intersected = intersect_ray_aabb(query_start, 1.0 / query_dir, lower, upper)
|
|
112
112
|
|
|
113
113
|
test.assertEqual(host_intersected, device_intersected[i])
|
|
114
114
|
|
|
@@ -129,6 +129,30 @@ def test_bvh_query_ray(test, device):
|
|
|
129
129
|
test_bvh(test, "ray", device)
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
def test_gh_288(test, device):
|
|
133
|
+
num_bounds = 1
|
|
134
|
+
lowers = ((0.5, -1.0, -1.0),) * num_bounds
|
|
135
|
+
uppers = ((1.0, 1.0, 1.0),) * num_bounds
|
|
136
|
+
|
|
137
|
+
device_lowers = wp.array(lowers, dtype=wp.vec3f, device=device)
|
|
138
|
+
device_uppers = wp.array(uppers, dtype=wp.vec3f, device=device)
|
|
139
|
+
|
|
140
|
+
bvh = wp.Bvh(device_lowers, device_uppers)
|
|
141
|
+
|
|
142
|
+
bounds_intersected = wp.zeros(shape=num_bounds, dtype=int, device=device)
|
|
143
|
+
|
|
144
|
+
for x in (0.0, 0.75):
|
|
145
|
+
query_start = wp.vec3(x, 0.0, 0.0)
|
|
146
|
+
query_dir = wp.vec3(1.0, 0.0, 0.0)
|
|
147
|
+
|
|
148
|
+
wp.launch(
|
|
149
|
+
kernel=bvh_query_ray, dim=1, inputs=[bvh.id, query_start, query_dir, bounds_intersected], device=device
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
device_intersected = bounds_intersected.numpy()
|
|
153
|
+
test.assertEqual(device_intersected.sum(), num_bounds)
|
|
154
|
+
|
|
155
|
+
|
|
132
156
|
devices = get_test_devices()
|
|
133
157
|
|
|
134
158
|
|
|
@@ -161,6 +185,7 @@ class TestBvh(unittest.TestCase):
|
|
|
161
185
|
|
|
162
186
|
add_function_test(TestBvh, "test_bvh_aabb", test_bvh_query_aabb, devices=devices)
|
|
163
187
|
add_function_test(TestBvh, "test_bvh_ray", test_bvh_query_ray, devices=devices)
|
|
188
|
+
add_function_test(TestBvh, "test_gh_288", test_gh_288, devices=devices)
|
|
164
189
|
|
|
165
190
|
if __name__ == "__main__":
|
|
166
191
|
wp.clear_kernel_cache()
|
warp/tests/test_codegen.py
CHANGED
|
@@ -467,6 +467,28 @@ def test_error_collection_construct(test, device):
|
|
|
467
467
|
wp.launch(kernel, dim=1)
|
|
468
468
|
|
|
469
469
|
|
|
470
|
+
def test_error_unmatched_arguments(test, device):
|
|
471
|
+
def kernel_1_fn():
|
|
472
|
+
a = 1 * 1.0
|
|
473
|
+
|
|
474
|
+
def kernel_2_fn():
|
|
475
|
+
x = wp.dot(wp.vec2(1.0, 2.0), wp.vec2h(wp.float16(1.0), wp.float16(2.0)))
|
|
476
|
+
|
|
477
|
+
kernel = wp.Kernel(func=kernel_1_fn)
|
|
478
|
+
with test.assertRaisesRegex(
|
|
479
|
+
RuntimeError,
|
|
480
|
+
r"Input types must be the same, got \['int32', 'float32'\]",
|
|
481
|
+
):
|
|
482
|
+
wp.launch(kernel, dim=1)
|
|
483
|
+
|
|
484
|
+
kernel = wp.Kernel(func=kernel_2_fn)
|
|
485
|
+
with test.assertRaisesRegex(
|
|
486
|
+
RuntimeError,
|
|
487
|
+
r"Input types must be exactly the same, got \[\"vector\(length=2, dtype=<class 'warp.types.float32'>\)\", \"vector\(length=2, dtype=<class 'warp.types.float16'>\)\"\]",
|
|
488
|
+
):
|
|
489
|
+
wp.launch(kernel, dim=1)
|
|
490
|
+
|
|
491
|
+
|
|
470
492
|
@wp.kernel
|
|
471
493
|
def test_call_syntax():
|
|
472
494
|
expected_pow = 16.0
|
|
@@ -618,6 +640,9 @@ add_function_test(TestCodeGen, func=test_error_global_var, name="test_error_glob
|
|
|
618
640
|
add_function_test(
|
|
619
641
|
TestCodeGen, func=test_error_collection_construct, name="test_error_collection_construct", devices=devices
|
|
620
642
|
)
|
|
643
|
+
add_function_test(
|
|
644
|
+
TestCodeGen, func=test_error_unmatched_arguments, name="test_error_unmatched_arguments", devices=devices
|
|
645
|
+
)
|
|
621
646
|
|
|
622
647
|
add_kernel_test(TestCodeGen, name="test_call_syntax", kernel=test_call_syntax, dim=1, devices=devices)
|
|
623
648
|
|
|
@@ -18,6 +18,8 @@ UNIT_VEC = wp.constant(wp.vec3(SQRT3_OVER_3, SQRT3_OVER_3, SQRT3_OVER_3))
|
|
|
18
18
|
ONE_FP16 = wp.constant(wp.float16(1.0))
|
|
19
19
|
TEST_BOOL = wp.constant(True)
|
|
20
20
|
|
|
21
|
+
SHADOWED_GLOBAL = wp.constant(17)
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class Foobar:
|
|
23
25
|
ONE = wp.constant(1)
|
|
@@ -68,6 +70,18 @@ def test_closure_capture(test, device):
|
|
|
68
70
|
wp.launch(two_closure, dim=(1), inputs=[2], device=device)
|
|
69
71
|
|
|
70
72
|
|
|
73
|
+
def test_closure_precedence(test, device):
|
|
74
|
+
"""Verifies that closure constants take precedence over globals"""
|
|
75
|
+
|
|
76
|
+
SHADOWED_GLOBAL = wp.constant(42)
|
|
77
|
+
|
|
78
|
+
@wp.kernel
|
|
79
|
+
def closure_kernel():
|
|
80
|
+
wp.expect_eq(SHADOWED_GLOBAL, 42)
|
|
81
|
+
|
|
82
|
+
wp.launch(closure_kernel, dim=1, device=device)
|
|
83
|
+
|
|
84
|
+
|
|
71
85
|
def test_hash_global_capture(test, device):
|
|
72
86
|
"""Verifies that global variables are included in the module hash"""
|
|
73
87
|
|
|
@@ -206,6 +220,7 @@ add_kernel_test(TestConstants, test_int, dim=1, inputs=[a], devices=devices)
|
|
|
206
220
|
add_kernel_test(TestConstants, test_float, dim=1, inputs=[x], devices=devices)
|
|
207
221
|
|
|
208
222
|
add_function_test(TestConstants, "test_closure_capture", test_closure_capture, devices=devices)
|
|
223
|
+
add_function_test(TestConstants, "test_closure_precedence", test_closure_precedence, devices=devices)
|
|
209
224
|
add_function_test(TestConstants, "test_hash_global_capture", test_hash_global_capture, devices=devices)
|
|
210
225
|
add_function_test(TestConstants, "test_hash_redefine_kernel", test_hash_redefine_kernel, devices=devices)
|
|
211
226
|
add_function_test(TestConstants, "test_hash_redefine_constant_only", test_hash_redefine_constant_only, devices=devices)
|
warp/tests/test_examples.py
CHANGED
|
@@ -250,7 +250,11 @@ add_example_test(
|
|
|
250
250
|
test_options_cpu={"num_frames": 10},
|
|
251
251
|
)
|
|
252
252
|
add_example_test(
|
|
253
|
-
TestOptimExamples,
|
|
253
|
+
TestOptimExamples,
|
|
254
|
+
name="optim.example_cloth_throw",
|
|
255
|
+
devices=test_devices,
|
|
256
|
+
test_options={"test_timeout": 600},
|
|
257
|
+
test_options_cpu={"train_iters": 3},
|
|
254
258
|
)
|
|
255
259
|
add_example_test(
|
|
256
260
|
TestOptimExamples,
|
|
@@ -390,6 +394,7 @@ add_example_test(
|
|
|
390
394
|
name="fem.example_mixed_elasticity",
|
|
391
395
|
devices=test_devices,
|
|
392
396
|
test_options={"nonconforming_stresses": True, "mesh": "quad", "headless": True},
|
|
397
|
+
test_options_cpu={"test_timeout": 600},
|
|
393
398
|
)
|
|
394
399
|
add_example_test(
|
|
395
400
|
TestFemExamples, name="fem.example_stokes_transfer", devices=test_devices, test_options={"headless": True}
|
warp/tests/test_fem.py
CHANGED
|
@@ -1252,6 +1252,8 @@ def test_particle_quadratures(test, device):
|
|
|
1252
1252
|
geo = fem.Grid2D(res=wp.vec2i(2))
|
|
1253
1253
|
|
|
1254
1254
|
domain = fem.Cells(geo)
|
|
1255
|
+
|
|
1256
|
+
# Explicit quadrature
|
|
1255
1257
|
points, weights = domain.reference_element().instantiate_quadrature(order=4, family=fem.Polynomial.GAUSS_LEGENDRE)
|
|
1256
1258
|
points_per_cell = len(points)
|
|
1257
1259
|
|
|
@@ -1266,9 +1268,16 @@ def test_particle_quadratures(test, device):
|
|
|
1266
1268
|
test.assertEqual(explicit_quadrature.max_points_per_element(), points_per_cell)
|
|
1267
1269
|
test.assertEqual(explicit_quadrature.total_point_count(), points_per_cell * geo.cell_count())
|
|
1268
1270
|
|
|
1271
|
+
# test integration accuracy
|
|
1269
1272
|
val = fem.integrate(_bicubic, quadrature=explicit_quadrature)
|
|
1270
1273
|
test.assertAlmostEqual(val, 1.0 / 16, places=5)
|
|
1271
1274
|
|
|
1275
|
+
# test indexing validity
|
|
1276
|
+
arr = wp.empty(explicit_quadrature.total_point_count(), dtype=float)
|
|
1277
|
+
fem.interpolate(_piecewise_constant, dest=arr, quadrature=explicit_quadrature)
|
|
1278
|
+
assert_np_equal(arr.numpy(), np.arange(geo.cell_count()).repeat(points_per_cell))
|
|
1279
|
+
|
|
1280
|
+
# PIC quadrature
|
|
1272
1281
|
element_indices = wp.array([3, 3, 2], dtype=int, device=device)
|
|
1273
1282
|
element_coords = wp.array(
|
|
1274
1283
|
[
|
|
@@ -1286,6 +1295,7 @@ def test_particle_quadratures(test, device):
|
|
|
1286
1295
|
test.assertEqual(pic_quadrature.total_point_count(), 3)
|
|
1287
1296
|
test.assertEqual(pic_quadrature.active_cell_count(), 2)
|
|
1288
1297
|
|
|
1298
|
+
# Test integration accuracy
|
|
1289
1299
|
val = fem.integrate(_piecewise_constant, quadrature=pic_quadrature)
|
|
1290
1300
|
test.assertAlmostEqual(val, 1.25, places=5)
|
|
1291
1301
|
|
|
@@ -1305,6 +1315,46 @@ def test_particle_quadratures(test, device):
|
|
|
1305
1315
|
assert_np_equal(measures.grad.numpy(), np.full(3, 4.0)) # == 1.0 / cell_area
|
|
1306
1316
|
|
|
1307
1317
|
|
|
1318
|
+
@fem.integrand
|
|
1319
|
+
def _value_at_node(s: fem.Sample, f: fem.Field, values: wp.array(dtype=float)):
|
|
1320
|
+
node_index = fem.operator.node_partition_index(f, s.qp_index)
|
|
1321
|
+
return values[node_index]
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
def test_nodal_quadrature(test, device):
|
|
1325
|
+
geo = fem.Grid2D(res=wp.vec2i(2))
|
|
1326
|
+
|
|
1327
|
+
domain = fem.Cells(geo)
|
|
1328
|
+
|
|
1329
|
+
space = fem.make_polynomial_space(geo, degree=2, discontinuous=True, family=fem.Polynomial.GAUSS_LEGENDRE)
|
|
1330
|
+
nodal_quadrature = fem.NodalQuadrature(domain, space)
|
|
1331
|
+
|
|
1332
|
+
test.assertEqual(nodal_quadrature.max_points_per_element(), 9)
|
|
1333
|
+
test.assertEqual(nodal_quadrature.total_point_count(), 9 * geo.cell_count())
|
|
1334
|
+
|
|
1335
|
+
val = fem.integrate(_bicubic, quadrature=nodal_quadrature)
|
|
1336
|
+
test.assertAlmostEqual(val, 1.0 / 16, places=5)
|
|
1337
|
+
|
|
1338
|
+
# test accessing data associated to a given node
|
|
1339
|
+
|
|
1340
|
+
piecewise_constant_space = fem.make_polynomial_space(geo, degree=0)
|
|
1341
|
+
geo_partition = fem.LinearGeometryPartition(geo, 3, 4)
|
|
1342
|
+
space_partition = fem.make_space_partition(piecewise_constant_space, geo_partition)
|
|
1343
|
+
field = fem.make_discrete_field(piecewise_constant_space, space_partition=space_partition)
|
|
1344
|
+
|
|
1345
|
+
partition_domain = fem.Cells(geo_partition)
|
|
1346
|
+
partition_nodal_quadrature = fem.NodalQuadrature(partition_domain, piecewise_constant_space)
|
|
1347
|
+
|
|
1348
|
+
partition_node_values = wp.array([5.0], dtype=float)
|
|
1349
|
+
val = fem.integrate(
|
|
1350
|
+
_value_at_node,
|
|
1351
|
+
quadrature=partition_nodal_quadrature,
|
|
1352
|
+
fields={"f": field},
|
|
1353
|
+
values={"values": partition_node_values},
|
|
1354
|
+
)
|
|
1355
|
+
test.assertAlmostEqual(val, 5.0 / geo.cell_count(), places=5)
|
|
1356
|
+
|
|
1357
|
+
|
|
1308
1358
|
@wp.func
|
|
1309
1359
|
def aniso_bicubic_fn(x: wp.vec2, scale: wp.vec2):
|
|
1310
1360
|
return wp.pow(x[0] * scale[0], 3.0) * wp.pow(x[1] * scale[1], 3.0)
|
|
@@ -1485,6 +1535,7 @@ add_function_test(TestFem, "test_deformed_geometry", test_deformed_geometry, dev
|
|
|
1485
1535
|
add_function_test(TestFem, "test_dof_mapper", test_dof_mapper)
|
|
1486
1536
|
add_function_test(TestFem, "test_point_basis", test_point_basis)
|
|
1487
1537
|
add_function_test(TestFem, "test_particle_quadratures", test_particle_quadratures)
|
|
1538
|
+
add_function_test(TestFem, "test_nodal_quadrature", test_nodal_quadrature)
|
|
1488
1539
|
add_function_test(TestFem, "test_implicit_fields", test_implicit_fields)
|
|
1489
1540
|
add_kernel_test(TestFem, test_qr_eigenvalues, dim=1, devices=devices)
|
|
1490
1541
|
add_kernel_test(TestFem, test_qr_inverse, dim=100, devices=devices)
|
warp/tests/test_grad_debug.py
CHANGED
|
@@ -93,7 +93,8 @@ def test_gradcheck_3d(test, device):
|
|
|
93
93
|
inputs=[a_3d, b_3d, c_3d],
|
|
94
94
|
outputs=[out1_3d, out2_3d],
|
|
95
95
|
max_inputs_per_var=4,
|
|
96
|
-
|
|
96
|
+
# use integer indices instead of variable names
|
|
97
|
+
input_output_mask=[(0, 0), (1, 1)],
|
|
97
98
|
eps=1e-4,
|
|
98
99
|
)
|
|
99
100
|
|
warp/tests/test_model.py
CHANGED
|
@@ -102,6 +102,61 @@ class TestModel(unittest.TestCase):
|
|
|
102
102
|
assert_np_equal(np.array(builder1.edge_rest_angle), np.array(builder2.edge_rest_angle), tol=1.0e-4)
|
|
103
103
|
assert_np_equal(np.array(builder1.edge_bending_properties), np.array(builder2.edge_bending_properties))
|
|
104
104
|
|
|
105
|
+
def test_collapse_fixed_joints(self):
|
|
106
|
+
def add_three_cubes(builder: ModelBuilder, parent_body=-1):
|
|
107
|
+
unit_cube = {"hx": 0.5, "hy": 0.5, "hz": 0.5, "density": 1.0}
|
|
108
|
+
b0 = builder.add_body()
|
|
109
|
+
builder.add_shape_box(body=b0, **unit_cube)
|
|
110
|
+
builder.add_joint_fixed(parent=parent_body, child=b0, parent_xform=wp.transform(wp.vec3(1.0, 0.0, 0.0)))
|
|
111
|
+
b1 = builder.add_body()
|
|
112
|
+
builder.add_shape_box(body=b1, **unit_cube)
|
|
113
|
+
builder.add_joint_fixed(parent=parent_body, child=b1, parent_xform=wp.transform(wp.vec3(0.0, 1.0, 0.0)))
|
|
114
|
+
b2 = builder.add_body()
|
|
115
|
+
builder.add_shape_box(body=b2, **unit_cube)
|
|
116
|
+
builder.add_joint_fixed(parent=parent_body, child=b2, parent_xform=wp.transform(wp.vec3(0.0, 0.0, 1.0)))
|
|
117
|
+
return b2
|
|
118
|
+
|
|
119
|
+
builder = ModelBuilder()
|
|
120
|
+
# only fixed joints
|
|
121
|
+
builder.add_articulation()
|
|
122
|
+
add_three_cubes(builder)
|
|
123
|
+
assert builder.joint_count == 3
|
|
124
|
+
assert builder.body_count == 3
|
|
125
|
+
|
|
126
|
+
# fixed joints followed by a non-fixed joint
|
|
127
|
+
builder.add_articulation()
|
|
128
|
+
last_body = add_three_cubes(builder)
|
|
129
|
+
assert builder.joint_count == 6
|
|
130
|
+
assert builder.body_count == 6
|
|
131
|
+
assert builder.articulation_count == 2
|
|
132
|
+
b3 = builder.add_body()
|
|
133
|
+
builder.add_shape_box(body=b3, hx=0.5, hy=0.5, hz=0.5, density=1.0, pos=wp.vec3(1.0, 2.0, 3.0))
|
|
134
|
+
builder.add_joint_revolute(parent=last_body, child=b3, axis=wp.vec3(0.0, 1.0, 0.0))
|
|
135
|
+
|
|
136
|
+
# a non-fixed joint followed by fixed joints
|
|
137
|
+
builder.add_articulation()
|
|
138
|
+
b4 = builder.add_body()
|
|
139
|
+
builder.add_shape_box(body=b4, hx=0.5, hy=0.5, hz=0.5, density=1.0)
|
|
140
|
+
builder.add_joint_free(parent=-1, child=b4, parent_xform=wp.transform(wp.vec3(0.0, -1.0, 0.0)))
|
|
141
|
+
assert builder.joint_count == 8
|
|
142
|
+
assert builder.body_count == 8
|
|
143
|
+
assert builder.articulation_count == 3
|
|
144
|
+
add_three_cubes(builder, parent_body=b4)
|
|
145
|
+
|
|
146
|
+
builder.collapse_fixed_joints()
|
|
147
|
+
|
|
148
|
+
assert builder.joint_count == 2
|
|
149
|
+
assert builder.articulation_count == 2
|
|
150
|
+
assert builder.articulation_start == [0, 1]
|
|
151
|
+
assert builder.joint_type == [wp.sim.JOINT_REVOLUTE, wp.sim.JOINT_FREE]
|
|
152
|
+
assert builder.shape_count == 11
|
|
153
|
+
assert builder.shape_body == [-1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1]
|
|
154
|
+
assert builder.body_count == 2
|
|
155
|
+
assert builder.body_com[0] == wp.vec3(1.0, 2.0, 3.0)
|
|
156
|
+
assert builder.body_com[1] == wp.vec3(0.25, 0.25, 0.25)
|
|
157
|
+
assert builder.body_mass == [1.0, 4.0]
|
|
158
|
+
assert builder.body_inv_mass == [1.0, 0.25]
|
|
159
|
+
|
|
105
160
|
|
|
106
161
|
if __name__ == "__main__":
|
|
107
162
|
wp.clear_kernel_cache()
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Copyright (c) 2024 NVIDIA CORPORATION. All rights reserved.
|
|
2
|
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
|
3
|
+
# and proprietary rights in and to this software, related documentation
|
|
4
|
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
|
5
|
+
# distribution of this software and related documentation without an express
|
|
6
|
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
|
7
|
+
|
|
8
|
+
import unittest
|
|
9
|
+
|
|
10
|
+
from warp.sim.collide import triangle_closest_point_barycentric
|
|
11
|
+
from warp.tests.unittest_utils import *
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# a-b is the edge where the closest point is located at
|
|
15
|
+
@wp.func
|
|
16
|
+
def check_edge_feasible_region(p: wp.vec3, a: wp.vec3, b: wp.vec3, c: wp.vec3, eps: float):
|
|
17
|
+
ap = p - a
|
|
18
|
+
bp = p - b
|
|
19
|
+
ab = b - a
|
|
20
|
+
|
|
21
|
+
if wp.dot(ap, ab) < -eps:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
if wp.dot(bp, ab) > eps:
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
ab_sqr_norm = wp.dot(ab, ab)
|
|
28
|
+
if ab_sqr_norm < eps:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
t = wp.dot(ab, c - a) / ab_sqr_norm
|
|
32
|
+
|
|
33
|
+
perpendicular_foot = a + t * ab
|
|
34
|
+
|
|
35
|
+
if wp.dot(c - perpendicular_foot, p - perpendicular_foot) > eps:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# closest point is a
|
|
42
|
+
@wp.func
|
|
43
|
+
def check_vertex_feasible_region(p: wp.vec3, a: wp.vec3, b: wp.vec3, c: wp.vec3, eps: float):
|
|
44
|
+
ap = p - a
|
|
45
|
+
ba = a - b
|
|
46
|
+
ca = a - c
|
|
47
|
+
|
|
48
|
+
if wp.dot(ap, ba) < -eps:
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
if wp.dot(p, ca) < -eps:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@wp.func
|
|
58
|
+
def return_true():
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_point_triangle_closest_point(test, device):
|
|
63
|
+
passed = wp.array([True], dtype=wp.bool, device=device)
|
|
64
|
+
|
|
65
|
+
@wp.kernel
|
|
66
|
+
def test_point_triangle_closest_point_kernel(tri: wp.array(dtype=wp.vec3), passed: wp.array(dtype=wp.bool)):
|
|
67
|
+
state = wp.uint32(wp.rand_init(wp.int32(123), wp.int32(0)))
|
|
68
|
+
eps = 1e-5
|
|
69
|
+
|
|
70
|
+
for _i in range(1000):
|
|
71
|
+
l = wp.float32(0.0)
|
|
72
|
+
while l < eps:
|
|
73
|
+
p = wp.vec3(wp.randn(state), wp.randn(state), wp.randn(state))
|
|
74
|
+
l = wp.length(p)
|
|
75
|
+
|
|
76
|
+
# project to a sphere with r=2
|
|
77
|
+
p = 2.0 * p / l
|
|
78
|
+
|
|
79
|
+
bary = triangle_closest_point_barycentric(tri[0], tri[1], tri[2], p)
|
|
80
|
+
|
|
81
|
+
for dim in range(3):
|
|
82
|
+
v1_index = (dim + 1) % 3
|
|
83
|
+
v2_index = (dim + 2) % 3
|
|
84
|
+
v1 = tri[v1_index]
|
|
85
|
+
v2 = tri[v2_index]
|
|
86
|
+
v3 = tri[dim]
|
|
87
|
+
|
|
88
|
+
# on edge
|
|
89
|
+
if bary[dim] == 0.0 and bary[v1_index] != 0.0 and bary[v2_index] != 0.0:
|
|
90
|
+
if not check_edge_feasible_region(p, v1, v2, v3, eps):
|
|
91
|
+
passed[0] = False
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
# p-closest_p must be perpendicular to v1-v2
|
|
95
|
+
closest_p = a * bary[0] + b * bary[1] + c * bary[2]
|
|
96
|
+
e = v1 - v2
|
|
97
|
+
err = wp.dot(e, closest_p - p)
|
|
98
|
+
if wp.abs(err) > eps:
|
|
99
|
+
passed[0] = False
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
if bary[v1_index] == 0.0 and bary[v2_index] == 0.0:
|
|
103
|
+
if not check_vertex_feasible_region(p, v3, v1, v2, eps):
|
|
104
|
+
passed[0] = False
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
if bary[dim] != 0.0 and bary[v1_index] != 0.0 and bary[v2_index] != 0.0:
|
|
108
|
+
closest_p = a * bary[0] + b * bary[1] + c * bary[2]
|
|
109
|
+
e1 = v1 - v2
|
|
110
|
+
e2 = v1 - v3
|
|
111
|
+
if wp.abs(wp.dot(e1, closest_p - p)) > eps or wp.abs(wp.dot(e2, closest_p - p)) > eps:
|
|
112
|
+
passed[0] = False
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
a = wp.vec3(1.0, 0.0, 0.0)
|
|
116
|
+
b = wp.vec3(0.0, 0.0, 0.0)
|
|
117
|
+
c = wp.vec3(0.0, 1.0, 0.0)
|
|
118
|
+
|
|
119
|
+
tri = wp.array([a, b, c], dtype=wp.vec3, device=device)
|
|
120
|
+
wp.launch(test_point_triangle_closest_point_kernel, dim=1, inputs=[tri, passed], device=device)
|
|
121
|
+
passed = passed.numpy()
|
|
122
|
+
|
|
123
|
+
test.assertTrue(passed.all())
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
devices = get_test_devices()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class TestTriangleClosestPoint(unittest.TestCase):
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
add_function_test(
|
|
134
|
+
TestTriangleClosestPoint,
|
|
135
|
+
"test_point_triangle_closest_point",
|
|
136
|
+
test_point_triangle_closest_point,
|
|
137
|
+
devices=devices,
|
|
138
|
+
check_output=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
wp.clear_kernel_cache()
|
|
143
|
+
unittest.main(verbosity=2)
|
warp/tests/test_reload.py
CHANGED
|
@@ -189,7 +189,32 @@ def test_reload_references(test, device):
|
|
|
189
189
|
test_dependent.run(expect=4.0, device=device) # 2 * 2 = 4
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
def test_graph_launch_after_module_reload(test, device):
|
|
193
|
+
@wp.kernel
|
|
194
|
+
def foo(a: wp.array(dtype=int)):
|
|
195
|
+
a[0] = 42
|
|
196
|
+
|
|
197
|
+
with wp.ScopedDevice(device):
|
|
198
|
+
a = wp.zeros(1, dtype=int)
|
|
199
|
+
|
|
200
|
+
# preload module before graph capture
|
|
201
|
+
wp.load_module(device=device)
|
|
202
|
+
|
|
203
|
+
# capture a launch
|
|
204
|
+
with wp.ScopedCapture(force_module_load=False) as capture:
|
|
205
|
+
wp.launch(foo, dim=1, inputs=[a])
|
|
206
|
+
|
|
207
|
+
# unload the module
|
|
208
|
+
foo.module.unload()
|
|
209
|
+
|
|
210
|
+
# launch previously captured graph
|
|
211
|
+
wp.capture_launch(capture.graph)
|
|
212
|
+
|
|
213
|
+
test.assertEqual(a.numpy()[0], 42)
|
|
214
|
+
|
|
215
|
+
|
|
192
216
|
devices = get_test_devices()
|
|
217
|
+
cuda_devices = get_cuda_test_devices()
|
|
193
218
|
|
|
194
219
|
|
|
195
220
|
class TestReload(unittest.TestCase):
|
|
@@ -200,6 +225,9 @@ add_function_test(TestReload, "test_redefine", test_redefine, devices=devices)
|
|
|
200
225
|
add_function_test(TestReload, "test_reload", test_reload, devices=devices)
|
|
201
226
|
add_function_test(TestReload, "test_reload_class", test_reload_class, devices=devices)
|
|
202
227
|
add_function_test(TestReload, "test_reload_references", test_reload_references, devices=devices)
|
|
228
|
+
add_function_test(
|
|
229
|
+
TestReload, "test_graph_launch_after_module_reload", test_graph_launch_after_module_reload, devices=cuda_devices
|
|
230
|
+
)
|
|
203
231
|
|
|
204
232
|
|
|
205
233
|
if __name__ == "__main__":
|
warp/tests/test_struct.py
CHANGED
|
@@ -589,6 +589,52 @@ def test_dependent_module_import(c: DependentModuleImport_C):
|
|
|
589
589
|
wp.tid() # nop, we're just testing codegen
|
|
590
590
|
|
|
591
591
|
|
|
592
|
+
def test_struct_array_content_hash(test, device):
|
|
593
|
+
# Ensure that the memory address of the struct does not affect the content hash
|
|
594
|
+
|
|
595
|
+
@wp.struct
|
|
596
|
+
class ContentHashStruct:
|
|
597
|
+
i: int
|
|
598
|
+
|
|
599
|
+
@wp.kernel
|
|
600
|
+
def dummy_kernel(a: wp.array(dtype=ContentHashStruct)):
|
|
601
|
+
i = wp.tid()
|
|
602
|
+
|
|
603
|
+
module_hash_0 = wp.get_module(dummy_kernel.__module__).hash_module()
|
|
604
|
+
|
|
605
|
+
# Redefine ContentHashStruct to have the same members as before but a new memory address
|
|
606
|
+
@wp.struct
|
|
607
|
+
class ContentHashStruct:
|
|
608
|
+
i: int
|
|
609
|
+
|
|
610
|
+
@wp.kernel
|
|
611
|
+
def dummy_kernel(a: wp.array(dtype=ContentHashStruct)):
|
|
612
|
+
i = wp.tid()
|
|
613
|
+
|
|
614
|
+
module_hash_1 = wp.get_module(dummy_kernel.__module__).hash_module(recompute_content_hash=True)
|
|
615
|
+
|
|
616
|
+
test.assertEqual(
|
|
617
|
+
module_hash_1,
|
|
618
|
+
module_hash_0,
|
|
619
|
+
"Module hash should be unchanged when ContentHashStruct is redefined but unchanged.",
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
# Redefine ContentHashStruct to have different members. This time we should get a new hash.
|
|
623
|
+
@wp.struct
|
|
624
|
+
class ContentHashStruct:
|
|
625
|
+
i: float
|
|
626
|
+
|
|
627
|
+
@wp.kernel
|
|
628
|
+
def dummy_kernel(a: wp.array(dtype=ContentHashStruct)):
|
|
629
|
+
i = wp.tid()
|
|
630
|
+
|
|
631
|
+
module_hash_2 = wp.get_module(dummy_kernel.__module__).hash_module(recompute_content_hash=True)
|
|
632
|
+
|
|
633
|
+
test.assertNotEqual(
|
|
634
|
+
module_hash_2, module_hash_0, "Module hash should be different when ContentHashStruct redefined and changed."
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
|
|
592
638
|
devices = get_test_devices()
|
|
593
639
|
|
|
594
640
|
|
|
@@ -636,36 +682,6 @@ add_kernel_test(
|
|
|
636
682
|
devices=devices,
|
|
637
683
|
)
|
|
638
684
|
|
|
639
|
-
add_kernel_test(
|
|
640
|
-
TestStruct,
|
|
641
|
-
name="test_struct_mutate_attributes",
|
|
642
|
-
kernel=test_struct_mutate_attributes_kernel,
|
|
643
|
-
dim=1,
|
|
644
|
-
inputs=[],
|
|
645
|
-
devices=devices,
|
|
646
|
-
)
|
|
647
|
-
add_kernel_test(
|
|
648
|
-
TestStruct,
|
|
649
|
-
kernel=test_uninitialized,
|
|
650
|
-
name="test_uninitialized",
|
|
651
|
-
dim=1,
|
|
652
|
-
inputs=[Uninitialized()],
|
|
653
|
-
devices=devices,
|
|
654
|
-
)
|
|
655
|
-
add_kernel_test(TestStruct, kernel=test_return, name="test_return", dim=1, inputs=[], devices=devices)
|
|
656
|
-
add_function_test(TestStruct, "test_nested_struct", test_nested_struct, devices=devices)
|
|
657
|
-
add_function_test(TestStruct, "test_nested_array_struct", test_nested_array_struct, devices=devices)
|
|
658
|
-
add_function_test(TestStruct, "test_nested_empty_struct", test_nested_empty_struct, devices=devices)
|
|
659
|
-
add_function_test(TestStruct, "test_struct_math_conversions", test_struct_math_conversions, devices=devices)
|
|
660
|
-
add_kernel_test(
|
|
661
|
-
TestStruct,
|
|
662
|
-
name="test_struct_default_attributes",
|
|
663
|
-
kernel=test_struct_default_attributes_kernel,
|
|
664
|
-
dim=1,
|
|
665
|
-
inputs=[],
|
|
666
|
-
devices=devices,
|
|
667
|
-
)
|
|
668
|
-
|
|
669
685
|
add_kernel_test(
|
|
670
686
|
TestStruct,
|
|
671
687
|
name="test_struct_mutate_attributes",
|
|
@@ -702,6 +718,8 @@ add_kernel_test(
|
|
|
702
718
|
devices=devices,
|
|
703
719
|
)
|
|
704
720
|
|
|
721
|
+
add_function_test(TestStruct, "test_struct_array_content_hash", test_struct_array_content_hash, devices=None)
|
|
722
|
+
|
|
705
723
|
|
|
706
724
|
if __name__ == "__main__":
|
|
707
725
|
wp.clear_kernel_cache()
|
warp/types.py
CHANGED
|
@@ -1576,7 +1576,7 @@ class array(Array):
|
|
|
1576
1576
|
|
|
1577
1577
|
Args:
|
|
1578
1578
|
data (Union[list, tuple, ndarray]): An object to construct the array from, can be a Tuple, List, or generally any type convertible to an np.array
|
|
1579
|
-
dtype (Union): One of the
|
|
1579
|
+
dtype (Union): One of the available `data types <#data-types>`_, such as :class:`warp.float32`, :class:`warp.mat33`, or a custom `struct <#structs>`_. If dtype is ``Any`` and data is an ndarray, then it will be inferred from the array data type
|
|
1580
1580
|
shape (tuple): Dimensions of the array
|
|
1581
1581
|
strides (tuple): Number of bytes in each dimension between successive elements of the array
|
|
1582
1582
|
length (int): Number of elements of the data type (deprecated, users should use `shape` argument)
|
|
@@ -1614,7 +1614,9 @@ class array(Array):
|
|
|
1614
1614
|
if isinstance(shape, int):
|
|
1615
1615
|
shape = (shape,)
|
|
1616
1616
|
else:
|
|
1617
|
-
shape
|
|
1617
|
+
# The type of shape's elements are eventually passed onto capacity which is used to allocate memory. We
|
|
1618
|
+
# explicitly enforce that shape is a tuple of (64-bit) ints to ensure that capacity is 64-bit.
|
|
1619
|
+
shape = tuple(int(x) for x in shape)
|
|
1618
1620
|
if len(shape) > ARRAY_MAX_DIMS:
|
|
1619
1621
|
raise RuntimeError(
|
|
1620
1622
|
f"Failed to create array with shape {shape}, the maximum number of dimensions is {ARRAY_MAX_DIMS}"
|