warp-lang 1.3.1__py3-none-win_amd64.whl → 1.3.2__py3-none-win_amd64.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/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, dir, lower, upper):
49
- l1 = (lower[0] - start[0]) * dir[0]
50
- l2 = (upper[0] - start[0]) * dir[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]) * dir[1]
55
- l2 = (upper[1] - start[1]) * dir[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]) * dir[2]
60
- l2 = (upper[2] - start[2]) * dir[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()
@@ -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)
@@ -250,7 +250,11 @@ add_example_test(
250
250
  test_options_cpu={"num_frames": 10},
251
251
  )
252
252
  add_example_test(
253
- TestOptimExamples, name="optim.example_cloth_throw", devices=test_devices, test_options_cpu={"train_iters": 3}
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)
@@ -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
- input_output_mask=[("a", "out1"), ("b", "out2")],
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 built-in types, e.g.: :class:`warp.mat33`, if dtype is Any and data an ndarray then it will be inferred from the array data type
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 = tuple(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}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: warp-lang
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: A Python framework for high-performance simulation and graphics programming
5
5
  Author-email: NVIDIA Corporation <mmacklin@nvidia.com>
6
6
  License: NVIDIA Software License
@@ -19,16 +19,16 @@ Description-Content-Type: text/markdown
19
19
  License-File: LICENSE.md
20
20
  Requires-Dist: numpy
21
21
  Provides-Extra: dev
22
- Requires-Dist: pre-commit ; extra == 'dev'
23
- Requires-Dist: ruff ; extra == 'dev'
24
- Requires-Dist: nvtx ; extra == 'dev'
25
- Requires-Dist: furo ; extra == 'dev'
26
- Requires-Dist: sphinx-copybutton ; extra == 'dev'
27
- Requires-Dist: coverage[toml] ; extra == 'dev'
22
+ Requires-Dist: pre-commit; extra == "dev"
23
+ Requires-Dist: ruff; extra == "dev"
24
+ Requires-Dist: nvtx; extra == "dev"
25
+ Requires-Dist: furo; extra == "dev"
26
+ Requires-Dist: sphinx-copybutton; extra == "dev"
27
+ Requires-Dist: coverage[toml]; extra == "dev"
28
28
  Provides-Extra: extras
29
- Requires-Dist: usd-core ; extra == 'extras'
30
- Requires-Dist: matplotlib ; extra == 'extras'
31
- Requires-Dist: pyglet ; extra == 'extras'
29
+ Requires-Dist: usd-core; extra == "extras"
30
+ Requires-Dist: matplotlib; extra == "extras"
31
+ Requires-Dist: pyglet; extra == "extras"
32
32
 
33
33
  [![PyPI version](https://badge.fury.io/py/warp-lang.svg)](https://badge.fury.io/py/warp-lang)
34
34
  ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/NVIDIA/warp?link=https%3A%2F%2Fgithub.com%2FNVIDIA%2Fwarp%2Fcommits%2Fmain)
@@ -77,9 +77,9 @@ the `pip install` command, e.g.
77
77
 
78
78
  | Platform | Install Command |
79
79
  | --------------- | ----------------------------------------------------------------------------------------------------------------------------- |
80
- | Linux aarch64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.1/warp_lang-1.3.1+cu11-py3-none-manylinux2014_aarch64.whl` |
81
- | Linux x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.1/warp_lang-1.3.1+cu11-py3-none-manylinux2014_x86_64.whl` |
82
- | Windows x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.1/warp_lang-1.3.1+cu11-py3-none-win_amd64.whl` |
80
+ | Linux aarch64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.2/warp_lang-1.3.2+cu11-py3-none-manylinux2014_aarch64.whl` |
81
+ | Linux x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.2/warp_lang-1.3.2+cu11-py3-none-manylinux2014_x86_64.whl` |
82
+ | Windows x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.2/warp_lang-1.3.2+cu11-py3-none-win_amd64.whl` |
83
83
 
84
84
  The `--force-reinstall` option may need to be used to overwrite a previous installation.
85
85
 
@@ -361,10 +361,10 @@ This ensures that subsequent modifications to the library will be reflected in t
361
361
  Please see the following resources for additional background on Warp:
362
362
 
363
363
  * [Product Page](https://developer.nvidia.com/warp-python)
364
+ * [GTC 2024 Presentation](https://www.nvidia.com/en-us/on-demand/session/gtc24-s63345/)
364
365
  * [GTC 2022 Presentation](https://www.nvidia.com/en-us/on-demand/session/gtcspring22-s41599)
365
366
  * [GTC 2021 Presentation](https://www.nvidia.com/en-us/on-demand/session/gtcspring21-s31838)
366
367
  * [SIGGRAPH Asia 2021 Differentiable Simulation Course](https://dl.acm.org/doi/abs/10.1145/3476117.3483433)
367
- * [GTC 2024 Presentation](https://www.nvidia.com/en-us/on-demand/session/gtc24-s63345/)
368
368
 
369
369
  The underlying technology in Warp has been used in a number of research projects at NVIDIA including the following publications:
370
370