warp-lang 1.5.0__py3-none-win_amd64.whl → 1.6.0__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.

Files changed (132) hide show
  1. warp/__init__.py +5 -0
  2. warp/autograd.py +414 -191
  3. warp/bin/warp-clang.dll +0 -0
  4. warp/bin/warp.dll +0 -0
  5. warp/build.py +40 -12
  6. warp/build_dll.py +13 -6
  7. warp/builtins.py +1124 -497
  8. warp/codegen.py +261 -136
  9. warp/config.py +1 -1
  10. warp/context.py +357 -119
  11. warp/examples/assets/square_cloth.usd +0 -0
  12. warp/examples/benchmarks/benchmark_gemm.py +27 -18
  13. warp/examples/benchmarks/benchmark_interop_paddle.py +3 -3
  14. warp/examples/benchmarks/benchmark_interop_torch.py +3 -3
  15. warp/examples/core/example_torch.py +18 -34
  16. warp/examples/fem/example_apic_fluid.py +1 -0
  17. warp/examples/fem/example_mixed_elasticity.py +1 -1
  18. warp/examples/optim/example_bounce.py +1 -1
  19. warp/examples/optim/example_cloth_throw.py +1 -1
  20. warp/examples/optim/example_diffray.py +4 -15
  21. warp/examples/optim/example_drone.py +1 -1
  22. warp/examples/optim/example_softbody_properties.py +392 -0
  23. warp/examples/optim/example_trajectory.py +1 -3
  24. warp/examples/optim/example_walker.py +5 -0
  25. warp/examples/sim/example_cartpole.py +0 -2
  26. warp/examples/sim/example_cloth.py +3 -1
  27. warp/examples/sim/example_cloth_self_contact.py +260 -0
  28. warp/examples/sim/example_granular_collision_sdf.py +4 -5
  29. warp/examples/sim/example_jacobian_ik.py +0 -2
  30. warp/examples/sim/example_quadruped.py +5 -2
  31. warp/examples/tile/example_tile_cholesky.py +79 -0
  32. warp/examples/tile/example_tile_convolution.py +2 -2
  33. warp/examples/tile/example_tile_fft.py +2 -2
  34. warp/examples/tile/example_tile_filtering.py +3 -3
  35. warp/examples/tile/example_tile_matmul.py +4 -4
  36. warp/examples/tile/example_tile_mlp.py +12 -12
  37. warp/examples/tile/example_tile_nbody.py +180 -0
  38. warp/examples/tile/example_tile_walker.py +319 -0
  39. warp/fem/geometry/geometry.py +0 -2
  40. warp/math.py +147 -0
  41. warp/native/array.h +12 -0
  42. warp/native/builtin.h +0 -1
  43. warp/native/bvh.cpp +149 -70
  44. warp/native/bvh.cu +287 -68
  45. warp/native/bvh.h +195 -85
  46. warp/native/clang/clang.cpp +5 -1
  47. warp/native/coloring.cpp +5 -1
  48. warp/native/cuda_util.cpp +91 -53
  49. warp/native/cuda_util.h +5 -0
  50. warp/native/exports.h +40 -40
  51. warp/native/intersect.h +17 -0
  52. warp/native/mat.h +41 -0
  53. warp/native/mathdx.cpp +19 -0
  54. warp/native/mesh.cpp +25 -8
  55. warp/native/mesh.cu +153 -101
  56. warp/native/mesh.h +482 -403
  57. warp/native/quat.h +40 -0
  58. warp/native/solid_angle.h +7 -0
  59. warp/native/sort.cpp +85 -0
  60. warp/native/sort.cu +34 -0
  61. warp/native/sort.h +3 -1
  62. warp/native/spatial.h +11 -0
  63. warp/native/tile.h +1187 -669
  64. warp/native/tile_reduce.h +8 -6
  65. warp/native/vec.h +41 -0
  66. warp/native/warp.cpp +8 -1
  67. warp/native/warp.cu +263 -40
  68. warp/native/warp.h +19 -5
  69. warp/optim/linear.py +22 -4
  70. warp/render/render_opengl.py +130 -64
  71. warp/sim/__init__.py +6 -1
  72. warp/sim/collide.py +270 -26
  73. warp/sim/import_urdf.py +8 -8
  74. warp/sim/integrator_euler.py +25 -7
  75. warp/sim/integrator_featherstone.py +154 -35
  76. warp/sim/integrator_vbd.py +842 -40
  77. warp/sim/model.py +134 -72
  78. warp/sparse.py +1 -1
  79. warp/stubs.py +265 -132
  80. warp/tape.py +28 -30
  81. warp/tests/aux_test_module_unload.py +15 -0
  82. warp/tests/{test_sim_grad.py → flaky_test_sim_grad.py} +104 -63
  83. warp/tests/test_array.py +74 -0
  84. warp/tests/test_assert.py +242 -0
  85. warp/tests/test_codegen.py +14 -61
  86. warp/tests/test_collision.py +2 -2
  87. warp/tests/test_coloring.py +12 -2
  88. warp/tests/test_examples.py +12 -1
  89. warp/tests/test_func.py +21 -4
  90. warp/tests/test_grad_debug.py +87 -2
  91. warp/tests/test_hash_grid.py +1 -1
  92. warp/tests/test_ipc.py +116 -0
  93. warp/tests/test_lerp.py +13 -87
  94. warp/tests/test_mat.py +138 -167
  95. warp/tests/test_math.py +47 -1
  96. warp/tests/test_matmul.py +17 -16
  97. warp/tests/test_matmul_lite.py +10 -15
  98. warp/tests/test_mesh.py +84 -60
  99. warp/tests/test_mesh_query_aabb.py +165 -0
  100. warp/tests/test_mesh_query_point.py +328 -286
  101. warp/tests/test_mesh_query_ray.py +134 -121
  102. warp/tests/test_mlp.py +2 -2
  103. warp/tests/test_operators.py +43 -0
  104. warp/tests/test_overwrite.py +47 -2
  105. warp/tests/test_quat.py +77 -0
  106. warp/tests/test_reload.py +29 -0
  107. warp/tests/test_sim_grad_bounce_linear.py +204 -0
  108. warp/tests/test_smoothstep.py +17 -83
  109. warp/tests/test_static.py +19 -3
  110. warp/tests/test_tape.py +25 -0
  111. warp/tests/test_tile.py +178 -191
  112. warp/tests/test_tile_load.py +356 -0
  113. warp/tests/test_tile_mathdx.py +61 -8
  114. warp/tests/test_tile_mlp.py +17 -17
  115. warp/tests/test_tile_reduce.py +24 -18
  116. warp/tests/test_tile_shared_memory.py +66 -17
  117. warp/tests/test_tile_view.py +165 -0
  118. warp/tests/test_torch.py +35 -0
  119. warp/tests/test_utils.py +36 -24
  120. warp/tests/test_vec.py +110 -0
  121. warp/tests/unittest_suites.py +29 -4
  122. warp/tests/unittest_utils.py +30 -13
  123. warp/thirdparty/unittest_parallel.py +2 -2
  124. warp/types.py +411 -101
  125. warp/utils.py +10 -7
  126. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/METADATA +92 -69
  127. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/RECORD +130 -119
  128. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/WHEEL +1 -1
  129. warp/examples/benchmarks/benchmark_tile.py +0 -179
  130. warp/native/tile_gemm.h +0 -341
  131. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/LICENSE.md +0 -0
  132. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,204 @@
1
+ # Copyright (c) 2025 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 numpy as np
9
+
10
+ import warp as wp
11
+ import warp.optim
12
+ import warp.sim
13
+ import warp.sim.render
14
+ from warp.tests.unittest_utils import *
15
+
16
+
17
+ @wp.kernel
18
+ def update_trajectory_kernel(
19
+ trajectory: wp.array(dtype=wp.vec3),
20
+ q: wp.array(dtype=wp.transform),
21
+ time_step: wp.int32,
22
+ q_idx: wp.int32,
23
+ ):
24
+ trajectory[time_step] = wp.transform_get_translation(q[q_idx])
25
+
26
+
27
+ @wp.kernel
28
+ def trajectory_loss_kernel(
29
+ trajectory: wp.array(dtype=wp.vec3f),
30
+ target_trajectory: wp.array(dtype=wp.vec3f),
31
+ loss: wp.array(dtype=wp.float32),
32
+ ):
33
+ tid = wp.tid()
34
+ diff = trajectory[tid] - target_trajectory[tid]
35
+ distance_loss = wp.dot(diff, diff)
36
+ wp.atomic_add(loss, 0, distance_loss)
37
+
38
+
39
+ class BallBounceLinearTest:
40
+ def __init__(self, gravity=True, rendering=False):
41
+ # Ball bouncing scenario inspired by https://github.com/NVIDIA/warp/issues/349
42
+ self.fps = 30
43
+ self.num_frames = 60
44
+ self.sim_substeps = 20 # XXX need to use enough substeps to achieve smooth gradients
45
+ self.frame_dt = 1.0 / self.fps
46
+ self.sim_dt = self.frame_dt / self.sim_substeps
47
+ self.sim_duration = self.num_frames * self.frame_dt
48
+ self.sim_steps = int(self.sim_duration // self.sim_dt)
49
+
50
+ self.target_force_linear = 100.0
51
+
52
+ if gravity:
53
+ builder = wp.sim.ModelBuilder(up_vector=wp.vec3(0, 0, 1))
54
+ else:
55
+ builder = wp.sim.ModelBuilder(gravity=0.0, up_vector=wp.vec3(0, 0, 1))
56
+
57
+ b = builder.add_body(origin=wp.transform((0.5, 0.0, 1.0), wp.quat_identity()), name="ball")
58
+ builder.add_shape_sphere(
59
+ body=b, radius=0.1, density=100.0, ke=2000.0, kd=10.0, kf=200.0, mu=0.2, thickness=0.01
60
+ )
61
+ builder.set_ground_plane(ke=10, kd=10, kf=0.0, mu=0.2)
62
+ self.model = builder.finalize(requires_grad=True)
63
+
64
+ self.time = np.linspace(0, self.sim_duration, self.sim_steps)
65
+
66
+ self.integrator = wp.sim.SemiImplicitIntegrator()
67
+ if rendering:
68
+ self.renderer = wp.sim.render.SimRendererOpenGL(self.model, "ball_bounce_linear")
69
+ else:
70
+ self.renderer = None
71
+
72
+ self.loss = wp.array([0], dtype=wp.float32, requires_grad=True)
73
+ self.states = [self.model.state() for _ in range(self.sim_steps + 1)]
74
+ self.target_states = [self.model.state() for _ in range(self.sim_steps + 1)]
75
+
76
+ self.target_force = wp.array([0.0, 0.0, 0.0, 0.0, self.target_force_linear, 0.0], dtype=wp.spatial_vectorf)
77
+
78
+ self.trajectory = wp.empty(len(self.time), dtype=wp.vec3, requires_grad=True)
79
+ self.target_trajectory = wp.empty(len(self.time), dtype=wp.vec3)
80
+
81
+ def _reset(self):
82
+ self.loss = wp.array([0], dtype=wp.float32, requires_grad=True)
83
+
84
+ def generate_target_trajectory(self):
85
+ for i in range(self.sim_steps):
86
+ curr_state = self.target_states[i]
87
+ next_state = self.target_states[i + 1]
88
+ curr_state.clear_forces()
89
+ if i == 0:
90
+ wp.copy(curr_state.body_f, self.target_force, dest_offset=0, src_offset=0, count=1)
91
+ wp.sim.collide(self.model, curr_state)
92
+ self.integrator.simulate(self.model, curr_state, next_state, self.sim_dt)
93
+ wp.launch(kernel=update_trajectory_kernel, dim=1, inputs=[self.target_trajectory, curr_state.body_q, i, 0])
94
+
95
+ def forward(self, force: wp.array):
96
+ for i in range(self.sim_steps):
97
+ curr_state = self.states[i]
98
+ next_state = self.states[i + 1]
99
+ curr_state.clear_forces()
100
+ if i == 0:
101
+ wp.copy(curr_state.body_f, force, dest_offset=0, src_offset=0, count=1)
102
+ wp.sim.collide(self.model, curr_state)
103
+ self.integrator.simulate(self.model, curr_state, next_state, self.sim_dt)
104
+ wp.launch(kernel=update_trajectory_kernel, dim=1, inputs=[self.trajectory, curr_state.body_q, i, 0])
105
+
106
+ if self.renderer:
107
+ self.renderer.begin_frame(self.time[i])
108
+ self.renderer.render(curr_state)
109
+ self.renderer.end_frame()
110
+
111
+ def step(self, force: wp.array):
112
+ self.tape = wp.Tape()
113
+ self._reset()
114
+ with self.tape:
115
+ self.forward(force)
116
+ wp.launch(
117
+ kernel=trajectory_loss_kernel,
118
+ dim=len(self.trajectory),
119
+ inputs=[self.trajectory, self.target_trajectory, self.loss],
120
+ )
121
+ self.tape.backward(self.loss)
122
+ force_grad = force.grad.numpy()[0, 4]
123
+ self.tape.zero()
124
+
125
+ return self.loss.numpy()[0], force_grad
126
+
127
+ def evaluate(self, num_samples, plot_results=False):
128
+ forces = np.linspace(0, self.target_force_linear * 2, num_samples)
129
+ losses = np.zeros_like(forces)
130
+ grads = np.zeros_like(forces)
131
+
132
+ for i, fx in enumerate(forces):
133
+ force = wp.array([[0.0, 0.0, 0.0, 0.0, fx, 0.0]], dtype=wp.spatial_vectorf, requires_grad=True)
134
+ losses[i], grads[i] = self.step(force)
135
+ if plot_results:
136
+ print(f"Iteration {i + 1}/{num_samples}")
137
+ print(f"Force: {fx:.2f}, Loss: {losses[i]:.6f}, Grad: {grads[i]:.6f}")
138
+
139
+ assert np.isfinite(losses[i])
140
+ assert np.isfinite(grads[i])
141
+ if i > 0:
142
+ assert grads[i] >= grads[i - 1]
143
+
144
+ if plot_results:
145
+ import matplotlib.pyplot as plt
146
+
147
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
148
+
149
+ # Plot the loss curve
150
+ ax1.plot(forces, losses, label="Loss")
151
+ ax1.set_xlabel("Force")
152
+ ax1.set_ylabel("Loss")
153
+ ax1.set_title("Loss vs Force")
154
+ ax1.legend()
155
+
156
+ # Make sure the grads are not too large
157
+ grads = np.clip(grads, -1e4, 1e4)
158
+
159
+ # Plot the gradient curve
160
+ ax2.plot(forces, grads, label="Gradient", color="orange")
161
+ ax2.set_xlabel("Force")
162
+ ax2.set_ylabel("Gradient")
163
+ ax2.set_title("Gradient vs Force")
164
+ ax2.legend()
165
+
166
+ plt.suptitle("Loss and Gradient vs Force")
167
+ plt.tight_layout(rect=[0, 0, 1, 0.95])
168
+ plt.show()
169
+
170
+ return losses, grads
171
+
172
+
173
+ def test_sim_grad_bounce_linear(test, device):
174
+ with wp.ScopedDevice(device):
175
+ model = BallBounceLinearTest()
176
+ model.generate_target_trajectory()
177
+
178
+ num_samples = 20
179
+ losses, grads = model.evaluate(num_samples=num_samples)
180
+ # gradients must approximate linear behavior with zero crossing in the middle
181
+ test.assertTrue(np.abs(grads[1:] - grads[:-1]).max() < 1.1)
182
+ test.assertTrue(np.all(grads[: num_samples // 2] <= 0.0))
183
+ test.assertTrue(np.all(grads[num_samples // 2 :] >= 0.0))
184
+ # losses must follow a parabolic behavior
185
+ test.assertTrue(np.allclose(losses[: num_samples // 2], losses[num_samples // 2 :][::-1], atol=1.0))
186
+ diffs = losses[1:] - losses[:-1]
187
+ test.assertTrue(np.all(diffs[: num_samples // 2 - 1] <= 0.0))
188
+ test.assertTrue(np.all(diffs[num_samples // 2 - 1 :] >= 0.0))
189
+ # second derivative must be constant positive
190
+ diffs2 = diffs[1:] - diffs[:-1]
191
+ test.assertTrue(np.allclose(diffs2, diffs2[0], atol=1e-2))
192
+ test.assertTrue(np.all(diffs2 >= 0.0))
193
+
194
+
195
+ class TestSimGradBounceLinear(unittest.TestCase):
196
+ pass
197
+
198
+
199
+ devices = get_test_devices("basic")
200
+ add_function_test(TestSimGradBounceLinear, "test_sim_grad_bounce_linear", test_sim_grad_bounce_linear, devices=devices)
201
+
202
+ if __name__ == "__main__":
203
+ wp.clear_kernel_cache()
204
+ unittest.main(verbosity=2, failfast=True)
@@ -31,15 +31,7 @@ class TestData:
31
31
 
32
32
  TEST_DATA = {
33
33
  wp.float32: (
34
- TestData(
35
- a=1.0,
36
- b=2.0,
37
- t=1.5,
38
- expected=0.5,
39
- expected_adj_a=-0.75,
40
- expected_adj_b=-0.75,
41
- expected_adj_t=1.5,
42
- ),
34
+ TestData(a=1.0, b=2.0, t=1.5, expected=0.5, expected_adj_a=-0.75, expected_adj_b=-0.75, expected_adj_t=1.5),
43
35
  TestData(
44
36
  a=-1.0,
45
37
  b=2.0,
@@ -49,24 +41,8 @@ TEST_DATA = {
49
41
  expected_adj_b=-0.09375,
50
42
  expected_adj_t=0.375,
51
43
  ),
52
- TestData(
53
- a=0.0,
54
- b=1.0,
55
- t=9.9,
56
- expected=1.0,
57
- expected_adj_a=0.0,
58
- expected_adj_b=0.0,
59
- expected_adj_t=0.0,
60
- ),
61
- TestData(
62
- a=0.0,
63
- b=1.0,
64
- t=-9.9,
65
- expected=0.0,
66
- expected_adj_a=0.0,
67
- expected_adj_b=0.0,
68
- expected_adj_t=0.0,
69
- ),
44
+ TestData(a=0.0, b=1.0, t=9.9, expected=1.0, expected_adj_a=0.0, expected_adj_b=0.0, expected_adj_t=0.0),
45
+ TestData(a=0.0, b=1.0, t=-9.9, expected=0.0, expected_adj_a=0.0, expected_adj_b=0.0, expected_adj_t=0.0),
70
46
  ),
71
47
  }
72
48
 
@@ -83,72 +59,30 @@ def test_smoothstep(test, device):
83
59
 
84
60
  return fn
85
61
 
86
- for data_type in TEST_DATA:
62
+ for data_type, test_data_set in TEST_DATA.items():
87
63
  kernel_fn = make_kernel_fn(data_type)
88
64
  kernel = wp.Kernel(
89
65
  func=kernel_fn,
90
66
  key=f"test_smoothstep{data_type.__name__}_kernel",
91
67
  )
92
68
 
93
- for test_data in TEST_DATA[data_type]:
94
- a = wp.array(
95
- [test_data.a],
96
- dtype=data_type,
97
- device=device,
98
- requires_grad=True,
99
- )
100
- b = wp.array(
101
- [test_data.b],
102
- dtype=data_type,
103
- device=device,
104
- requires_grad=True,
105
- )
106
- t = wp.array(
107
- [test_data.t],
108
- dtype=float,
109
- device=device,
110
- requires_grad=True,
111
- )
112
- out = wp.array(
113
- [0] * wp.types.type_length(data_type),
114
- dtype=data_type,
115
- device=device,
116
- requires_grad=True,
117
- )
118
-
119
- tape = wp.Tape()
120
- with tape:
121
- wp.launch(
122
- kernel,
123
- dim=1,
124
- inputs=[a, b, t, out],
125
- device=device,
126
- )
127
-
128
- assert_np_equal(
129
- out.numpy(),
130
- np.array([test_data.expected]),
131
- tol=1e-6,
132
- )
69
+ for test_data in test_data_set:
70
+ a = wp.array([test_data.a], dtype=data_type, device=device, requires_grad=True)
71
+ b = wp.array([test_data.b], dtype=data_type, device=device, requires_grad=True)
72
+ t = wp.array([test_data.t], dtype=float, device=device, requires_grad=True)
73
+ out = wp.array([0] * wp.types.type_length(data_type), dtype=data_type, device=device, requires_grad=True)
74
+
75
+ with wp.Tape() as tape:
76
+ wp.launch(kernel, dim=1, inputs=[a, b, t, out], device=device)
77
+
78
+ assert_np_equal(out.numpy(), np.array([test_data.expected]), tol=1e-6)
133
79
 
134
80
  if test_data.check_backwards():
135
81
  tape.backward(out)
136
82
 
137
- assert_np_equal(
138
- tape.gradients[a].numpy(),
139
- np.array([test_data.expected_adj_a]),
140
- tol=1e-6,
141
- )
142
- assert_np_equal(
143
- tape.gradients[b].numpy(),
144
- np.array([test_data.expected_adj_b]),
145
- tol=1e-6,
146
- )
147
- assert_np_equal(
148
- tape.gradients[t].numpy(),
149
- np.array([test_data.expected_adj_t]),
150
- tol=1e-6,
151
- )
83
+ assert_np_equal(tape.gradients[a].numpy(), np.array([test_data.expected_adj_a]), tol=1e-6)
84
+ assert_np_equal(tape.gradients[b].numpy(), np.array([test_data.expected_adj_b]), tol=1e-6)
85
+ assert_np_equal(tape.gradients[t].numpy(), np.array([test_data.expected_adj_t]), tol=1e-6)
152
86
 
153
87
 
154
88
  devices = get_test_devices()
warp/tests/test_static.py CHANGED
@@ -281,20 +281,20 @@ def test_function_lookup(test, device):
281
281
 
282
282
  outputs = wp.empty(2, dtype=wp.float32)
283
283
 
284
- for op in op_handlers.keys():
284
+ for _op, op_func in op_handlers.items():
285
285
 
286
286
  @wp.kernel
287
287
  def operate(input: wp.array(dtype=inputs.dtype, ndim=2), output: wp.array(dtype=wp.float32)):
288
288
  tid = wp.tid()
289
289
  a, b = input[tid, 0], input[tid, 1]
290
290
  # retrieve the right function to use for the captured dtype variable
291
- output[tid] = wp.static(op_handlers[op])(a, b) # noqa: B023
291
+ output[tid] = wp.static(op_func)(a, b) # noqa: B023
292
292
 
293
293
  wp.launch(operate, dim=2, inputs=[inputs], outputs=[outputs])
294
294
  outputs_np = outputs.numpy()
295
295
  inputs_np = inputs.numpy()
296
296
  for i in range(len(outputs_np)):
297
- test.assertEqual(outputs_np[i], op_handlers[op](float(inputs_np[i][0]), float(inputs_np[i][1])))
297
+ test.assertEqual(outputs_np[i], op_func(float(inputs_np[i][0]), float(inputs_np[i][1])))
298
298
 
299
299
 
300
300
  def count_ssa_occurrences(kernel: wp.Kernel, ssas: List[str]) -> Dict[str, int]:
@@ -536,6 +536,21 @@ def test_static_function_hash(test, _):
536
536
  test.assertEqual(hash1, hash3)
537
537
 
538
538
 
539
+ @wp.kernel
540
+ def static_len_query_kernel(v1: wp.vec2):
541
+ v2 = wp.vec3()
542
+ m = wp.identity(n=wp.static(len(v1) + len(v2) + 1), dtype=float)
543
+ wp.expect_eq(wp.ddot(m, m), 6.0)
544
+
545
+ t = wp.transform_identity(float)
546
+ wp.expect_eq(wp.static(len(t)), 7)
547
+
548
+
549
+ def test_static_len_query(test, _):
550
+ v1 = wp.vec2()
551
+ wp.launch(static_len_query_kernel, 1, inputs=(v1,))
552
+
553
+
539
554
  devices = get_test_devices()
540
555
 
541
556
 
@@ -561,6 +576,7 @@ add_function_test(TestStatic, "test_static_if_else_elif", test_static_if_else_el
561
576
 
562
577
  add_function_test(TestStatic, "test_static_constant_hash", test_static_constant_hash, devices=None)
563
578
  add_function_test(TestStatic, "test_static_function_hash", test_static_function_hash, devices=None)
579
+ add_function_test(TestStatic, "test_static_len_query", test_static_len_query, devices=None)
564
580
 
565
581
 
566
582
  if __name__ == "__main__":
warp/tests/test_tape.py CHANGED
@@ -125,6 +125,30 @@ def test_tape_dot_product(test, device):
125
125
  assert_np_equal(tape.gradients[y].numpy(), x.numpy())
126
126
 
127
127
 
128
+ @wp.kernel
129
+ def assign_chain_kernel(x: wp.array(dtype=float), y: wp.array(dtype=float), z: wp.array(dtype=float)):
130
+ tid = wp.tid()
131
+ y[tid] = x[tid]
132
+ z[tid] = y[tid]
133
+
134
+
135
+ def test_tape_zero_multiple_outputs(test, device):
136
+ x = wp.array(np.arange(3), dtype=float, device=device, requires_grad=True)
137
+ y = wp.zeros_like(x)
138
+ z = wp.zeros_like(x)
139
+
140
+ tape = wp.Tape()
141
+ with tape:
142
+ wp.launch(assign_chain_kernel, dim=3, inputs=[x, y, z], device=device)
143
+
144
+ tape.backward(grads={y: wp.ones_like(x)})
145
+ assert_np_equal(x.grad.numpy(), np.ones(3, dtype=float))
146
+ tape.zero()
147
+
148
+ tape.backward(grads={z: wp.ones_like(x)})
149
+ assert_np_equal(x.grad.numpy(), np.ones(3, dtype=float))
150
+
151
+
128
152
  def test_tape_visualize(test, device):
129
153
  dim = 8
130
154
  tape = wp.Tape()
@@ -163,6 +187,7 @@ class TestTape(unittest.TestCase):
163
187
  add_function_test(TestTape, "test_tape_mul_constant", test_tape_mul_constant, devices=devices)
164
188
  add_function_test(TestTape, "test_tape_mul_variable", test_tape_mul_variable, devices=devices)
165
189
  add_function_test(TestTape, "test_tape_dot_product", test_tape_dot_product, devices=devices)
190
+ add_function_test(TestTape, "test_tape_zero_multiple_outputs", test_tape_zero_multiple_outputs, devices=devices)
166
191
  add_function_test(TestTape, "test_tape_visualize", test_tape_visualize, devices=devices)
167
192
 
168
193