warp-lang 1.3.3__py3-none-manylinux2014_aarch64.whl → 1.4.1__py3-none-manylinux2014_aarch64.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 (110) hide show
  1. warp/__init__.py +6 -0
  2. warp/autograd.py +59 -6
  3. warp/bin/warp.so +0 -0
  4. warp/build_dll.py +8 -10
  5. warp/builtins.py +103 -3
  6. warp/codegen.py +447 -53
  7. warp/config.py +1 -1
  8. warp/context.py +682 -405
  9. warp/dlpack.py +2 -0
  10. warp/examples/benchmarks/benchmark_cloth.py +10 -0
  11. warp/examples/core/example_render_opengl.py +12 -10
  12. warp/examples/fem/example_adaptive_grid.py +251 -0
  13. warp/examples/fem/example_apic_fluid.py +1 -1
  14. warp/examples/fem/example_diffusion_3d.py +2 -2
  15. warp/examples/fem/example_magnetostatics.py +1 -1
  16. warp/examples/fem/example_streamlines.py +1 -0
  17. warp/examples/fem/utils.py +25 -5
  18. warp/examples/sim/example_cloth.py +50 -6
  19. warp/fem/__init__.py +2 -0
  20. warp/fem/adaptivity.py +493 -0
  21. warp/fem/field/field.py +2 -1
  22. warp/fem/field/nodal_field.py +18 -26
  23. warp/fem/field/test.py +4 -4
  24. warp/fem/field/trial.py +4 -4
  25. warp/fem/geometry/__init__.py +1 -0
  26. warp/fem/geometry/adaptive_nanogrid.py +843 -0
  27. warp/fem/geometry/nanogrid.py +55 -28
  28. warp/fem/space/__init__.py +1 -1
  29. warp/fem/space/nanogrid_function_space.py +69 -35
  30. warp/fem/utils.py +118 -107
  31. warp/jax_experimental.py +28 -15
  32. warp/native/array.h +0 -1
  33. warp/native/builtin.h +103 -6
  34. warp/native/bvh.cu +4 -2
  35. warp/native/cuda_util.cpp +14 -0
  36. warp/native/cuda_util.h +2 -0
  37. warp/native/error.cpp +4 -2
  38. warp/native/exports.h +99 -0
  39. warp/native/mat.h +97 -0
  40. warp/native/mesh.cpp +36 -0
  41. warp/native/mesh.cu +52 -1
  42. warp/native/mesh.h +1 -0
  43. warp/native/quat.h +43 -0
  44. warp/native/range.h +11 -2
  45. warp/native/spatial.h +6 -0
  46. warp/native/vec.h +74 -0
  47. warp/native/warp.cpp +2 -1
  48. warp/native/warp.cu +10 -3
  49. warp/native/warp.h +8 -1
  50. warp/paddle.py +382 -0
  51. warp/sim/__init__.py +1 -0
  52. warp/sim/collide.py +519 -0
  53. warp/sim/integrator_euler.py +18 -5
  54. warp/sim/integrator_featherstone.py +5 -5
  55. warp/sim/integrator_vbd.py +1026 -0
  56. warp/sim/integrator_xpbd.py +2 -6
  57. warp/sim/model.py +50 -25
  58. warp/sparse.py +9 -7
  59. warp/stubs.py +459 -0
  60. warp/tape.py +2 -0
  61. warp/tests/aux_test_dependent.py +1 -0
  62. warp/tests/aux_test_name_clash1.py +32 -0
  63. warp/tests/aux_test_name_clash2.py +32 -0
  64. warp/tests/aux_test_square.py +1 -0
  65. warp/tests/test_array.py +188 -0
  66. warp/tests/test_async.py +3 -3
  67. warp/tests/test_atomic.py +6 -0
  68. warp/tests/test_closest_point_edge_edge.py +93 -1
  69. warp/tests/test_codegen.py +93 -15
  70. warp/tests/test_codegen_instancing.py +1457 -0
  71. warp/tests/test_collision.py +486 -0
  72. warp/tests/test_compile_consts.py +3 -28
  73. warp/tests/test_dlpack.py +170 -0
  74. warp/tests/test_examples.py +22 -8
  75. warp/tests/test_fast_math.py +10 -4
  76. warp/tests/test_fem.py +81 -1
  77. warp/tests/test_func.py +46 -0
  78. warp/tests/test_implicit_init.py +49 -0
  79. warp/tests/test_jax.py +58 -0
  80. warp/tests/test_mat.py +84 -0
  81. warp/tests/test_mesh_query_point.py +188 -0
  82. warp/tests/test_model.py +13 -0
  83. warp/tests/test_module_hashing.py +40 -0
  84. warp/tests/test_multigpu.py +3 -3
  85. warp/tests/test_overwrite.py +8 -0
  86. warp/tests/test_paddle.py +852 -0
  87. warp/tests/test_print.py +89 -0
  88. warp/tests/test_quat.py +111 -0
  89. warp/tests/test_reload.py +31 -1
  90. warp/tests/test_scalar_ops.py +2 -0
  91. warp/tests/test_static.py +568 -0
  92. warp/tests/test_streams.py +64 -3
  93. warp/tests/test_struct.py +4 -4
  94. warp/tests/test_torch.py +24 -0
  95. warp/tests/test_triangle_closest_point.py +137 -0
  96. warp/tests/test_types.py +1 -1
  97. warp/tests/test_vbd.py +386 -0
  98. warp/tests/test_vec.py +143 -0
  99. warp/tests/test_vec_scalar_ops.py +139 -0
  100. warp/tests/unittest_suites.py +12 -0
  101. warp/tests/unittest_utils.py +9 -5
  102. warp/thirdparty/dlpack.py +3 -1
  103. warp/types.py +167 -36
  104. warp/utils.py +37 -14
  105. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/METADATA +10 -8
  106. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/RECORD +109 -97
  107. warp/tests/test_point_triangle_closest_point.py +0 -143
  108. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/LICENSE.md +0 -0
  109. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/WHEEL +0 -0
  110. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/top_level.txt +0 -0
warp/tests/test_array.py CHANGED
@@ -2361,6 +2361,173 @@ def test_array_from_cai(test, device):
2361
2361
  assert_np_equal(arr_warp.numpy(), np.array([[2, 1, 1], [1, 0, 0], [1, 0, 0]]))
2362
2362
 
2363
2363
 
2364
+ def test_array_inplace_ops(test, device):
2365
+ @wp.kernel
2366
+ def inplace_add_1d(x: wp.array(dtype=float), y: wp.array(dtype=float)):
2367
+ i = wp.tid()
2368
+ x[i] += y[i]
2369
+
2370
+ @wp.kernel
2371
+ def inplace_add_2d(x: wp.array2d(dtype=float), y: wp.array2d(dtype=float)):
2372
+ i, j = wp.tid()
2373
+ x[i, j] += y[i, j]
2374
+
2375
+ @wp.kernel
2376
+ def inplace_add_3d(x: wp.array3d(dtype=float), y: wp.array3d(dtype=float)):
2377
+ i, j, k = wp.tid()
2378
+ x[i, j, k] += y[i, j, k]
2379
+
2380
+ @wp.kernel
2381
+ def inplace_add_4d(x: wp.array4d(dtype=float), y: wp.array4d(dtype=float)):
2382
+ i, j, k, l = wp.tid()
2383
+ x[i, j, k, l] += y[i, j, k, l]
2384
+
2385
+ @wp.kernel
2386
+ def inplace_sub_1d(x: wp.array(dtype=float), y: wp.array(dtype=float)):
2387
+ i = wp.tid()
2388
+ x[i] -= y[i]
2389
+
2390
+ @wp.kernel
2391
+ def inplace_sub_2d(x: wp.array2d(dtype=float), y: wp.array2d(dtype=float)):
2392
+ i, j = wp.tid()
2393
+ x[i, j] -= y[i, j]
2394
+
2395
+ @wp.kernel
2396
+ def inplace_sub_3d(x: wp.array3d(dtype=float), y: wp.array3d(dtype=float)):
2397
+ i, j, k = wp.tid()
2398
+ x[i, j, k] -= y[i, j, k]
2399
+
2400
+ @wp.kernel
2401
+ def inplace_sub_4d(x: wp.array4d(dtype=float), y: wp.array4d(dtype=float)):
2402
+ i, j, k, l = wp.tid()
2403
+ x[i, j, k, l] -= y[i, j, k, l]
2404
+
2405
+ @wp.kernel
2406
+ def inplace_add_vecs(x: wp.array(dtype=wp.vec3), y: wp.array(dtype=wp.vec3)):
2407
+ i = wp.tid()
2408
+ x[i] += y[i]
2409
+
2410
+ @wp.kernel
2411
+ def inplace_add_mats(x: wp.array(dtype=wp.mat33), y: wp.array(dtype=wp.mat33)):
2412
+ i = wp.tid()
2413
+ x[i] += y[i]
2414
+
2415
+ @wp.kernel
2416
+ def inplace_add_rhs(x: wp.array(dtype=float), y: wp.array(dtype=float), z: wp.array(dtype=float)):
2417
+ i = wp.tid()
2418
+ a = y[i]
2419
+ a += x[i]
2420
+ wp.atomic_add(z, 0, a)
2421
+
2422
+ N = 3
2423
+ x1 = wp.ones(N, dtype=float, requires_grad=True, device=device)
2424
+ x2 = wp.ones((N, N), dtype=float, requires_grad=True, device=device)
2425
+ x3 = wp.ones((N, N, N), dtype=float, requires_grad=True, device=device)
2426
+ x4 = wp.ones((N, N, N, N), dtype=float, requires_grad=True, device=device)
2427
+
2428
+ y1 = wp.clone(x1, requires_grad=True, device=device)
2429
+ y2 = wp.clone(x2, requires_grad=True, device=device)
2430
+ y3 = wp.clone(x3, requires_grad=True, device=device)
2431
+ y4 = wp.clone(x4, requires_grad=True, device=device)
2432
+
2433
+ v1 = wp.ones(1, dtype=wp.vec3, requires_grad=True, device=device)
2434
+ v2 = wp.clone(v1, requires_grad=True, device=device)
2435
+
2436
+ m1 = wp.ones(1, dtype=wp.mat33, requires_grad=True, device=device)
2437
+ m2 = wp.clone(m1, requires_grad=True, device=device)
2438
+
2439
+ x = wp.ones(1, dtype=float, requires_grad=True, device=device)
2440
+ y = wp.clone(x, requires_grad=True, device=device)
2441
+ z = wp.zeros(1, dtype=float, requires_grad=True, device=device)
2442
+
2443
+ np_ones_1d = np.ones(N, dtype=float)
2444
+ np_ones_2d = np.ones((N, N), dtype=float)
2445
+ np_ones_3d = np.ones((N, N, N), dtype=float)
2446
+ np_ones_4d = np.ones((N, N, N, N), dtype=float)
2447
+
2448
+ np_twos_1d = np.full(N, 2.0, dtype=float)
2449
+ np_twos_2d = np.full((N, N), 2.0, dtype=float)
2450
+ np_twos_3d = np.full((N, N, N), 2.0, dtype=float)
2451
+ np_twos_4d = np.full((N, N, N, N), 2.0, dtype=float)
2452
+
2453
+ tape = wp.Tape()
2454
+ with tape:
2455
+ wp.launch(inplace_add_1d, N, inputs=[x1, y1], device=device)
2456
+ wp.launch(inplace_add_2d, (N, N), inputs=[x2, y2], device=device)
2457
+ wp.launch(inplace_add_3d, (N, N, N), inputs=[x3, y3], device=device)
2458
+ wp.launch(inplace_add_4d, (N, N, N, N), inputs=[x4, y4], device=device)
2459
+
2460
+ tape.backward(grads={x1: wp.ones_like(x1), x2: wp.ones_like(x2), x3: wp.ones_like(x3), x4: wp.ones_like(x4)})
2461
+
2462
+ assert_np_equal(x1.grad.numpy(), np_ones_1d)
2463
+ assert_np_equal(x2.grad.numpy(), np_ones_2d)
2464
+ assert_np_equal(x3.grad.numpy(), np_ones_3d)
2465
+ assert_np_equal(x4.grad.numpy(), np_ones_4d)
2466
+
2467
+ assert_np_equal(y1.grad.numpy(), np_ones_1d)
2468
+ assert_np_equal(y2.grad.numpy(), np_ones_2d)
2469
+ assert_np_equal(y3.grad.numpy(), np_ones_3d)
2470
+ assert_np_equal(y4.grad.numpy(), np_ones_4d)
2471
+
2472
+ assert_np_equal(x1.numpy(), np_twos_1d)
2473
+ assert_np_equal(x2.numpy(), np_twos_2d)
2474
+ assert_np_equal(x3.numpy(), np_twos_3d)
2475
+ assert_np_equal(x4.numpy(), np_twos_4d)
2476
+
2477
+ x1.grad.zero_()
2478
+ x2.grad.zero_()
2479
+ x3.grad.zero_()
2480
+ x4.grad.zero_()
2481
+ tape.reset()
2482
+
2483
+ with tape:
2484
+ wp.launch(inplace_sub_1d, N, inputs=[x1, y1], device=device)
2485
+ wp.launch(inplace_sub_2d, (N, N), inputs=[x2, y2], device=device)
2486
+ wp.launch(inplace_sub_3d, (N, N, N), inputs=[x3, y3], device=device)
2487
+ wp.launch(inplace_sub_4d, (N, N, N, N), inputs=[x4, y4], device=device)
2488
+
2489
+ tape.backward(grads={x1: wp.ones_like(x1), x2: wp.ones_like(x2), x3: wp.ones_like(x3), x4: wp.ones_like(x4)})
2490
+
2491
+ assert_np_equal(x1.grad.numpy(), np_ones_1d)
2492
+ assert_np_equal(x2.grad.numpy(), np_ones_2d)
2493
+ assert_np_equal(x3.grad.numpy(), np_ones_3d)
2494
+ assert_np_equal(x4.grad.numpy(), np_ones_4d)
2495
+
2496
+ assert_np_equal(y1.grad.numpy(), -np_ones_1d)
2497
+ assert_np_equal(y2.grad.numpy(), -np_ones_2d)
2498
+ assert_np_equal(y3.grad.numpy(), -np_ones_3d)
2499
+ assert_np_equal(y4.grad.numpy(), -np_ones_4d)
2500
+
2501
+ assert_np_equal(x1.numpy(), np_ones_1d)
2502
+ assert_np_equal(x2.numpy(), np_ones_2d)
2503
+ assert_np_equal(x3.numpy(), np_ones_3d)
2504
+ assert_np_equal(x4.numpy(), np_ones_4d)
2505
+
2506
+ x1.grad.zero_()
2507
+ x2.grad.zero_()
2508
+ x3.grad.zero_()
2509
+ x4.grad.zero_()
2510
+ tape.reset()
2511
+
2512
+ with tape:
2513
+ wp.launch(inplace_add_vecs, 1, inputs=[v1, v2], device=device)
2514
+ wp.launch(inplace_add_mats, 1, inputs=[m1, m2], device=device)
2515
+ wp.launch(inplace_add_rhs, 1, inputs=[x, y, z], device=device)
2516
+
2517
+ tape.backward(loss=z, grads={v1: wp.ones_like(v1, requires_grad=False), m1: wp.ones_like(m1, requires_grad=False)})
2518
+
2519
+ assert_np_equal(v1.numpy(), np.full(shape=(1, 3), fill_value=2.0, dtype=float))
2520
+ assert_np_equal(v1.grad.numpy(), np.ones(shape=(1, 3), dtype=float))
2521
+ assert_np_equal(v2.grad.numpy(), np.ones(shape=(1, 3), dtype=float))
2522
+
2523
+ assert_np_equal(m1.numpy(), np.full(shape=(1, 3, 3), fill_value=2.0, dtype=float))
2524
+ assert_np_equal(m1.grad.numpy(), np.ones(shape=(1, 3, 3), dtype=float))
2525
+ assert_np_equal(m2.grad.numpy(), np.ones(shape=(1, 3, 3), dtype=float))
2526
+
2527
+ assert_np_equal(x.grad.numpy(), np.ones(1, dtype=float))
2528
+ assert_np_equal(y.grad.numpy(), np.ones(1, dtype=float))
2529
+
2530
+
2364
2531
  @wp.kernel
2365
2532
  def inc_scalar(a: wp.array(dtype=float)):
2366
2533
  tid = wp.tid()
@@ -2423,6 +2590,25 @@ def test_array_from_int64_domain(test, device):
2423
2590
  wp.zeros(np.array([1504, 1080, 520], dtype=np.int64), dtype=wp.float32, device=device)
2424
2591
 
2425
2592
 
2593
+ def test_numpy_array_interface(test, device):
2594
+ # We should be able to convert between NumPy and Warp arrays using __array_interface__ on CPU.
2595
+ # This tests all scalar types supported by both.
2596
+
2597
+ n = 10
2598
+
2599
+ scalar_types = wp.types.scalar_types
2600
+
2601
+ for dtype in scalar_types:
2602
+ # test round trip
2603
+ a1 = wp.zeros(n, dtype=dtype, device="cpu")
2604
+ na = np.array(a1)
2605
+ a2 = wp.array(na, device="cpu")
2606
+
2607
+ assert a1.dtype == a2.dtype
2608
+ assert a1.shape == a2.shape
2609
+ assert a1.strides == a2.strides
2610
+
2611
+
2426
2612
  devices = get_test_devices()
2427
2613
 
2428
2614
 
@@ -2481,7 +2667,9 @@ add_function_test(TestArray, "test_array_of_structs_from_numpy", test_array_of_s
2481
2667
  add_function_test(TestArray, "test_array_of_structs_roundtrip", test_array_of_structs_roundtrip, devices=devices)
2482
2668
  add_function_test(TestArray, "test_array_from_numpy", test_array_from_numpy, devices=devices)
2483
2669
  add_function_test(TestArray, "test_array_aliasing_from_numpy", test_array_aliasing_from_numpy, devices=["cpu"])
2670
+ add_function_test(TestArray, "test_numpy_array_interface", test_numpy_array_interface, devices=["cpu"])
2484
2671
 
2672
+ add_function_test(TestArray, "test_array_inplace_ops", test_array_inplace_ops, devices=devices)
2485
2673
  add_function_test(TestArray, "test_direct_from_numpy", test_direct_from_numpy, devices=["cpu"])
2486
2674
  add_function_test(TestArray, "test_kernel_array_from_ptr", test_kernel_array_from_ptr, devices=devices)
2487
2675
 
warp/tests/test_async.py CHANGED
@@ -11,7 +11,7 @@ import numpy as np
11
11
 
12
12
  import warp as wp
13
13
  from warp.tests.unittest_utils import *
14
- from warp.utils import check_iommu
14
+ from warp.utils import check_p2p
15
15
 
16
16
 
17
17
  class Capturable:
@@ -507,8 +507,8 @@ for src_type, src_ctor in array_constructors.items():
507
507
  copy_type = f"{array_type_codes[src_type]}2{array_type_codes[dst_type]}"
508
508
 
509
509
  for transfer_type, device_pair in device_pairs.items():
510
- # skip p2p tests if IOMMU is enabled on Linux
511
- if transfer_type == "p2p" and not check_iommu():
510
+ # skip p2p tests if not supported (e.g., IOMMU is enabled on Linux)
511
+ if transfer_type == "p2p" and not check_p2p():
512
512
  continue
513
513
 
514
514
  src_device = device_pair[0]
warp/tests/test_atomic.py CHANGED
@@ -45,6 +45,10 @@ def make_atomic_test(type):
45
45
  base = rng.random(size=1, dtype=np.float32)
46
46
  val = rng.random(size=n, dtype=np.float32)
47
47
 
48
+ elif type == wp.float64:
49
+ base = rng.random(size=1, dtype=np.float64)
50
+ val = rng.random(size=n, dtype=np.float64)
51
+
48
52
  else:
49
53
  base = rng.random(size=(1, *type._shape_), dtype=float)
50
54
  val = rng.random(size=(n, *type._shape_), dtype=float)
@@ -109,6 +113,7 @@ def make_atomic_test(type):
109
113
  # generate test functions for atomic types
110
114
  test_atomic_int = make_atomic_test(wp.int32)
111
115
  test_atomic_float = make_atomic_test(wp.float32)
116
+ test_atomic_double = make_atomic_test(wp.float64)
112
117
  test_atomic_vec2 = make_atomic_test(wp.vec2)
113
118
  test_atomic_vec3 = make_atomic_test(wp.vec3)
114
119
  test_atomic_vec4 = make_atomic_test(wp.vec4)
@@ -126,6 +131,7 @@ class TestAtomic(unittest.TestCase):
126
131
 
127
132
  add_function_test(TestAtomic, "test_atomic_int", test_atomic_int, devices=devices)
128
133
  add_function_test(TestAtomic, "test_atomic_float", test_atomic_float, devices=devices)
134
+ add_function_test(TestAtomic, "test_atomic_double", test_atomic_double, devices=devices)
129
135
  add_function_test(TestAtomic, "test_atomic_vec2", test_atomic_vec2, devices=devices)
130
136
  add_function_test(TestAtomic, "test_atomic_vec3", test_atomic_vec3, devices=devices)
131
137
  add_function_test(TestAtomic, "test_atomic_vec4", test_atomic_vec4, devices=devices)
@@ -170,6 +170,93 @@ def test_edge_edge_perpendicular_s0_t1(test, device):
170
170
  test.assertAlmostEqual(st0[1], 1.0) # t value
171
171
 
172
172
 
173
+ @wp.func
174
+ def check_edge_closest_point_sufficient_necessary(c1: wp.vec3, c2: wp.vec3, t: float, p: wp.vec3, q: wp.vec3):
175
+ """
176
+ This is a sufficient and necessary condition of closest point
177
+ c1: closest point on the other edge
178
+ c2: closest point on edge p-q
179
+ t: c2 = (1.0-t) * p + t * q
180
+ e1, e2: end points of the edge
181
+ """
182
+ eps = 1e-5
183
+ e = p - q
184
+ if t == 0.0:
185
+ wp.expect_eq(wp.dot(c1 - p, p - q) > -eps, True)
186
+ wp.expect_eq(wp.abs(wp.length(c2 - p)) < eps, True)
187
+ elif t == 1.0:
188
+ wp.expect_eq(wp.dot(c1 - q, q - p) > -eps, True)
189
+ wp.expect_eq(wp.abs(wp.length(c2 - q)) < eps, True)
190
+ else:
191
+ # interior closest point, c1c2 must be perpendicular to e
192
+ c1c2 = c1 - c2
193
+ wp.expect_eq(wp.abs(wp.dot(c1c2, e)) < eps, True)
194
+
195
+
196
+ @wp.kernel
197
+ def check_edge_closest_point_sufficient_necessary_kernel(
198
+ p1s: wp.array(dtype=wp.vec3),
199
+ q1s: wp.array(dtype=wp.vec3),
200
+ p2s: wp.array(dtype=wp.vec3),
201
+ q2s: wp.array(dtype=wp.vec3),
202
+ epsilon: float,
203
+ ):
204
+ tid = wp.tid()
205
+
206
+ p1 = p1s[tid]
207
+ q1 = q1s[tid]
208
+ p2 = p2s[tid]
209
+ q2 = q2s[tid]
210
+
211
+ st = wp.closest_point_edge_edge(p1, q1, p2, q2, epsilon)
212
+ s = st[0]
213
+ t = st[1]
214
+ c1 = p1 + (q1 - p1) * s
215
+ c2 = p2 + (q2 - p2) * t
216
+
217
+ check_edge_closest_point_sufficient_necessary(c1, c2, t, p2, q2)
218
+ check_edge_closest_point_sufficient_necessary(c2, c1, s, p1, q1)
219
+
220
+
221
+ def check_edge_closest_point_random(test, device):
222
+ num_tests = 100000
223
+ np.random.seed(12345)
224
+ p1 = wp.array(np.random.randn(num_tests, 3), dtype=wp.vec3, device=device)
225
+ q1 = wp.array(np.random.randn(num_tests, 3), dtype=wp.vec3, device=device)
226
+
227
+ p2 = wp.array(np.random.randn(num_tests, 3), dtype=wp.vec3, device=device)
228
+ q2 = wp.array(np.random.randn(num_tests, 3), dtype=wp.vec3, device=device)
229
+
230
+ wp.launch(
231
+ kernel=check_edge_closest_point_sufficient_necessary_kernel,
232
+ dim=num_tests,
233
+ inputs=[p1, q1, p2, q2, epsilon],
234
+ device=device,
235
+ )
236
+
237
+ # parallel edges
238
+ p1 = np.random.randn(num_tests, 3)
239
+ q1 = np.random.randn(num_tests, 3)
240
+
241
+ shifts = np.random.randn(num_tests, 3)
242
+
243
+ p2 = p1 + shifts
244
+ q2 = q1 + shifts
245
+
246
+ p1 = wp.array(p1, dtype=wp.vec3, device=device)
247
+ q1 = wp.array(q1, dtype=wp.vec3, device=device)
248
+
249
+ p2 = wp.array(p2, dtype=wp.vec3, device=device)
250
+ q2 = wp.array(q2, dtype=wp.vec3, device=device)
251
+
252
+ wp.launch(
253
+ kernel=check_edge_closest_point_sufficient_necessary_kernel,
254
+ dim=num_tests,
255
+ inputs=[p1, q1, p2, q2, epsilon],
256
+ device=device,
257
+ )
258
+
259
+
173
260
  devices = get_test_devices()
174
261
 
175
262
 
@@ -220,7 +307,12 @@ add_function_test(
220
307
  test_edge_edge_perpendicular_s0_t1,
221
308
  devices=devices,
222
309
  )
223
-
310
+ add_function_test(
311
+ TestClosestPointEdgeEdgeMethods,
312
+ "test_edge_closest_point_random",
313
+ check_edge_closest_point_random,
314
+ devices=devices,
315
+ )
224
316
 
225
317
  if __name__ == "__main__":
226
318
  wp.clear_kernel_cache()
@@ -405,24 +405,24 @@ def test_error_global_var(test, device):
405
405
 
406
406
  kernel = wp.Kernel(func=kernel_1_fn)
407
407
  with test.assertRaisesRegex(
408
- RuntimeError,
409
- r"Cannot reference a global variable from a kernel unless `wp.constant\(\)` is being used",
408
+ TypeError,
409
+ r"Invalid external reference type: <class 'warp.types.array'>",
410
410
  ):
411
- wp.launch(kernel, dim=out.shape, inputs=(), outputs=(out,))
411
+ wp.launch(kernel, dim=out.shape, inputs=(), outputs=(out,), device=device)
412
412
 
413
413
  kernel = wp.Kernel(func=kernel_2_fn)
414
414
  with test.assertRaisesRegex(
415
- RuntimeError,
416
- r"Cannot reference a global variable from a kernel unless `wp.constant\(\)` is being used",
415
+ TypeError,
416
+ r"Invalid external reference type: <class 'warp.types.array'>",
417
417
  ):
418
- wp.launch(kernel, dim=out.shape, inputs=(), outputs=(out,))
418
+ wp.launch(kernel, dim=out.shape, inputs=(), outputs=(out,), device=device)
419
419
 
420
420
  kernel = wp.Kernel(func=kernel_3_fn)
421
421
  with test.assertRaisesRegex(
422
- RuntimeError,
423
- r"Cannot reference a global variable from a kernel unless `wp.constant\(\)` is being used",
422
+ TypeError,
423
+ r"Invalid external reference type: <class 'warp.types.array'>",
424
424
  ):
425
- wp.launch(kernel, dim=out.shape, inputs=(), outputs=(out,))
425
+ wp.launch(kernel, dim=out.shape, inputs=(), outputs=(out,), device=device)
426
426
 
427
427
 
428
428
  def test_error_collection_construct(test, device):
@@ -443,28 +443,28 @@ def test_error_collection_construct(test, device):
443
443
  RuntimeError,
444
444
  r"List constructs are not supported in kernels. Use vectors like `wp.vec3\(\)` for small collections instead.",
445
445
  ):
446
- wp.launch(kernel, dim=1)
446
+ wp.launch(kernel, dim=1, device=device)
447
447
 
448
448
  kernel = wp.Kernel(func=kernel_2_fn)
449
449
  with test.assertRaisesRegex(
450
450
  RuntimeError,
451
451
  r"Tuple constructs are not supported in kernels. Use vectors like `wp.vec3\(\)` for small collections instead.",
452
452
  ):
453
- wp.launch(kernel, dim=1)
453
+ wp.launch(kernel, dim=1, device=device)
454
454
 
455
455
  kernel = wp.Kernel(func=kernel_3_fn)
456
456
  with test.assertRaisesRegex(
457
457
  RuntimeError,
458
458
  r"Construct `ast.Dict` not supported in kernels.",
459
459
  ):
460
- wp.launch(kernel, dim=1)
460
+ wp.launch(kernel, dim=1, device=device)
461
461
 
462
462
  kernel = wp.Kernel(func=kernel_4_fn)
463
463
  with test.assertRaisesRegex(
464
464
  RuntimeError,
465
465
  r"Tuple constructs are not supported in kernels. Use vectors like `wp.vec3\(\)` instead.",
466
466
  ):
467
- wp.launch(kernel, dim=1)
467
+ wp.launch(kernel, dim=1, device=device)
468
468
 
469
469
 
470
470
  def test_error_unmatched_arguments(test, device):
@@ -479,14 +479,60 @@ def test_error_unmatched_arguments(test, device):
479
479
  RuntimeError,
480
480
  r"Input types must be the same, got \['int32', 'float32'\]",
481
481
  ):
482
- wp.launch(kernel, dim=1)
482
+ wp.launch(kernel, dim=1, device=device)
483
483
 
484
484
  kernel = wp.Kernel(func=kernel_2_fn)
485
485
  with test.assertRaisesRegex(
486
486
  RuntimeError,
487
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
488
  ):
489
- wp.launch(kernel, dim=1)
489
+ wp.launch(kernel, dim=1, device=device)
490
+
491
+
492
+ def test_error_mutating_constant_in_dynamic_loop(test, device):
493
+ @wp.kernel
494
+ def dynamic_loop_kernel(n: int, input: wp.array(dtype=float)):
495
+ my_constant = 0.0
496
+ for i in range(n):
497
+ my_constant += input[i]
498
+
499
+ inputs = wp.array([1.0, 2.0, 3.0], dtype=float, device=device)
500
+ with test.assertRaisesRegex(
501
+ wp.codegen.WarpCodegenError,
502
+ r"Error mutating a constant my_constant inside a dynamic loop, use the following syntax\: pi = float\(3\.141\) to declare a dynamic variable",
503
+ ):
504
+ wp.launch(dynamic_loop_kernel, dim=1, inputs=[3, inputs], device=device)
505
+
506
+ # the following nested loop must not raise an error
507
+ const_a = 7
508
+ const_b = 5
509
+
510
+ @wp.kernel
511
+ def mixed_dyn_static_loop_kernel(dyn_a: int, dyn_b: int, dyn_c: int, output: wp.array(dtype=float, ndim=2)):
512
+ tid = wp.tid()
513
+ for i in range(const_a + 1):
514
+ for j in range(dyn_a + 1):
515
+ for k in range(dyn_b + 1):
516
+ for l in range(const_b + 1):
517
+ for m in range(dyn_c + 1):
518
+ coeff = i + j + k + l + m
519
+ output[tid, coeff] = 1.0
520
+
521
+ dyn_a, dyn_b, dyn_c = 3, 4, 5
522
+ num_threads = 10
523
+ output = wp.empty([num_threads, const_a + const_b + dyn_a + dyn_b + dyn_c + 1], dtype=float, device=device)
524
+ wp.launch(
525
+ mixed_dyn_static_loop_kernel,
526
+ num_threads,
527
+ inputs=[
528
+ dyn_a,
529
+ dyn_b,
530
+ dyn_c,
531
+ ],
532
+ outputs=[output],
533
+ device=device,
534
+ )
535
+ assert_np_equal(output.numpy(), np.ones([num_threads, const_a + const_b + dyn_a + dyn_b + dyn_c + 1]))
490
536
 
491
537
 
492
538
  @wp.kernel
@@ -507,6 +553,30 @@ def test_call_syntax():
507
553
  wp.expect_eq(wp.matrix(rot=rot, pos=pos, dtype=wp.float32, scale=scale), expected_matrix)
508
554
 
509
555
 
556
+ # test shadowing builtin functions
557
+ @wp.func
558
+ def sum(a: wp.vec3) -> float:
559
+ return a[0] + a[1] + a[2]
560
+
561
+
562
+ @wp.kernel
563
+ def test_shadow_builtin():
564
+ wp.expect_eq(sum(wp.vec3(1.0)), 3.0)
565
+
566
+
567
+ @wp.struct
568
+ class Iterator:
569
+ valid: wp.bool
570
+
571
+
572
+ @wp.kernel(enable_backward=False)
573
+ def test_while_condition_eval():
574
+ it = Iterator()
575
+ it.valid = True
576
+ while it.valid:
577
+ it.valid = False
578
+
579
+
510
580
  class TestCodeGen(unittest.TestCase):
511
581
  pass
512
582
 
@@ -643,8 +713,16 @@ add_function_test(
643
713
  add_function_test(
644
714
  TestCodeGen, func=test_error_unmatched_arguments, name="test_error_unmatched_arguments", devices=devices
645
715
  )
716
+ add_function_test(
717
+ TestCodeGen,
718
+ func=test_error_mutating_constant_in_dynamic_loop,
719
+ name="test_error_mutating_constant_in_dynamic_loop",
720
+ devices=devices,
721
+ )
646
722
 
647
723
  add_kernel_test(TestCodeGen, name="test_call_syntax", kernel=test_call_syntax, dim=1, devices=devices)
724
+ add_kernel_test(TestCodeGen, name="test_shadow_builtin", kernel=test_shadow_builtin, dim=1, devices=devices)
725
+ add_kernel_test(TestCodeGen, name="test_while_condition_eval", kernel=test_while_condition_eval, dim=1, devices=devices)
648
726
 
649
727
 
650
728
  if __name__ == "__main__":