warp-lang 1.2.2__py3-none-manylinux2014_x86_64.whl → 1.3.1__py3-none-manylinux2014_x86_64.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.

Files changed (193) hide show
  1. warp/__init__.py +8 -6
  2. warp/autograd.py +823 -0
  3. warp/bin/warp.so +0 -0
  4. warp/build.py +6 -2
  5. warp/builtins.py +1412 -888
  6. warp/codegen.py +503 -166
  7. warp/config.py +48 -18
  8. warp/context.py +400 -198
  9. warp/dlpack.py +8 -0
  10. warp/examples/assets/bunny.usd +0 -0
  11. warp/examples/benchmarks/benchmark_cloth_warp.py +1 -1
  12. warp/examples/benchmarks/benchmark_interop_torch.py +158 -0
  13. warp/examples/benchmarks/benchmark_launches.py +1 -1
  14. warp/examples/core/example_cupy.py +78 -0
  15. warp/examples/fem/example_apic_fluid.py +17 -36
  16. warp/examples/fem/example_burgers.py +9 -18
  17. warp/examples/fem/example_convection_diffusion.py +7 -17
  18. warp/examples/fem/example_convection_diffusion_dg.py +27 -47
  19. warp/examples/fem/example_deformed_geometry.py +11 -22
  20. warp/examples/fem/example_diffusion.py +7 -18
  21. warp/examples/fem/example_diffusion_3d.py +24 -28
  22. warp/examples/fem/example_diffusion_mgpu.py +7 -14
  23. warp/examples/fem/example_magnetostatics.py +190 -0
  24. warp/examples/fem/example_mixed_elasticity.py +111 -80
  25. warp/examples/fem/example_navier_stokes.py +30 -34
  26. warp/examples/fem/example_nonconforming_contact.py +290 -0
  27. warp/examples/fem/example_stokes.py +17 -32
  28. warp/examples/fem/example_stokes_transfer.py +12 -21
  29. warp/examples/fem/example_streamlines.py +350 -0
  30. warp/examples/fem/utils.py +936 -0
  31. warp/fabric.py +5 -2
  32. warp/fem/__init__.py +13 -3
  33. warp/fem/cache.py +161 -11
  34. warp/fem/dirichlet.py +37 -28
  35. warp/fem/domain.py +105 -14
  36. warp/fem/field/__init__.py +14 -3
  37. warp/fem/field/field.py +454 -11
  38. warp/fem/field/nodal_field.py +33 -18
  39. warp/fem/geometry/deformed_geometry.py +50 -15
  40. warp/fem/geometry/hexmesh.py +12 -24
  41. warp/fem/geometry/nanogrid.py +106 -31
  42. warp/fem/geometry/quadmesh_2d.py +6 -11
  43. warp/fem/geometry/tetmesh.py +103 -61
  44. warp/fem/geometry/trimesh_2d.py +98 -47
  45. warp/fem/integrate.py +231 -186
  46. warp/fem/operator.py +14 -9
  47. warp/fem/quadrature/pic_quadrature.py +35 -9
  48. warp/fem/quadrature/quadrature.py +119 -32
  49. warp/fem/space/basis_space.py +98 -22
  50. warp/fem/space/collocated_function_space.py +3 -1
  51. warp/fem/space/function_space.py +7 -2
  52. warp/fem/space/grid_2d_function_space.py +3 -3
  53. warp/fem/space/grid_3d_function_space.py +4 -4
  54. warp/fem/space/hexmesh_function_space.py +3 -2
  55. warp/fem/space/nanogrid_function_space.py +12 -14
  56. warp/fem/space/partition.py +45 -47
  57. warp/fem/space/restriction.py +19 -16
  58. warp/fem/space/shape/cube_shape_function.py +91 -3
  59. warp/fem/space/shape/shape_function.py +7 -0
  60. warp/fem/space/shape/square_shape_function.py +32 -0
  61. warp/fem/space/shape/tet_shape_function.py +11 -7
  62. warp/fem/space/shape/triangle_shape_function.py +10 -1
  63. warp/fem/space/topology.py +116 -42
  64. warp/fem/types.py +8 -1
  65. warp/fem/utils.py +301 -83
  66. warp/native/array.h +16 -0
  67. warp/native/builtin.h +0 -15
  68. warp/native/cuda_util.cpp +14 -6
  69. warp/native/exports.h +1348 -1308
  70. warp/native/quat.h +79 -0
  71. warp/native/rand.h +27 -4
  72. warp/native/sparse.cpp +83 -81
  73. warp/native/sparse.cu +381 -453
  74. warp/native/vec.h +64 -0
  75. warp/native/volume.cpp +40 -49
  76. warp/native/volume_builder.cu +2 -3
  77. warp/native/volume_builder.h +12 -17
  78. warp/native/warp.cu +3 -3
  79. warp/native/warp.h +69 -59
  80. warp/render/render_opengl.py +17 -9
  81. warp/sim/articulation.py +117 -17
  82. warp/sim/collide.py +35 -29
  83. warp/sim/model.py +123 -18
  84. warp/sim/render.py +3 -1
  85. warp/sparse.py +867 -203
  86. warp/stubs.py +312 -541
  87. warp/tape.py +29 -1
  88. warp/tests/disabled_kinematics.py +1 -1
  89. warp/tests/test_adam.py +1 -1
  90. warp/tests/test_arithmetic.py +1 -1
  91. warp/tests/test_array.py +58 -1
  92. warp/tests/test_array_reduce.py +1 -1
  93. warp/tests/test_async.py +1 -1
  94. warp/tests/test_atomic.py +1 -1
  95. warp/tests/test_bool.py +1 -1
  96. warp/tests/test_builtins_resolution.py +1 -1
  97. warp/tests/test_bvh.py +6 -1
  98. warp/tests/test_closest_point_edge_edge.py +1 -1
  99. warp/tests/test_codegen.py +91 -1
  100. warp/tests/test_compile_consts.py +1 -1
  101. warp/tests/test_conditional.py +1 -1
  102. warp/tests/test_copy.py +1 -1
  103. warp/tests/test_ctypes.py +1 -1
  104. warp/tests/test_dense.py +1 -1
  105. warp/tests/test_devices.py +1 -1
  106. warp/tests/test_dlpack.py +1 -1
  107. warp/tests/test_examples.py +33 -4
  108. warp/tests/test_fabricarray.py +5 -2
  109. warp/tests/test_fast_math.py +1 -1
  110. warp/tests/test_fem.py +213 -6
  111. warp/tests/test_fp16.py +1 -1
  112. warp/tests/test_func.py +1 -1
  113. warp/tests/test_future_annotations.py +90 -0
  114. warp/tests/test_generics.py +1 -1
  115. warp/tests/test_grad.py +1 -1
  116. warp/tests/test_grad_customs.py +1 -1
  117. warp/tests/test_grad_debug.py +247 -0
  118. warp/tests/test_hash_grid.py +6 -1
  119. warp/tests/test_implicit_init.py +354 -0
  120. warp/tests/test_import.py +1 -1
  121. warp/tests/test_indexedarray.py +1 -1
  122. warp/tests/test_intersect.py +1 -1
  123. warp/tests/test_jax.py +1 -1
  124. warp/tests/test_large.py +1 -1
  125. warp/tests/test_launch.py +1 -1
  126. warp/tests/test_lerp.py +1 -1
  127. warp/tests/test_linear_solvers.py +1 -1
  128. warp/tests/test_lvalue.py +1 -1
  129. warp/tests/test_marching_cubes.py +5 -2
  130. warp/tests/test_mat.py +34 -35
  131. warp/tests/test_mat_lite.py +2 -1
  132. warp/tests/test_mat_scalar_ops.py +1 -1
  133. warp/tests/test_math.py +1 -1
  134. warp/tests/test_matmul.py +20 -16
  135. warp/tests/test_matmul_lite.py +1 -1
  136. warp/tests/test_mempool.py +1 -1
  137. warp/tests/test_mesh.py +5 -2
  138. warp/tests/test_mesh_query_aabb.py +1 -1
  139. warp/tests/test_mesh_query_point.py +1 -1
  140. warp/tests/test_mesh_query_ray.py +1 -1
  141. warp/tests/test_mlp.py +1 -1
  142. warp/tests/test_model.py +1 -1
  143. warp/tests/test_module_hashing.py +77 -1
  144. warp/tests/test_modules_lite.py +1 -1
  145. warp/tests/test_multigpu.py +1 -1
  146. warp/tests/test_noise.py +1 -1
  147. warp/tests/test_operators.py +1 -1
  148. warp/tests/test_options.py +1 -1
  149. warp/tests/test_overwrite.py +542 -0
  150. warp/tests/test_peer.py +1 -1
  151. warp/tests/test_pinned.py +1 -1
  152. warp/tests/test_print.py +1 -1
  153. warp/tests/test_quat.py +15 -1
  154. warp/tests/test_rand.py +1 -1
  155. warp/tests/test_reload.py +1 -1
  156. warp/tests/test_rounding.py +1 -1
  157. warp/tests/test_runlength_encode.py +1 -1
  158. warp/tests/test_scalar_ops.py +95 -0
  159. warp/tests/test_sim_grad.py +1 -1
  160. warp/tests/test_sim_kinematics.py +1 -1
  161. warp/tests/test_smoothstep.py +1 -1
  162. warp/tests/test_sparse.py +82 -15
  163. warp/tests/test_spatial.py +1 -1
  164. warp/tests/test_special_values.py +2 -11
  165. warp/tests/test_streams.py +11 -1
  166. warp/tests/test_struct.py +1 -1
  167. warp/tests/test_tape.py +1 -1
  168. warp/tests/test_torch.py +194 -1
  169. warp/tests/test_transient_module.py +1 -1
  170. warp/tests/test_types.py +1 -1
  171. warp/tests/test_utils.py +1 -1
  172. warp/tests/test_vec.py +15 -63
  173. warp/tests/test_vec_lite.py +2 -1
  174. warp/tests/test_vec_scalar_ops.py +65 -1
  175. warp/tests/test_verify_fp.py +1 -1
  176. warp/tests/test_volume.py +28 -2
  177. warp/tests/test_volume_write.py +1 -1
  178. warp/tests/unittest_serial.py +1 -1
  179. warp/tests/unittest_suites.py +9 -1
  180. warp/tests/walkthrough_debug.py +1 -1
  181. warp/thirdparty/unittest_parallel.py +2 -5
  182. warp/torch.py +103 -41
  183. warp/types.py +341 -224
  184. warp/utils.py +11 -2
  185. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/METADATA +99 -46
  186. warp_lang-1.3.1.dist-info/RECORD +368 -0
  187. warp/examples/fem/bsr_utils.py +0 -378
  188. warp/examples/fem/mesh_utils.py +0 -133
  189. warp/examples/fem/plot_utils.py +0 -292
  190. warp_lang-1.2.2.dist-info/RECORD +0 -359
  191. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/LICENSE.md +0 -0
  192. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/WHEEL +0 -0
  193. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/top_level.txt +0 -0
warp/tests/test_fem.py CHANGED
@@ -18,7 +18,14 @@ from warp.fem.geometry import DeformedGeometry
18
18
  from warp.fem.geometry.closest_point import project_on_tet_at_origin, project_on_tri_at_origin
19
19
  from warp.fem.space import shape
20
20
  from warp.fem.types import make_free_sample
21
- from warp.fem.utils import grid_to_hexes, grid_to_quads, grid_to_tets, grid_to_tris
21
+ from warp.fem.utils import (
22
+ grid_to_hexes,
23
+ grid_to_quads,
24
+ grid_to_tets,
25
+ grid_to_tris,
26
+ inverse_qr,
27
+ symmetric_eigenvalues_qr,
28
+ )
22
29
  from warp.tests.unittest_utils import *
23
30
 
24
31
 
@@ -872,7 +879,7 @@ def test_shape_function_weight(test, shape: shape.ShapeFunction, coord_sampler,
872
879
  def node_unity_test():
873
880
  n = wp.tid()
874
881
  node_w = weight_fn(node_coords_fn(n), n)
875
- wp.expect_near(node_w, 1.0, places=5)
882
+ wp.expect_near(node_w, 1.0, 1e-5)
876
883
 
877
884
  wp.launch(node_unity_test, dim=NODE_COUNT, inputs=[])
878
885
 
@@ -1201,6 +1208,34 @@ def test_point_basis(test, device):
1201
1208
 
1202
1209
  test.assertAlmostEqual(np.sum(zeros.numpy()), 0.0, places=5)
1203
1210
 
1211
+ # test point basis with variable points per cell
1212
+ points = wp.array([[0.25, 0.33], [0.33, 0.25], [0.8, 0.8]], dtype=wp.vec2)
1213
+ pic = fem.PicQuadrature(domain, positions=points)
1214
+
1215
+ test.assertEqual(pic.active_cell_count(), 2)
1216
+ test.assertEqual(pic.total_point_count(), 3)
1217
+ test.assertEqual(pic.max_points_per_element(), 2)
1218
+
1219
+ point_basis = fem.PointBasisSpace(pic)
1220
+ point_space = fem.make_collocated_function_space(point_basis)
1221
+ point_test = fem.make_test(point_space, domain=domain)
1222
+ test.assertEqual(point_test.space_restriction.node_count(), 3)
1223
+
1224
+ ones = fem.integrate(linear_form, fields={"u": point_test}, quadrature=pic)
1225
+ test.assertAlmostEqual(np.sum(ones.numpy()), pic.active_cell_count() / geo.cell_count(), places=5)
1226
+
1227
+ zeros = fem.integrate(linear_form, quadrature=other_quadrature, fields={"u": point_test})
1228
+ test.assertAlmostEqual(np.sum(zeros.numpy()), 0.0, places=5)
1229
+
1230
+ linear_vec = fem.make_polynomial_space(geo, dtype=wp.vec2)
1231
+ linear_test = fem.make_test(linear_vec)
1232
+ point_trial = fem.make_trial(point_space)
1233
+
1234
+ mat = fem.integrate(vector_divergence_form, fields={"u": linear_test, "q": point_trial}, quadrature=pic)
1235
+ test.assertEqual(mat.nrow, 9)
1236
+ test.assertEqual(mat.ncol, 3)
1237
+ test.assertEqual(mat.nnz_sync(), 12)
1238
+
1204
1239
 
1205
1240
  @fem.integrand
1206
1241
  def _bicubic(s: Sample, domain: Domain):
@@ -1228,7 +1263,7 @@ def test_particle_quadratures(test, device):
1228
1263
 
1229
1264
  explicit_quadrature = fem.ExplicitQuadrature(domain, points, weights)
1230
1265
 
1231
- test.assertEqual(explicit_quadrature.points_per_element(), points_per_cell)
1266
+ test.assertEqual(explicit_quadrature.max_points_per_element(), points_per_cell)
1232
1267
  test.assertEqual(explicit_quadrature.total_point_count(), points_per_cell * geo.cell_count())
1233
1268
 
1234
1269
  val = fem.integrate(_bicubic, quadrature=explicit_quadrature)
@@ -1247,13 +1282,182 @@ def test_particle_quadratures(test, device):
1247
1282
 
1248
1283
  pic_quadrature = fem.PicQuadrature(domain, positions=(element_indices, element_coords))
1249
1284
 
1250
- test.assertIsNone(pic_quadrature.points_per_element())
1285
+ test.assertEqual(pic_quadrature.max_points_per_element(), 2)
1251
1286
  test.assertEqual(pic_quadrature.total_point_count(), 3)
1252
1287
  test.assertEqual(pic_quadrature.active_cell_count(), 2)
1253
1288
 
1254
1289
  val = fem.integrate(_piecewise_constant, quadrature=pic_quadrature)
1255
1290
  test.assertAlmostEqual(val, 1.25, places=5)
1256
1291
 
1292
+ # Test differentiability of PicQuadrature w.r.t positions and measures
1293
+ points = wp.array([[0.25, 0.33], [0.33, 0.25], [0.8, 0.8]], dtype=wp.vec2, device=device, requires_grad=True)
1294
+ measures = wp.ones(3, dtype=float, device=device, requires_grad=True)
1295
+
1296
+ tape = wp.Tape()
1297
+ with tape:
1298
+ pic = fem.PicQuadrature(domain, positions=points, measures=measures, requires_grad=True)
1299
+
1300
+ pic.arg_value(device).particle_coords.grad.fill_(1.0)
1301
+ pic.arg_value(device).particle_fraction.grad.fill_(1.0)
1302
+ tape.backward()
1303
+
1304
+ assert_np_equal(points.grad.numpy(), np.full((3, 2), 2.0)) # == 1.0 / cell_size
1305
+ assert_np_equal(measures.grad.numpy(), np.full(3, 4.0)) # == 1.0 / cell_area
1306
+
1307
+
1308
+ @wp.func
1309
+ def aniso_bicubic_fn(x: wp.vec2, scale: wp.vec2):
1310
+ return wp.pow(x[0] * scale[0], 3.0) * wp.pow(x[1] * scale[1], 3.0)
1311
+
1312
+
1313
+ @wp.func
1314
+ def aniso_bicubic_grad(x: wp.vec2, scale: wp.vec2):
1315
+ return wp.vec2(
1316
+ 3.0 * scale[0] * wp.pow(x[0] * scale[0], 2.0) * wp.pow(x[1] * scale[1], 3.0),
1317
+ 3.0 * scale[1] * wp.pow(x[0] * scale[0], 3.0) * wp.pow(x[1] * scale[1], 2.0),
1318
+ )
1319
+
1320
+
1321
+ def test_implicit_fields(test, device):
1322
+ geo = fem.Grid2D(res=wp.vec2i(2))
1323
+ domain = fem.Cells(geo)
1324
+ boundary = fem.BoundarySides(geo)
1325
+
1326
+ space = fem.make_polynomial_space(geo)
1327
+ vec_space = fem.make_polynomial_space(geo, dtype=wp.vec2)
1328
+ discrete_field = fem.make_discrete_field(space)
1329
+ discrete_vec_field = fem.make_discrete_field(vec_space)
1330
+
1331
+ # Uniform
1332
+
1333
+ uniform = fem.UniformField(domain, 5.0)
1334
+ fem.interpolate(uniform, dest=discrete_field)
1335
+ assert_np_equal(discrete_field.dof_values.numpy(), np.full(9, 5.0))
1336
+
1337
+ fem.interpolate(grad_field, fields={"p": uniform}, dest=discrete_vec_field)
1338
+ assert_np_equal(discrete_vec_field.dof_values.numpy(), np.zeros((9, 2)))
1339
+
1340
+ uniform.value = 2.0
1341
+ fem.interpolate(uniform.trace(), dest=fem.make_restriction(discrete_field, domain=boundary))
1342
+ assert_np_equal(discrete_field.dof_values.numpy(), np.array([2.0] * 4 + [5.0] + [2.0] * 4))
1343
+
1344
+ # Implicit
1345
+
1346
+ implicit = fem.ImplicitField(
1347
+ domain, func=aniso_bicubic_fn, values={"scale": wp.vec2(2.0, 4.0)}, grad_func=aniso_bicubic_grad
1348
+ )
1349
+ fem.interpolate(implicit, dest=discrete_field)
1350
+ assert_np_equal(
1351
+ discrete_field.dof_values.numpy(),
1352
+ np.array([0.0, 0.0, 0.0, 0.0, 2.0**3, 4.0**3, 0.0, 2.0**3 * 2.0**3, 4.0**3 * 2.0**3]),
1353
+ )
1354
+
1355
+ fem.interpolate(grad_field, fields={"p": implicit}, dest=discrete_vec_field)
1356
+ assert_np_equal(discrete_vec_field.dof_values.numpy()[0], np.zeros(2))
1357
+ assert_np_equal(discrete_vec_field.dof_values.numpy()[-1], np.full(2, (2.0**9.0 * 3.0)))
1358
+
1359
+ implicit.values.scale = wp.vec2(-2.0, -2.0)
1360
+ fem.interpolate(implicit.trace(), dest=fem.make_restriction(discrete_field, domain=boundary))
1361
+ assert_np_equal(
1362
+ discrete_field.dof_values.numpy(),
1363
+ np.array([0.0, 0.0, 0.0, 0.0, 2.0**3, 2.0**3, 0.0, 2.0**3, 4.0**3]),
1364
+ )
1365
+
1366
+ # Nonconforming
1367
+
1368
+ geo2 = fem.Grid2D(res=wp.vec2i(1), bounds_lo=wp.vec2(0.25, 0.25), bounds_hi=wp.vec2(2.0, 2.0))
1369
+ domain2 = fem.Cells(geo2)
1370
+ boundary2 = fem.BoundarySides(geo2)
1371
+ space2 = fem.make_polynomial_space(geo2)
1372
+ vec_space2 = fem.make_polynomial_space(geo2, dtype=wp.vec2)
1373
+ discrete_field2 = fem.make_discrete_field(space2)
1374
+ discrete_vec_field2 = fem.make_discrete_field(vec_space2)
1375
+
1376
+ nonconforming = fem.NonconformingField(domain2, discrete_field, background=5.0)
1377
+ fem.interpolate(
1378
+ nonconforming,
1379
+ dest=discrete_field2,
1380
+ )
1381
+ assert_np_equal(discrete_field2.dof_values.numpy(), np.array([2.0] + [5.0] * 3))
1382
+
1383
+ fem.interpolate(grad_field, fields={"p": nonconforming}, dest=discrete_vec_field2)
1384
+ assert_np_equal(discrete_vec_field2.dof_values.numpy()[0], np.full(2, 8.0))
1385
+ assert_np_equal(discrete_vec_field2.dof_values.numpy()[-1], np.zeros(2))
1386
+
1387
+ discrete_field2.dof_values.zero_()
1388
+ fem.interpolate(
1389
+ nonconforming.trace(),
1390
+ dest=fem.make_restriction(discrete_field2, domain=boundary2),
1391
+ )
1392
+ assert_np_equal(discrete_field2.dof_values.numpy(), np.array([2.0] + [5.0] * 3))
1393
+
1394
+
1395
+ @wp.kernel
1396
+ def test_qr_eigenvalues():
1397
+ tol = 1.0e-6
1398
+
1399
+ # zero
1400
+ Zero = wp.mat33(0.0)
1401
+ Id = wp.identity(n=3, dtype=float)
1402
+ D3, P3 = symmetric_eigenvalues_qr(Zero, tol * tol)
1403
+ wp.expect_eq(D3, wp.vec3(0.0))
1404
+ wp.expect_eq(P3, Id)
1405
+
1406
+ # Identity
1407
+ D3, P3 = symmetric_eigenvalues_qr(Id, tol * tol)
1408
+ wp.expect_eq(D3, wp.vec3(1.0))
1409
+ wp.expect_eq(wp.transpose(P3) * P3, Id)
1410
+
1411
+ # rank 1
1412
+ v = wp.vec4(0.0, 1.0, 1.0, 0.0)
1413
+ Rank1 = wp.outer(v, v)
1414
+ D4, P4 = symmetric_eigenvalues_qr(Rank1, tol * tol)
1415
+ wp.expect_near(wp.max(D4), wp.length_sq(v), tol)
1416
+ Err4 = wp.transpose(P4) * wp.diag(D4) * P4 - Rank1
1417
+ wp.expect_near(wp.ddot(Err4, Err4), 0.0, tol)
1418
+
1419
+ # rank 2
1420
+ v2 = wp.vec4(0.0, 0.5, -0.5, 0.0)
1421
+ Rank2 = Rank1 + wp.outer(v2, v2)
1422
+ D4, P4 = symmetric_eigenvalues_qr(Rank2, tol * tol)
1423
+ wp.expect_near(wp.max(D4), wp.length_sq(v), tol)
1424
+ wp.expect_near(D4[0] + D4[1] + D4[2] + D4[3], wp.length_sq(v) + wp.length_sq(v2), tol)
1425
+ Err4 = wp.transpose(P4) * wp.diag(D4) * P4 - Rank2
1426
+ wp.expect_near(wp.ddot(Err4, Err4), 0.0, tol)
1427
+
1428
+ # rank 4
1429
+ v3 = wp.vec4(1.0, 2.0, 3.0, 4.0)
1430
+ v4 = wp.vec4(2.0, 1.0, 0.0, -1.0)
1431
+ Rank4 = Rank2 + wp.outer(v3, v3) + wp.outer(v4, v4)
1432
+ D4, P4 = symmetric_eigenvalues_qr(Rank4, tol * tol)
1433
+ Err4 = wp.transpose(P4) * wp.diag(D4) * P4 - Rank4
1434
+ wp.expect_near(wp.ddot(Err4, Err4), 0.0, tol)
1435
+
1436
+
1437
+ @wp.kernel
1438
+ def test_qr_inverse():
1439
+ rng = wp.rand_init(4356, wp.tid())
1440
+ M = wp.mat33(
1441
+ wp.randf(rng, 0.0, 10.0),
1442
+ wp.randf(rng, 0.0, 10.0),
1443
+ wp.randf(rng, 0.0, 10.0),
1444
+ wp.randf(rng, 0.0, 10.0),
1445
+ wp.randf(rng, 0.0, 10.0),
1446
+ wp.randf(rng, 0.0, 10.0),
1447
+ wp.randf(rng, 0.0, 10.0),
1448
+ wp.randf(rng, 0.0, 10.0),
1449
+ wp.randf(rng, 0.0, 10.0),
1450
+ )
1451
+
1452
+ if wp.determinant(M) != 0.0:
1453
+ tol = 1.0e-8
1454
+ Mi = inverse_qr(M)
1455
+ Id = wp.identity(n=3, dtype=float)
1456
+ Err = M * Mi - Id
1457
+ wp.expect_near(wp.ddot(Err, Err), 0.0, tol)
1458
+ Err = Mi * M - Id
1459
+ wp.expect_near(wp.ddot(Err, Err), 0.0, tol)
1460
+
1257
1461
 
1258
1462
  devices = get_test_devices()
1259
1463
  cuda_devices = get_selected_cuda_test_devices()
@@ -1281,6 +1485,9 @@ add_function_test(TestFem, "test_deformed_geometry", test_deformed_geometry, dev
1281
1485
  add_function_test(TestFem, "test_dof_mapper", test_dof_mapper)
1282
1486
  add_function_test(TestFem, "test_point_basis", test_point_basis)
1283
1487
  add_function_test(TestFem, "test_particle_quadratures", test_particle_quadratures)
1488
+ add_function_test(TestFem, "test_implicit_fields", test_implicit_fields)
1489
+ add_kernel_test(TestFem, test_qr_eigenvalues, dim=1, devices=devices)
1490
+ add_kernel_test(TestFem, test_qr_inverse, dim=100, devices=devices)
1284
1491
 
1285
1492
 
1286
1493
  class TestFemShapeFunctions(unittest.TestCase):
@@ -1294,5 +1501,5 @@ add_function_test(TestFemShapeFunctions, "test_tet_shape_functions", test_tet_sh
1294
1501
 
1295
1502
 
1296
1503
  if __name__ == "__main__":
1297
- wp.build.clear_kernel_cache()
1298
- unittest.main(verbosity=2)
1504
+ wp.clear_kernel_cache()
1505
+ unittest.main(verbosity=2, failfast=True)
warp/tests/test_fp16.py CHANGED
@@ -124,5 +124,5 @@ add_function_test(TestFp16, "test_fp16_kernel_parameter", test_fp16_kernel_param
124
124
 
125
125
 
126
126
  if __name__ == "__main__":
127
- wp.build.clear_kernel_cache()
127
+ wp.clear_kernel_cache()
128
128
  unittest.main(verbosity=2)
warp/tests/test_func.py CHANGED
@@ -332,5 +332,5 @@ add_kernel_test(TestFunc, kernel=test_builtin_shadowing, name="test_builtin_shad
332
332
 
333
333
 
334
334
  if __name__ == "__main__":
335
- wp.build.clear_kernel_cache()
335
+ wp.clear_kernel_cache()
336
336
  unittest.main(verbosity=2)
@@ -0,0 +1,90 @@
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
+ # This is what we are actually testing.
9
+ from __future__ import annotations
10
+
11
+ import unittest
12
+
13
+ import warp as wp
14
+ from warp.tests.unittest_utils import *
15
+
16
+
17
+ @wp.struct
18
+ class FooData:
19
+ x: float
20
+ y: float
21
+
22
+
23
+ class Foo:
24
+ Data = FooData
25
+
26
+ @wp.func
27
+ def compute():
28
+ pass
29
+
30
+
31
+ @wp.kernel
32
+ def kernel_1(
33
+ out: wp.array(dtype=float),
34
+ ):
35
+ tid = wp.tid()
36
+
37
+
38
+ @wp.kernel
39
+ def kernel_2(
40
+ out: wp.array(dtype=float),
41
+ ):
42
+ tid = wp.tid()
43
+ out[tid] = 1.23
44
+
45
+
46
+ def create_kernel_3(foo: Foo):
47
+ def fn(
48
+ data: foo.Data,
49
+ out: wp.array(dtype=float),
50
+ ):
51
+ tid = wp.tid()
52
+
53
+ # Referencing a variable in a type hint like `foo.Data` isn't officially
54
+ # accepted by Python but it's still being used in some places (e.g.: `warp.fem`)
55
+ # where it works only because the variable being referenced within the function,
56
+ # which causes it to be promoted to a closure variable. Without that,
57
+ # it wouldn't be possible to resolve `foo` and to evaluate the `foo.Data`
58
+ # string to its corresponding type.
59
+ foo.compute()
60
+
61
+ out[tid] = data.x + data.y
62
+
63
+ return wp.Kernel(func=fn)
64
+
65
+
66
+ def test_future_annotations(test, device):
67
+ foo = Foo()
68
+ foo_data = FooData()
69
+ foo_data.x = 1.23
70
+ foo_data.y = 2.34
71
+
72
+ out = wp.empty(1, dtype=float)
73
+
74
+ kernel_3 = create_kernel_3(foo)
75
+
76
+ wp.launch(kernel_1, dim=out.shape, outputs=(out,))
77
+ wp.launch(kernel_2, dim=out.shape, outputs=(out,))
78
+ wp.launch(kernel_3, dim=out.shape, inputs=(foo_data,), outputs=(out,))
79
+
80
+
81
+ class TestFutureAnnotations(unittest.TestCase):
82
+ pass
83
+
84
+
85
+ add_function_test(TestFutureAnnotations, "test_future_annotations", test_future_annotations)
86
+
87
+
88
+ if __name__ == "__main__":
89
+ wp.clear_kernel_cache()
90
+ unittest.main(verbosity=2)
@@ -592,5 +592,5 @@ add_function_test(TestGenerics, "test_type_operator_misspell", test_type_operato
592
592
  add_function_test(TestGenerics, "test_type_attribute_error", test_type_attribute_error, devices=devices)
593
593
 
594
594
  if __name__ == "__main__":
595
- wp.build.clear_kernel_cache()
595
+ wp.clear_kernel_cache()
596
596
  unittest.main(verbosity=2)
warp/tests/test_grad.py CHANGED
@@ -881,5 +881,5 @@ add_function_test(TestGrad, "test_gradient_slice_3d_2d", test_gradient_slice_3d_
881
881
 
882
882
 
883
883
  if __name__ == "__main__":
884
- wp.build.clear_kernel_cache()
884
+ wp.clear_kernel_cache()
885
885
  unittest.main(verbosity=2, failfast=False)
@@ -327,5 +327,5 @@ add_function_test(TestGradCustoms, "test_custom_grad_no_return", test_custom_gra
327
327
 
328
328
 
329
329
  if __name__ == "__main__":
330
- wp.build.clear_kernel_cache()
330
+ wp.clear_kernel_cache()
331
331
  unittest.main(verbosity=2, failfast=False)
@@ -0,0 +1,247 @@
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
+ import warp as wp
11
+ from warp.autograd import gradcheck, gradcheck_tape, jacobian, jacobian_fd
12
+ from warp.tests.unittest_utils import *
13
+
14
+
15
+ @wp.kernel
16
+ def kernel_3d(
17
+ a: wp.array(dtype=float, ndim=3),
18
+ b: wp.array(dtype=float, ndim=3),
19
+ c: wp.array(dtype=float, ndim=3),
20
+ out1: wp.array(dtype=float, ndim=3),
21
+ out2: wp.array(dtype=float, ndim=3),
22
+ ):
23
+ i, j, k = wp.tid()
24
+ out1[i, j, k] = a[i, j, k] * b[i, j, k] + c[i, j, k]
25
+ out2[i, j, k] = -a[i, j, k] * b[i, j, k] - c[i, j, k]
26
+
27
+
28
+ @wp.kernel
29
+ def kernel_mixed(
30
+ a: wp.array(dtype=float),
31
+ b: wp.array(dtype=wp.vec3),
32
+ out1: wp.array(dtype=wp.vec2),
33
+ out2: wp.array(dtype=wp.quat),
34
+ ):
35
+ tid = wp.tid()
36
+ ai, bi = a[tid], b[tid]
37
+ out1[tid] = wp.vec2(ai * wp.length(bi), -ai * wp.dot(bi, wp.vec3(0.1, 1.0, -0.1)))
38
+ out2[tid] = wp.normalize(wp.quat(ai, bi[0], bi[1], bi[2]))
39
+
40
+
41
+ @wp.kernel
42
+ def vec_length_kernel(a: wp.array(dtype=wp.vec3), out: wp.array(dtype=float)):
43
+ tid = wp.tid()
44
+ v = a[tid]
45
+ # instead of wp.length(v), we use a trivial implementation that
46
+ # fails when a division by zero is occurs in the backward pass of sqrt
47
+ out[tid] = wp.sqrt(v[0] ** 2.0 + v[1] ** 2.0 + v[2] ** 2.0)
48
+
49
+
50
+ @wp.func
51
+ def wrong_grad_func(x: float):
52
+ return x * x
53
+
54
+
55
+ @wp.func_grad(wrong_grad_func)
56
+ def adj_wrong_grad_func(x: float, adj: float):
57
+ wp.adjoint[x] -= 2.0 * x * adj
58
+
59
+
60
+ @wp.kernel
61
+ def wrong_grad_kernel(a: wp.array(dtype=float), out: wp.array(dtype=float)):
62
+ tid = wp.tid()
63
+ out[tid] = wrong_grad_func(a[tid])
64
+
65
+
66
+ def test_gradcheck_3d(test, device):
67
+ a_3d = wp.array([((2.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
68
+ b_3d = wp.array([((3.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
69
+ c_3d = wp.array([((4.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
70
+
71
+ out1_3d = wp.array([((3.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
72
+ out2_3d = wp.array([((4.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
73
+
74
+ jacs_ad = jacobian(
75
+ kernel_3d,
76
+ dim=a_3d.shape,
77
+ inputs=[a_3d, b_3d, c_3d],
78
+ outputs=[out1_3d, out2_3d],
79
+ max_outputs_per_var=4,
80
+ input_output_mask=[("a", "out1"), ("b", "out2")],
81
+ )
82
+
83
+ assert sorted(jacs_ad.keys()) == [(0, 0), (1, 1)]
84
+ assert jacs_ad[(0, 0)].shape == (6, 6)
85
+ assert jacs_ad[(1, 1)].shape == (6, 6)
86
+ # all entries beyond the max_outputs_per_var are NaN
87
+ assert np.all(np.isnan(jacs_ad[(0, 0)].numpy()[4:]))
88
+ assert np.all(np.isnan(jacs_ad[(1, 1)].numpy()[4:]))
89
+
90
+ jacs_fd = jacobian_fd(
91
+ kernel_3d,
92
+ dim=a_3d.shape,
93
+ inputs=[a_3d, b_3d, c_3d],
94
+ outputs=[out1_3d, out2_3d],
95
+ max_inputs_per_var=4,
96
+ input_output_mask=[("a", "out1"), ("b", "out2")],
97
+ eps=1e-4,
98
+ )
99
+
100
+ assert sorted(jacs_fd.keys()) == [(0, 0), (1, 1)]
101
+ assert jacs_fd[(0, 0)].shape == (6, 6)
102
+ assert jacs_fd[(1, 1)].shape == (6, 6)
103
+ # all entries beyond the max_inputs_per_var are NaN
104
+ assert np.all(np.isnan(jacs_fd[(0, 0)].numpy()[:, 4:]))
105
+ assert np.all(np.isnan(jacs_fd[(1, 1)].numpy()[:, 4:]))
106
+
107
+ # manual gradcheck
108
+ assert np.allclose(jacs_ad[(0, 0)].numpy()[:4, :4], jacs_fd[(0, 0)].numpy()[:4, :4], atol=1e-2, rtol=1e-2)
109
+ assert np.allclose(jacs_ad[(1, 1)].numpy()[:4, :4], jacs_fd[(1, 1)].numpy()[:4, :4], atol=1e-2, rtol=1e-2)
110
+
111
+ passed = gradcheck(
112
+ kernel_3d,
113
+ dim=a_3d.shape,
114
+ inputs=[a_3d, b_3d, c_3d],
115
+ outputs=[out1_3d, out2_3d],
116
+ max_inputs_per_var=4,
117
+ max_outputs_per_var=4,
118
+ input_output_mask=[("a", "out1"), ("b", "out2")],
119
+ show_summary=False,
120
+ )
121
+ assert passed
122
+
123
+
124
+ def test_gradcheck_mixed(test, device):
125
+ a = wp.array([2.0, -1.0], dtype=wp.float32, requires_grad=True, device=device)
126
+ b = wp.array([wp.vec3(3.0, 1.0, 2.0), wp.vec3(-4.0, -1.0, 0.0)], dtype=wp.vec3, requires_grad=True, device=device)
127
+ out1 = wp.zeros(2, dtype=wp.vec2, requires_grad=True, device=device)
128
+ out2 = wp.zeros(2, dtype=wp.quat, requires_grad=True, device=device)
129
+
130
+ jacs_ad = jacobian(
131
+ kernel_mixed,
132
+ dim=len(a),
133
+ inputs=[a, b],
134
+ outputs=[out1, out2],
135
+ )
136
+ jacs_fd = jacobian_fd(
137
+ kernel_mixed,
138
+ dim=len(a),
139
+ inputs=[a, b],
140
+ outputs=[out1, out2],
141
+ eps=1e-4,
142
+ )
143
+
144
+ # manual gradcheck
145
+ for i in range(2):
146
+ for j in range(2):
147
+ assert np.allclose(jacs_ad[(i, j)].numpy(), jacs_fd[(i, j)].numpy(), atol=1e-2, rtol=1e-2)
148
+
149
+ passed = gradcheck(
150
+ kernel_mixed,
151
+ dim=len(a),
152
+ inputs=[a, b],
153
+ outputs=[out1, out2],
154
+ raise_exception=False,
155
+ show_summary=False,
156
+ )
157
+
158
+ assert passed
159
+
160
+
161
+ def test_gradcheck_nan(test, device):
162
+ a = wp.array([wp.vec3(1.0, 2.0, 3.0), wp.vec3(0.0, 0.0, 0.0)], dtype=wp.vec3, requires_grad=True, device=device)
163
+ out = wp.array([0.0, 0.0], dtype=float, requires_grad=True, device=device)
164
+
165
+ with test.assertRaises(ValueError):
166
+ gradcheck(
167
+ vec_length_kernel,
168
+ dim=a.shape,
169
+ inputs=[a],
170
+ outputs=[out],
171
+ raise_exception=True,
172
+ show_summary=False,
173
+ )
174
+
175
+
176
+ def test_gradcheck_incorrect(test, device):
177
+ a = wp.array([1.0, 2.0, 3.0], dtype=wp.float32, requires_grad=True, device=device)
178
+ out = wp.zeros_like(a)
179
+
180
+ with test.assertRaises(ValueError):
181
+ gradcheck(
182
+ wrong_grad_kernel,
183
+ dim=a.shape,
184
+ inputs=[a],
185
+ outputs=[out],
186
+ raise_exception=True,
187
+ show_summary=False,
188
+ )
189
+
190
+
191
+ def test_gradcheck_tape(test, device):
192
+ a = wp.array([2.0, -1.0], dtype=wp.float32, requires_grad=True, device=device)
193
+ b = wp.array([wp.vec3(3.0, 1.0, 2.0), wp.vec3(-4.0, -1.0, 0.0)], dtype=wp.vec3, requires_grad=True, device=device)
194
+ out1 = wp.zeros(2, dtype=wp.vec2, requires_grad=True, device=device)
195
+ out2 = wp.zeros(2, dtype=wp.quat, requires_grad=True, device=device)
196
+
197
+ a_3d = wp.array([((2.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
198
+ b_3d = wp.array([((3.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
199
+ c_3d = wp.array([((4.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
200
+
201
+ out1_3d = wp.array([((3.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
202
+ out2_3d = wp.array([((4.0, 0.0), (1.0, 0.0), (2.0, 0.0))], dtype=float, requires_grad=True, device=device)
203
+
204
+ tape = wp.Tape()
205
+ with tape:
206
+ wp.launch(
207
+ kernel_mixed,
208
+ dim=len(a),
209
+ inputs=[a, b],
210
+ outputs=[out1, out2],
211
+ device=device,
212
+ )
213
+
214
+ wp.launch(
215
+ kernel_3d,
216
+ dim=a_3d.shape,
217
+ inputs=[a_3d, b_3d, c_3d],
218
+ outputs=[out1_3d, out2_3d],
219
+ device=device,
220
+ )
221
+
222
+ passed = gradcheck_tape(
223
+ tape,
224
+ raise_exception=False,
225
+ show_summary=False,
226
+ )
227
+
228
+ assert passed
229
+
230
+
231
+ devices = get_test_devices()
232
+
233
+
234
+ class TestGradDebug(unittest.TestCase):
235
+ pass
236
+
237
+
238
+ add_function_test(TestGradDebug, "test_gradcheck_3d", test_gradcheck_3d, devices=devices)
239
+ add_function_test(TestGradDebug, "test_gradcheck_mixed", test_gradcheck_mixed, devices=devices)
240
+ add_function_test(TestGradDebug, "test_gradcheck_nan", test_gradcheck_nan, devices=devices)
241
+ add_function_test(TestGradDebug, "test_gradcheck_incorrect", test_gradcheck_incorrect, devices=devices)
242
+ add_function_test(TestGradDebug, "test_gradcheck_tape", test_gradcheck_tape, devices=devices)
243
+
244
+
245
+ if __name__ == "__main__":
246
+ wp.build.clear_kernel_cache()
247
+ unittest.main(verbosity=2, failfast=False)
@@ -198,11 +198,16 @@ class TestHashGrid(unittest.TestCase):
198
198
 
199
199
  wp.Kernel(func=kernel_fn)
200
200
 
201
+ def test_hashgrid_new_del(self):
202
+ # test the scenario in which a hashgrid is created but not initialized before gc
203
+ instance = wp.HashGrid.__new__(wp.HashGrid)
204
+ instance.__del__()
205
+
201
206
 
202
207
  add_function_test(TestHashGrid, "test_hashgrid_query", test_hashgrid_query, devices=devices)
203
208
  add_function_test(TestHashGrid, "test_hashgrid_inputs", test_hashgrid_inputs, devices=devices)
204
209
 
205
210
 
206
211
  if __name__ == "__main__":
207
- wp.build.clear_kernel_cache()
212
+ wp.clear_kernel_cache()
208
213
  unittest.main(verbosity=2, failfast=False)