warp-lang 1.3.2__py3-none-manylinux2014_x86_64.whl → 1.4.0__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 (107) 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 +126 -4
  6. warp/codegen.py +435 -53
  7. warp/config.py +1 -1
  8. warp/context.py +678 -403
  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 +23 -4
  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 +113 -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 +2 -0
  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 -17
  39. warp/native/mat.h +97 -0
  40. warp/native/mesh.cpp +36 -0
  41. warp/native/mesh.cu +51 -0
  42. warp/native/mesh.h +1 -0
  43. warp/native/quat.h +43 -0
  44. warp/native/spatial.h +6 -0
  45. warp/native/vec.h +74 -0
  46. warp/native/warp.cpp +2 -1
  47. warp/native/warp.cu +10 -3
  48. warp/native/warp.h +8 -1
  49. warp/paddle.py +382 -0
  50. warp/sim/__init__.py +1 -0
  51. warp/sim/collide.py +519 -0
  52. warp/sim/integrator_euler.py +18 -5
  53. warp/sim/integrator_featherstone.py +5 -5
  54. warp/sim/integrator_vbd.py +1026 -0
  55. warp/sim/model.py +49 -23
  56. warp/stubs.py +459 -0
  57. warp/tape.py +2 -0
  58. warp/tests/aux_test_dependent.py +1 -0
  59. warp/tests/aux_test_name_clash1.py +32 -0
  60. warp/tests/aux_test_name_clash2.py +32 -0
  61. warp/tests/aux_test_square.py +1 -0
  62. warp/tests/test_array.py +222 -0
  63. warp/tests/test_async.py +3 -3
  64. warp/tests/test_atomic.py +6 -0
  65. warp/tests/test_closest_point_edge_edge.py +93 -1
  66. warp/tests/test_codegen.py +62 -15
  67. warp/tests/test_codegen_instancing.py +1457 -0
  68. warp/tests/test_collision.py +486 -0
  69. warp/tests/test_compile_consts.py +3 -28
  70. warp/tests/test_dlpack.py +170 -0
  71. warp/tests/test_examples.py +22 -8
  72. warp/tests/test_fast_math.py +10 -4
  73. warp/tests/test_fem.py +64 -0
  74. warp/tests/test_func.py +46 -0
  75. warp/tests/test_implicit_init.py +49 -0
  76. warp/tests/test_jax.py +58 -0
  77. warp/tests/test_mat.py +84 -0
  78. warp/tests/test_mesh_query_point.py +188 -0
  79. warp/tests/test_module_hashing.py +40 -0
  80. warp/tests/test_multigpu.py +3 -3
  81. warp/tests/test_overwrite.py +8 -0
  82. warp/tests/test_paddle.py +852 -0
  83. warp/tests/test_print.py +89 -0
  84. warp/tests/test_quat.py +111 -0
  85. warp/tests/test_reload.py +31 -1
  86. warp/tests/test_scalar_ops.py +2 -0
  87. warp/tests/test_static.py +412 -0
  88. warp/tests/test_streams.py +64 -3
  89. warp/tests/test_struct.py +4 -4
  90. warp/tests/test_torch.py +24 -0
  91. warp/tests/test_triangle_closest_point.py +137 -0
  92. warp/tests/test_types.py +1 -1
  93. warp/tests/test_vbd.py +386 -0
  94. warp/tests/test_vec.py +143 -0
  95. warp/tests/test_vec_scalar_ops.py +139 -0
  96. warp/tests/test_volume.py +30 -0
  97. warp/tests/unittest_suites.py +12 -0
  98. warp/tests/unittest_utils.py +9 -5
  99. warp/thirdparty/dlpack.py +3 -1
  100. warp/types.py +157 -34
  101. warp/utils.py +37 -14
  102. {warp_lang-1.3.2.dist-info → warp_lang-1.4.0.dist-info}/METADATA +10 -8
  103. {warp_lang-1.3.2.dist-info → warp_lang-1.4.0.dist-info}/RECORD +106 -94
  104. warp/tests/test_point_triangle_closest_point.py +0 -143
  105. {warp_lang-1.3.2.dist-info → warp_lang-1.4.0.dist-info}/LICENSE.md +0 -0
  106. {warp_lang-1.3.2.dist-info → warp_lang-1.4.0.dist-info}/WHEEL +0 -0
  107. {warp_lang-1.3.2.dist-info → warp_lang-1.4.0.dist-info}/top_level.txt +0 -0
warp/tests/test_array.py CHANGED
@@ -2295,6 +2295,39 @@ def test_array_from_numpy(test, device):
2295
2295
  assert_np_equal(result.numpy(), expected.numpy())
2296
2296
 
2297
2297
 
2298
+ def test_array_aliasing_from_numpy(test, device):
2299
+ device = wp.get_device(device)
2300
+ assert device.is_cpu
2301
+
2302
+ a_np = np.ones(8, dtype=np.int32)
2303
+ a_wp = wp.array(a_np, dtype=int, copy=False, device=device)
2304
+ test.assertIs(a_wp._ref, a_np) # check that some ref is kept to original array
2305
+ test.assertEqual(a_wp.ptr, a_np.ctypes.data)
2306
+
2307
+ a_np_2 = a_wp.numpy()
2308
+ test.assertTrue((a_np_2 == 1).all())
2309
+
2310
+ # updating source array should update aliased array
2311
+ a_np.fill(2)
2312
+ test.assertTrue((a_np_2 == 2).all())
2313
+
2314
+ # trying to alias from a different type should do a copy
2315
+ # do it twice to check that the copy buffer is not being reused for different arrays
2316
+
2317
+ b_np = np.ones(8, dtype=np.int64)
2318
+ c_np = np.zeros(8, dtype=np.int64)
2319
+ b_wp = wp.array(b_np, dtype=int, copy=False, device=device)
2320
+ c_wp = wp.array(c_np, dtype=int, copy=False, device=device)
2321
+
2322
+ test.assertNotEqual(b_wp.ptr, b_np.ctypes.data)
2323
+ test.assertNotEqual(b_wp.ptr, c_wp.ptr)
2324
+
2325
+ b_np_2 = b_wp.numpy()
2326
+ c_np_2 = c_wp.numpy()
2327
+ test.assertTrue((b_np_2 == 1).all())
2328
+ test.assertTrue((c_np_2 == 0).all())
2329
+
2330
+
2298
2331
  def test_array_from_cai(test, device):
2299
2332
  import torch
2300
2333
 
@@ -2328,6 +2361,173 @@ def test_array_from_cai(test, device):
2328
2361
  assert_np_equal(arr_warp.numpy(), np.array([[2, 1, 1], [1, 0, 0], [1, 0, 0]]))
2329
2362
 
2330
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
+
2331
2531
  @wp.kernel
2332
2532
  def inc_scalar(a: wp.array(dtype=float)):
2333
2533
  tid = wp.tid()
@@ -2390,6 +2590,25 @@ def test_array_from_int64_domain(test, device):
2390
2590
  wp.zeros(np.array([1504, 1080, 520], dtype=np.int64), dtype=wp.float32, device=device)
2391
2591
 
2392
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
+
2393
2612
  devices = get_test_devices()
2394
2613
 
2395
2614
 
@@ -2447,7 +2666,10 @@ add_function_test(TestArray, "test_array_of_structs_grad", test_array_of_structs
2447
2666
  add_function_test(TestArray, "test_array_of_structs_from_numpy", test_array_of_structs_from_numpy, devices=devices)
2448
2667
  add_function_test(TestArray, "test_array_of_structs_roundtrip", test_array_of_structs_roundtrip, devices=devices)
2449
2668
  add_function_test(TestArray, "test_array_from_numpy", test_array_from_numpy, devices=devices)
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"])
2450
2671
 
2672
+ add_function_test(TestArray, "test_array_inplace_ops", test_array_inplace_ops, devices=devices)
2451
2673
  add_function_test(TestArray, "test_direct_from_numpy", test_direct_from_numpy, devices=["cpu"])
2452
2674
  add_function_test(TestArray, "test_kernel_array_from_ptr", test_kernel_array_from_ptr, devices=devices)
2453
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,29 @@ 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)
490
505
 
491
506
 
492
507
  @wp.kernel
@@ -507,6 +522,30 @@ def test_call_syntax():
507
522
  wp.expect_eq(wp.matrix(rot=rot, pos=pos, dtype=wp.float32, scale=scale), expected_matrix)
508
523
 
509
524
 
525
+ # test shadowing builtin functions
526
+ @wp.func
527
+ def sum(a: wp.vec3) -> float:
528
+ return a[0] + a[1] + a[2]
529
+
530
+
531
+ @wp.kernel
532
+ def test_shadow_builtin():
533
+ wp.expect_eq(sum(wp.vec3(1.0)), 3.0)
534
+
535
+
536
+ @wp.struct
537
+ class Iterator:
538
+ valid: wp.bool
539
+
540
+
541
+ @wp.kernel(enable_backward=False)
542
+ def test_while_condition_eval():
543
+ it = Iterator()
544
+ it.valid = True
545
+ while it.valid:
546
+ it.valid = False
547
+
548
+
510
549
  class TestCodeGen(unittest.TestCase):
511
550
  pass
512
551
 
@@ -643,8 +682,16 @@ add_function_test(
643
682
  add_function_test(
644
683
  TestCodeGen, func=test_error_unmatched_arguments, name="test_error_unmatched_arguments", devices=devices
645
684
  )
685
+ add_function_test(
686
+ TestCodeGen,
687
+ func=test_error_mutating_constant_in_dynamic_loop,
688
+ name="test_error_mutating_constant_in_dynamic_loop",
689
+ devices=devices,
690
+ )
646
691
 
647
692
  add_kernel_test(TestCodeGen, name="test_call_syntax", kernel=test_call_syntax, dim=1, devices=devices)
693
+ add_kernel_test(TestCodeGen, name="test_shadow_builtin", kernel=test_shadow_builtin, dim=1, devices=devices)
694
+ add_kernel_test(TestCodeGen, name="test_while_condition_eval", kernel=test_while_condition_eval, dim=1, devices=devices)
648
695
 
649
696
 
650
697
  if __name__ == "__main__":