warp-lang 0.11.0__py3-none-manylinux2014_x86_64.whl → 1.0.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 (170) hide show
  1. warp/__init__.py +8 -0
  2. warp/bin/warp-clang.so +0 -0
  3. warp/bin/warp.so +0 -0
  4. warp/build.py +7 -6
  5. warp/build_dll.py +70 -79
  6. warp/builtins.py +10 -6
  7. warp/codegen.py +51 -19
  8. warp/config.py +7 -8
  9. warp/constants.py +3 -0
  10. warp/context.py +948 -245
  11. warp/dlpack.py +198 -113
  12. warp/examples/assets/bunny.usd +0 -0
  13. warp/examples/assets/cartpole.urdf +110 -0
  14. warp/examples/assets/crazyflie.usd +0 -0
  15. warp/examples/assets/cube.usda +42 -0
  16. warp/examples/assets/nv_ant.xml +92 -0
  17. warp/examples/assets/nv_humanoid.xml +183 -0
  18. warp/examples/assets/quadruped.urdf +268 -0
  19. warp/examples/assets/rocks.nvdb +0 -0
  20. warp/examples/assets/rocks.usd +0 -0
  21. warp/examples/assets/sphere.usda +56 -0
  22. warp/examples/assets/torus.usda +105 -0
  23. warp/examples/benchmarks/benchmark_api.py +383 -0
  24. warp/examples/benchmarks/benchmark_cloth.py +279 -0
  25. warp/examples/benchmarks/benchmark_cloth_cupy.py +88 -0
  26. warp/examples/benchmarks/benchmark_cloth_jax.py +100 -0
  27. warp/examples/benchmarks/benchmark_cloth_numba.py +142 -0
  28. warp/examples/benchmarks/benchmark_cloth_numpy.py +77 -0
  29. warp/examples/benchmarks/benchmark_cloth_pytorch.py +86 -0
  30. warp/examples/benchmarks/benchmark_cloth_taichi.py +112 -0
  31. warp/examples/benchmarks/benchmark_cloth_warp.py +146 -0
  32. warp/examples/benchmarks/benchmark_launches.py +295 -0
  33. warp/examples/core/example_dem.py +221 -0
  34. warp/examples/core/example_fluid.py +267 -0
  35. warp/examples/core/example_graph_capture.py +129 -0
  36. warp/examples/core/example_marching_cubes.py +177 -0
  37. warp/examples/core/example_mesh.py +154 -0
  38. warp/examples/core/example_mesh_intersect.py +193 -0
  39. warp/examples/core/example_nvdb.py +169 -0
  40. warp/examples/core/example_raycast.py +89 -0
  41. warp/examples/core/example_raymarch.py +178 -0
  42. warp/examples/core/example_render_opengl.py +141 -0
  43. warp/examples/core/example_sph.py +389 -0
  44. warp/examples/core/example_torch.py +181 -0
  45. warp/examples/core/example_wave.py +249 -0
  46. warp/examples/fem/bsr_utils.py +380 -0
  47. warp/examples/fem/example_apic_fluid.py +391 -0
  48. warp/examples/fem/example_convection_diffusion.py +168 -0
  49. warp/examples/fem/example_convection_diffusion_dg.py +209 -0
  50. warp/examples/fem/example_convection_diffusion_dg0.py +194 -0
  51. warp/examples/fem/example_deformed_geometry.py +159 -0
  52. warp/examples/fem/example_diffusion.py +173 -0
  53. warp/examples/fem/example_diffusion_3d.py +152 -0
  54. warp/examples/fem/example_diffusion_mgpu.py +214 -0
  55. warp/examples/fem/example_mixed_elasticity.py +222 -0
  56. warp/examples/fem/example_navier_stokes.py +243 -0
  57. warp/examples/fem/example_stokes.py +192 -0
  58. warp/examples/fem/example_stokes_transfer.py +249 -0
  59. warp/examples/fem/mesh_utils.py +109 -0
  60. warp/examples/fem/plot_utils.py +287 -0
  61. warp/examples/optim/example_bounce.py +248 -0
  62. warp/examples/optim/example_cloth_throw.py +210 -0
  63. warp/examples/optim/example_diffray.py +535 -0
  64. warp/examples/optim/example_drone.py +850 -0
  65. warp/examples/optim/example_inverse_kinematics.py +169 -0
  66. warp/examples/optim/example_inverse_kinematics_torch.py +170 -0
  67. warp/examples/optim/example_spring_cage.py +234 -0
  68. warp/examples/optim/example_trajectory.py +201 -0
  69. warp/examples/sim/example_cartpole.py +128 -0
  70. warp/examples/sim/example_cloth.py +184 -0
  71. warp/examples/sim/example_granular.py +113 -0
  72. warp/examples/sim/example_granular_collision_sdf.py +185 -0
  73. warp/examples/sim/example_jacobian_ik.py +213 -0
  74. warp/examples/sim/example_particle_chain.py +106 -0
  75. warp/examples/sim/example_quadruped.py +179 -0
  76. warp/examples/sim/example_rigid_chain.py +191 -0
  77. warp/examples/sim/example_rigid_contact.py +176 -0
  78. warp/examples/sim/example_rigid_force.py +126 -0
  79. warp/examples/sim/example_rigid_gyroscopic.py +97 -0
  80. warp/examples/sim/example_rigid_soft_contact.py +124 -0
  81. warp/examples/sim/example_soft_body.py +178 -0
  82. warp/fabric.py +29 -20
  83. warp/fem/cache.py +0 -1
  84. warp/fem/dirichlet.py +0 -2
  85. warp/fem/integrate.py +0 -1
  86. warp/jax.py +45 -0
  87. warp/jax_experimental.py +339 -0
  88. warp/native/builtin.h +12 -0
  89. warp/native/bvh.cu +18 -18
  90. warp/native/clang/clang.cpp +8 -3
  91. warp/native/cuda_util.cpp +94 -5
  92. warp/native/cuda_util.h +35 -6
  93. warp/native/cutlass_gemm.cpp +1 -1
  94. warp/native/cutlass_gemm.cu +4 -1
  95. warp/native/error.cpp +66 -0
  96. warp/native/error.h +27 -0
  97. warp/native/mesh.cu +2 -2
  98. warp/native/reduce.cu +4 -4
  99. warp/native/runlength_encode.cu +2 -2
  100. warp/native/scan.cu +2 -2
  101. warp/native/sparse.cu +0 -1
  102. warp/native/temp_buffer.h +2 -2
  103. warp/native/warp.cpp +95 -60
  104. warp/native/warp.cu +1053 -218
  105. warp/native/warp.h +49 -32
  106. warp/optim/linear.py +33 -16
  107. warp/render/render_opengl.py +202 -101
  108. warp/render/render_usd.py +82 -40
  109. warp/sim/__init__.py +13 -4
  110. warp/sim/articulation.py +4 -5
  111. warp/sim/collide.py +320 -175
  112. warp/sim/import_mjcf.py +25 -30
  113. warp/sim/import_urdf.py +94 -63
  114. warp/sim/import_usd.py +51 -36
  115. warp/sim/inertia.py +3 -2
  116. warp/sim/integrator.py +233 -0
  117. warp/sim/integrator_euler.py +447 -469
  118. warp/sim/integrator_featherstone.py +1991 -0
  119. warp/sim/integrator_xpbd.py +1420 -640
  120. warp/sim/model.py +765 -487
  121. warp/sim/particles.py +2 -1
  122. warp/sim/render.py +35 -13
  123. warp/sim/utils.py +222 -11
  124. warp/stubs.py +8 -0
  125. warp/tape.py +16 -1
  126. warp/tests/aux_test_grad_customs.py +23 -0
  127. warp/tests/test_array.py +190 -1
  128. warp/tests/test_async.py +656 -0
  129. warp/tests/test_bool.py +50 -0
  130. warp/tests/test_dlpack.py +164 -11
  131. warp/tests/test_examples.py +166 -74
  132. warp/tests/test_fem.py +8 -1
  133. warp/tests/test_generics.py +15 -5
  134. warp/tests/test_grad.py +1 -1
  135. warp/tests/test_grad_customs.py +172 -12
  136. warp/tests/test_jax.py +254 -0
  137. warp/tests/test_large.py +29 -6
  138. warp/tests/test_launch.py +25 -0
  139. warp/tests/test_linear_solvers.py +20 -3
  140. warp/tests/test_matmul.py +61 -16
  141. warp/tests/test_matmul_lite.py +13 -13
  142. warp/tests/test_mempool.py +186 -0
  143. warp/tests/test_multigpu.py +3 -0
  144. warp/tests/test_options.py +16 -2
  145. warp/tests/test_peer.py +137 -0
  146. warp/tests/test_print.py +3 -1
  147. warp/tests/test_quat.py +23 -0
  148. warp/tests/test_sim_kinematics.py +97 -0
  149. warp/tests/test_snippet.py +126 -3
  150. warp/tests/test_streams.py +108 -79
  151. warp/tests/test_torch.py +16 -8
  152. warp/tests/test_utils.py +32 -27
  153. warp/tests/test_verify_fp.py +65 -0
  154. warp/tests/test_volume.py +1 -1
  155. warp/tests/unittest_serial.py +2 -0
  156. warp/tests/unittest_suites.py +12 -0
  157. warp/tests/unittest_utils.py +14 -7
  158. warp/thirdparty/unittest_parallel.py +15 -3
  159. warp/torch.py +10 -8
  160. warp/types.py +363 -246
  161. warp/utils.py +143 -19
  162. warp_lang-1.0.0.dist-info/LICENSE.md +126 -0
  163. warp_lang-1.0.0.dist-info/METADATA +394 -0
  164. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/RECORD +167 -86
  165. warp/sim/optimizer.py +0 -138
  166. warp_lang-0.11.0.dist-info/LICENSE.md +0 -36
  167. warp_lang-0.11.0.dist-info/METADATA +0 -238
  168. /warp/tests/{walkthough_debug.py → walkthrough_debug.py} +0 -0
  169. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/WHEEL +0 -0
  170. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,169 @@
1
+ # Copyright (c) 2022 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
+ ###########################################################################
9
+ # Example Sim Rigid Kinematics
10
+ #
11
+ # Tests rigid body forward and backwards kinematics through the
12
+ # wp.sim.eval_ik() and wp.sim.eval_fk() methods.
13
+ #
14
+ ###########################################################################
15
+
16
+ import os
17
+
18
+ import numpy as np
19
+
20
+ import warp as wp
21
+ import warp.sim
22
+ import warp.sim.render
23
+
24
+ wp.init()
25
+
26
+ TARGET = wp.constant(wp.vec3(2.0, 1.0, 0.0))
27
+
28
+
29
+ @wp.kernel
30
+ def compute_loss(body_q: wp.array(dtype=wp.transform), body_index: int, loss: wp.array(dtype=float)):
31
+ x = wp.transform_get_translation(body_q[body_index])
32
+
33
+ delta = x - TARGET
34
+ loss[0] = wp.dot(delta, delta)
35
+
36
+
37
+ @wp.kernel
38
+ def step_kernel(x: wp.array(dtype=float), grad: wp.array(dtype=float), alpha: float):
39
+ tid = wp.tid()
40
+
41
+ # gradient descent step
42
+ x[tid] = x[tid] - grad[tid] * alpha
43
+
44
+
45
+ class Example:
46
+ def __init__(self, stage, device=None, verbose=False):
47
+ self.verbose = verbose
48
+ if device is None:
49
+ self.device = wp.get_device()
50
+ else:
51
+ self.device = device
52
+
53
+ self.frame_dt = 1.0 / 60.0
54
+ self.render_time = 0.0
55
+
56
+ builder = wp.sim.ModelBuilder()
57
+ builder.add_articulation()
58
+
59
+ chain_length = 4
60
+ chain_width = 1.0
61
+
62
+ for i in range(chain_length):
63
+ if i == 0:
64
+ parent = -1
65
+ parent_joint_xform = wp.transform([0.0, 0.0, 0.0], wp.quat_identity())
66
+ else:
67
+ parent = builder.joint_count - 1
68
+ parent_joint_xform = wp.transform([chain_width, 0.0, 0.0], wp.quat_identity())
69
+
70
+ # create body
71
+ b = builder.add_body(origin=wp.transform([i, 0.0, 0.0], wp.quat_identity()), armature=0.1)
72
+
73
+ builder.add_joint_revolute(
74
+ parent=parent,
75
+ child=b,
76
+ axis=(0.0, 0.0, 1.0),
77
+ parent_xform=parent_joint_xform,
78
+ child_xform=wp.transform_identity(),
79
+ limit_lower=-np.deg2rad(60.0),
80
+ limit_upper=np.deg2rad(60.0),
81
+ target_ke=0.0,
82
+ target_kd=0.0,
83
+ limit_ke=30.0,
84
+ limit_kd=30.0,
85
+ )
86
+
87
+ if i == chain_length - 1:
88
+ # create end effector
89
+ builder.add_shape_sphere(pos=wp.vec3(0.0, 0.0, 0.0), radius=0.1, density=10.0, body=b)
90
+
91
+ else:
92
+ # create shape
93
+ builder.add_shape_box(
94
+ pos=wp.vec3(chain_width * 0.5, 0.0, 0.0), hx=chain_width * 0.5, hy=0.1, hz=0.1, density=10.0, body=b
95
+ )
96
+
97
+ # finalize model
98
+ self.model = builder.finalize(self.device)
99
+ self.model.ground = False
100
+
101
+ self.state = self.model.state()
102
+
103
+ self.renderer = None
104
+ if stage:
105
+ self.renderer = wp.sim.render.SimRenderer(self.model, stage, scaling=50.0)
106
+
107
+ # optimization variables
108
+ self.loss = wp.zeros(1, dtype=float, device=self.device)
109
+
110
+ self.model.joint_q.requires_grad = True
111
+ self.state.body_q.requires_grad = True
112
+ self.loss.requires_grad = True
113
+
114
+ self.train_rate = 0.01
115
+
116
+ def forward(self):
117
+ wp.sim.eval_fk(self.model, self.model.joint_q, self.model.joint_qd, None, self.state)
118
+
119
+ wp.launch(
120
+ compute_loss,
121
+ dim=1,
122
+ inputs=[self.state.body_q, len(self.state.body_q) - 1, self.loss],
123
+ device=self.device,
124
+ )
125
+
126
+ def step(self):
127
+ tape = wp.Tape()
128
+ with tape:
129
+ self.forward()
130
+ tape.backward(loss=self.loss)
131
+
132
+ if self.verbose:
133
+ print(f"loss: {self.loss}")
134
+ print(f"joint_grad: {tape.gradients[self.model.joint_q]}")
135
+
136
+ # gradient descent
137
+ wp.launch(
138
+ step_kernel,
139
+ dim=len(self.model.joint_q),
140
+ inputs=[self.model.joint_q, tape.gradients[self.model.joint_q], self.train_rate],
141
+ device=self.device,
142
+ )
143
+
144
+ # zero gradients
145
+ tape.zero()
146
+
147
+ def render(self):
148
+ if self.renderer is None:
149
+ return
150
+
151
+ self.renderer.begin_frame(self.render_time)
152
+ self.renderer.render(self.state)
153
+ self.renderer.render_sphere(name="target", pos=TARGET, rot=wp.quat_identity(), radius=0.1, color=(1.0, 0.0, 0.0))
154
+ self.renderer.end_frame()
155
+ self.render_time += self.frame_dt
156
+
157
+
158
+ if __name__ == "__main__":
159
+ stage_path = os.path.join(os.path.dirname(__file__), "example_inverse_kinematics.usd")
160
+ example = Example(stage_path, device=wp.get_preferred_device(), verbose=True)
161
+
162
+ train_iters = 512
163
+
164
+ for _ in range(train_iters):
165
+ example.step()
166
+ example.render()
167
+
168
+ if example.renderer:
169
+ example.renderer.save()
@@ -0,0 +1,170 @@
1
+ # Copyright (c) 2022 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
+ ###########################################################################
9
+ # Example Sim Rigid Kinematics
10
+ #
11
+ # Tests rigid body forward and backwards kinematics through the
12
+ # wp.sim.eval_ik() and wp.sim.eval_fk() methods. Shows how to connect
13
+ # gradients from Warp to PyTorch, through custom autograd nodes.
14
+ #
15
+ ###########################################################################
16
+
17
+ import os
18
+
19
+ import numpy as np
20
+ import torch
21
+
22
+ import warp as wp
23
+ import warp.sim
24
+ import warp.sim.render
25
+
26
+ wp.init()
27
+
28
+
29
+ class ForwardKinematics(torch.autograd.Function):
30
+ @staticmethod
31
+ def forward(ctx, joint_q, joint_qd, model):
32
+
33
+ ctx.tape = wp.Tape()
34
+ ctx.model = model
35
+ ctx.joint_q = wp.from_torch(joint_q)
36
+ ctx.joint_qd = wp.from_torch(joint_qd)
37
+
38
+ # allocate output
39
+ ctx.state = model.state()
40
+
41
+ with ctx.tape:
42
+ wp.sim.eval_fk(model, ctx.joint_q, ctx.joint_qd, None, ctx.state)
43
+
44
+ return (wp.to_torch(ctx.state.body_q), wp.to_torch(ctx.state.body_qd))
45
+
46
+ @staticmethod
47
+ def backward(ctx, adj_body_q, adj_body_qd):
48
+
49
+ # map incoming Torch grads to our output variables
50
+ ctx.state.body_q.grad = wp.from_torch(adj_body_q, dtype=wp.transform)
51
+ ctx.state.body_qd.grad = wp.from_torch(adj_body_qd, dtype=wp.spatial_vector)
52
+
53
+ ctx.tape.backward()
54
+
55
+ # return adjoint w.r.t. inputs
56
+ return (wp.to_torch(ctx.tape.gradients[ctx.joint_q]), wp.to_torch(ctx.tape.gradients[ctx.joint_qd]), None)
57
+
58
+
59
+ class Example:
60
+ def __init__(self, stage, device=None, verbose=False):
61
+ self.verbose = verbose
62
+
63
+ self.frame_dt = 1.0 / 60.0
64
+ self.render_time = 0.0
65
+
66
+ builder = wp.sim.ModelBuilder()
67
+ builder.add_articulation()
68
+
69
+ chain_length = 4
70
+ chain_width = 1.0
71
+
72
+ for i in range(chain_length):
73
+ if i == 0:
74
+ parent = -1
75
+ parent_joint_xform = wp.transform([0.0, 0.0, 0.0], wp.quat_identity())
76
+ else:
77
+ parent = builder.joint_count - 1
78
+ parent_joint_xform = wp.transform([chain_width, 0.0, 0.0], wp.quat_identity())
79
+
80
+ # create body
81
+ b = builder.add_body(origin=wp.transform([i, 0.0, 0.0], wp.quat_identity()), armature=0.1)
82
+
83
+ builder.add_joint_revolute(
84
+ parent=parent,
85
+ child=b,
86
+ axis=wp.vec3(0.0, 0.0, 1.0),
87
+ parent_xform=parent_joint_xform,
88
+ child_xform=wp.transform_identity(),
89
+ limit_lower=-np.deg2rad(60.0),
90
+ limit_upper=np.deg2rad(60.0),
91
+ target_ke=0.0,
92
+ target_kd=0.0,
93
+ limit_ke=30.0,
94
+ limit_kd=30.0,
95
+ )
96
+
97
+ if i == chain_length - 1:
98
+ # create end effector
99
+ builder.add_shape_sphere(pos=wp.vec3(0.0, 0.0, 0.0), radius=0.1, density=10.0, body=b)
100
+
101
+ else:
102
+ # create shape
103
+ builder.add_shape_box(
104
+ pos=wp.vec3(chain_width * 0.5, 0.0, 0.0), hx=chain_width * 0.5, hy=0.1, hz=0.1, density=10.0, body=b
105
+ )
106
+
107
+ # finalize model
108
+ self.model = builder.finalize(device)
109
+ self.model.ground = False
110
+
111
+ self.torch_device = wp.device_to_torch(self.model.device)
112
+
113
+ self.renderer = None
114
+ if stage:
115
+ self.renderer = wp.sim.render.SimRenderer(self.model, stage, scaling=50.0)
116
+
117
+ self.target = torch.from_numpy(np.array((2.0, 1.0, 0.0))).to(self.torch_device)
118
+
119
+ self.body_q = None
120
+ self.body_qd = None
121
+
122
+ # optimization variable
123
+ self.joint_q = torch.zeros(len(self.model.joint_q), requires_grad=True, device=self.torch_device)
124
+ self.joint_qd = torch.zeros(len(self.model.joint_qd), requires_grad=True, device=self.torch_device)
125
+
126
+ self.train_rate = 0.01
127
+
128
+ def forward(self):
129
+ (self.body_q, self.body_qd) = ForwardKinematics.apply(self.joint_q, self.joint_qd, self.model)
130
+ self.loss = torch.norm(self.body_q[self.model.body_count - 1][0:3] - self.target) ** 2.0
131
+
132
+ def step(self):
133
+ self.forward()
134
+ self.loss.backward()
135
+
136
+ if self.verbose:
137
+ print(f"loss: {self.loss}")
138
+ print(f"loss: {self.joint_q.grad}")
139
+
140
+ with torch.no_grad():
141
+ self.joint_q -= self.joint_q.grad * self.train_rate
142
+ self.joint_q.grad.zero_()
143
+
144
+ def render(self):
145
+ if self.renderer is None:
146
+ return
147
+
148
+ s = self.model.state()
149
+ s.body_q = wp.from_torch(self.body_q, dtype=wp.transform, requires_grad=False)
150
+ s.body_qd = wp.from_torch(self.body_qd, dtype=wp.spatial_vector, requires_grad=False)
151
+
152
+ self.renderer.begin_frame(self.render_time)
153
+ self.renderer.render(s)
154
+ self.renderer.render_sphere(name="target", pos=self.target, rot=wp.quat_identity(), radius=0.1, color=(1.0, 0.0, 0.0))
155
+ self.renderer.end_frame()
156
+ self.render_time += self.frame_dt
157
+
158
+
159
+ if __name__ == "__main__":
160
+ stage_path = os.path.join(os.path.dirname(__file__), "example_inverse_kinematics_torch.usd")
161
+ example = Example(stage_path, device=wp.get_preferred_device(), verbose=True)
162
+
163
+ train_iters = 512
164
+
165
+ for _ in range(train_iters):
166
+ example.step()
167
+ example.render()
168
+
169
+ if example.renderer:
170
+ example.renderer.save()
@@ -0,0 +1,234 @@
1
+ # Copyright (c) 2023 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
+ ###########################################################################
9
+ # Example Diff Sim Spring Cage
10
+ #
11
+ # A single particle is attached with springs to each point of a cage.
12
+ # The objective is to optimize the rest length of the springs in order
13
+ # for the particle to be pulled towards a target position.
14
+ #
15
+ ###########################################################################
16
+
17
+ import os
18
+
19
+ import numpy as np
20
+ import warp as wp
21
+
22
+
23
+ wp.init()
24
+
25
+
26
+ @wp.kernel
27
+ def compute_loss_kernel(
28
+ pos: wp.array(dtype=wp.vec3),
29
+ target_pos: wp.vec3,
30
+ loss: wp.array(dtype=float),
31
+ ):
32
+ loss[0] = wp.length_sq(pos[0] - target_pos)
33
+
34
+
35
+ @wp.kernel(enable_backward=False)
36
+ def apply_gradient_kernel(
37
+ spring_rest_lengths_grad: wp.array(dtype=float),
38
+ train_rate: float,
39
+ spring_rest_lengths: wp.array(dtype=float),
40
+ ):
41
+ tid = wp.tid()
42
+
43
+ spring_rest_lengths[tid] -= spring_rest_lengths_grad[tid] * train_rate
44
+
45
+
46
+ class Example:
47
+ def __init__(
48
+ self,
49
+ stage=None,
50
+ verbose=False,
51
+ ):
52
+ import warp.sim
53
+
54
+ # Duration of the simulation, in seconds.
55
+ duration = 1.0
56
+
57
+ # Number of frames per second.
58
+ self.fps = 30.0
59
+
60
+ # Duration of a single simulation iteration in number of frames.
61
+ self.frame_count = int(duration * self.fps)
62
+
63
+ # Number of simulation steps to take per frame.
64
+ self.sim_substep_count = 1
65
+
66
+ # Delta time between each simulation substep.
67
+ self.sim_dt = 1.0 / (self.fps * self.sim_substep_count)
68
+
69
+ # Target position that we want the main particle to reach by optimising
70
+ # the rest lengths of the springs.
71
+ self.target_pos = (0.125, 0.25, 0.375)
72
+
73
+ # Number of training iterations.
74
+ self.train_iters = 100
75
+
76
+ # Factor by which the rest lengths of the springs are adjusted after each
77
+ # iteration, relatively to the corresponding gradients. Lower values
78
+ # converge more slowly but have less chances to miss the local minimum.
79
+ self.train_rate = 0.5
80
+
81
+ # Initialize the helper to build a physics scene.
82
+ builder = warp.sim.ModelBuilder()
83
+
84
+ # Define the main particle at the origin.
85
+ particle_mass = 1.0
86
+ builder.add_particle((0.0, 0.0, 0.0), (0.0, 0.0, 0.0), particle_mass)
87
+
88
+ # Define the cage made of points that will be pulling our main particle
89
+ # using springs.
90
+ # fmt: off
91
+ builder.add_particle((-0.7, 0.8, 0.2), (0.0, 0.0, 0.0), 0.0)
92
+ builder.add_particle(( 0.0, 0.2, 1.1), (0.0, 0.0, 0.0), 0.0)
93
+ builder.add_particle(( 0.1, 0.1, -1.2), (0.0, 0.0, 0.0), 0.0)
94
+ builder.add_particle(( 0.6, 0.4, 0.4), (0.0, 0.0, 0.0), 0.0)
95
+ builder.add_particle(( 0.7, -0.9, -0.2), (0.0, 0.0, 0.0), 0.0)
96
+ builder.add_particle((-0.8, -0.8, 0.1), (0.0, 0.0, 0.0), 0.0)
97
+ builder.add_particle((-0.9, 0.2, -0.8), (0.0, 0.0, 0.0), 0.0)
98
+ builder.add_particle(( 1.0, 0.4, -0.1), (0.0, 0.0, 0.0), 0.0)
99
+ # fmt: on
100
+
101
+ # Define the spring constraints between the main particle and the cage points.
102
+ spring_elastic_stiffness = 100.0
103
+ spring_elastic_damping = 10.0
104
+ for i in range(1, builder.particle_count):
105
+ builder.add_spring(0, i, spring_elastic_stiffness, spring_elastic_damping, 0)
106
+
107
+ # Build the model and set-up its properties.
108
+ self.model = builder.finalize(requires_grad=True)
109
+ self.model.gravity = np.array((0.0, 0.0, 0.0))
110
+ self.model.ground = False
111
+
112
+ # Use the Euler integrator for stepping through the simulation.
113
+ self.integrator = warp.sim.SemiImplicitIntegrator()
114
+
115
+ # Initialize a state for each simulation step.
116
+ self.states = tuple(self.model.state() for _ in range(self.frame_count * self.sim_substep_count + 1))
117
+
118
+ # Initialize a loss value that will represent the distance of the main
119
+ # particle to the target position. It needs to be defined as an array
120
+ # so that it can be written out by a kernel.
121
+ self.loss = wp.zeros(1, dtype=float, requires_grad=True)
122
+
123
+ if stage:
124
+ import warp.sim.render
125
+
126
+ # Helper to render the physics scene as a USD file.
127
+ self.renderer = warp.sim.render.SimRenderer(self.model, stage, fps=self.fps, scaling=10.0)
128
+
129
+ # Allows rendering one simulation to USD every N training iterations.
130
+ self.render_iteration_steps = 2
131
+
132
+ # Frame number used to render the simulation iterations onto the USD file.
133
+ self.render_frame = 0
134
+ else:
135
+ self.renderer = None
136
+
137
+ self.use_graph = wp.get_device().is_cuda
138
+ if self.use_graph:
139
+ # Capture all the kernel launches into a CUDA graph so that they can
140
+ # all be run in a single graph launch, which helps with performance.
141
+ with wp.ScopedCapture() as capture:
142
+ self.tape = wp.Tape()
143
+ with self.tape:
144
+ self.forward()
145
+ self.tape.backward(loss=self.loss)
146
+ self.graph = capture.graph
147
+
148
+ self.verbose = verbose
149
+
150
+ def forward(self):
151
+ for i in range(1, len(self.states)):
152
+ prev = self.states[i - 1]
153
+ curr = self.states[i]
154
+ prev.clear_forces()
155
+ self.integrator.simulate(
156
+ self.model,
157
+ prev,
158
+ curr,
159
+ self.sim_dt,
160
+ )
161
+
162
+ last_state = self.states[-1]
163
+ wp.launch(
164
+ compute_loss_kernel,
165
+ dim=1,
166
+ inputs=(
167
+ last_state.particle_q,
168
+ self.target_pos,
169
+ ),
170
+ outputs=(self.loss,),
171
+ )
172
+
173
+ def step(self):
174
+ if self.use_graph:
175
+ wp.capture_launch(self.graph)
176
+ else:
177
+ self.tape = wp.Tape()
178
+ with self.tape:
179
+ self.forward()
180
+ self.tape.backward(loss=self.loss)
181
+
182
+ wp.launch(
183
+ apply_gradient_kernel,
184
+ dim=self.model.spring_count,
185
+ inputs=(
186
+ self.model.spring_rest_length.grad,
187
+ self.train_rate,
188
+ ),
189
+ outputs=(self.model.spring_rest_length,),
190
+ )
191
+
192
+ self.tape.zero()
193
+
194
+ def render(self):
195
+ if self.renderer is None:
196
+ return
197
+
198
+ self.renderer.begin_frame(0.0)
199
+ self.renderer.render_box(
200
+ name="target",
201
+ pos=self.target_pos,
202
+ rot=wp.quat_identity(),
203
+ extents=(0.1, 0.1, 0.1),
204
+ color=(1.0, 0.0, 0.0),
205
+ )
206
+ self.renderer.end_frame()
207
+
208
+ for frame in range(self.frame_count):
209
+ self.renderer.begin_frame(self.render_frame / self.fps)
210
+ self.renderer.render(self.states[frame * self.sim_substep_count])
211
+ self.renderer.end_frame()
212
+
213
+ self.render_frame += 1
214
+
215
+
216
+ if __name__ == "__main__":
217
+
218
+ stage_path = os.path.join(os.path.dirname(__file__), "example_spring_cage.usd")
219
+
220
+ example = Example(stage_path, verbose=True)
221
+
222
+ for iteration in range(example.train_iters):
223
+ example.step()
224
+
225
+ loss = example.loss.numpy()[0]
226
+
227
+ if example.verbose:
228
+ print(f"[{iteration:3d}] loss={loss:.8f}")
229
+
230
+ if iteration == example.train_iters - 1 or iteration % example.render_iteration_steps == 0:
231
+ example.render()
232
+
233
+ if example.renderer:
234
+ example.renderer.save()