Cómo crear simulaciones aceleradas por GPU de alto rendimiento y flujos de trabajo de física diferenciables utilizando los kernels Warp de NVIDIA
ángulos = np.linspace(0.0, 2.0 * np.pi, n_particles, endpoint=False, dtype=np.float32) px0_np = 0.4 * np.cos(angles).astype(np.float32) py0_np = (0.7 + 0.15 * np.sin(ángulos)).astype(np.float32) vx0_np = (-0.8 * np.sin(ángulos)).astype(np.float32) vy0_np = (0.8 * np.cos(ángulos)).astype(np.float32) px0_wp = wp.array(px0_np, dtype=wp.float32, dispositivo=dispositivo) py0_wp = wp.array(py0_np, dtype=wp.float32, dispositivo=dispositivo) vx0_wp = wp.array(vx0_np, dtype=wp.float32, dispositivo=dispositivo) vy0_wp = wp.array(vy0_np, dtype=wp.float32, dispositivo=dispositivo) state_size = (pasos + 1) * n_partículas px_wp = wp.empty(state_size, dtype=wp.float32, dispositivo=dispositivo) py_wp = wp.empty(state_size, dtype=wp.float32, dispositivo=dispositivo) vx_wp = wp.empty(state_size, dtype=wp.float32, dispositivo=dispositivo) vy_wp = wp.empty(state_size, dtype=wp.float32, dispositivo=dispositivo) wp.launch( kernel=init_particles_kernel, dim=n_particles, inputs=[n_particles, px0_wp, py0_wp, vx0_wp, vy0_wp]salidas =[px_wp, py_wp, vx_wp, vy_wp]dispositivo=dispositivo, ) wp.launch( kernel=simulate_particles_kernel, dim=pasos * n_partículas, entradas=[n_particles, dt, gravity, damping, bounce, radius]salidas =[px_wp, py_wp, vx_wp, vy_wp]dispositivo = dispositivo, ) wp.synchronize() px_traj = px_wp.numpy().reshape(pasos + 1, n_partículas) py_traj = py_wp.numpy().reshape(pasos + 1, n_partículas) sample_ids = np.linspace(0, n_partículas – 1, 16, dtype=int) plt.figure(figsize=(8, 6)) para idx en sample_ids: plt.plot(px_traj[:, idx]py_traj[:, idx]ancho de línea=1.5) plt.axhline(radius, linestyle=”–“) plt.xlim(-1.05, 1.05) plt.ylim(0.0, 1.25) plt.title(f”Trayectorias de partículas deformadas en {dispositivo}”) plt.xlabel(“x”) plt.ylabel(“y”) plt.show() proj_steps = 180 proj_dt = np.float32(0.025) proj_g = np.float32(-9.8) target_x = np.float32(3.8) target_y = np.float32(0.0) vx_value = np.float32(2.0) vy_value = np.float32(6.5) lr = 0.08 iteros = 60 pérdida_historia = []
vx_historia = []
vy_historia = []

para ello en el rango (iters): init_vx_wp = wp.array(np.array([vx_value]dtype=np.float32), dtype=wp.float32, dispositivo=dispositivo, require_grad=True) init_vy_wp = wp.array(np.array([vy_value]dtype=np.float32), dtype=wp.float32, dispositivo=dispositivo, requiere_grad=True) x_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo, requiere_grad=True) y_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo, requiere_grad=True) vx_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo, require_grad=True) vy_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo, require_grad=True) loss_wp = wp.zeros(1, dtype=wp.float32, dispositivo=dispositivo, require_grad=True) tape = wp.Tape() con cinta: wp.launch( kernel=init_projectile_kernel, dim=1, inputs=[]salidas =[x_hist_wp, y_hist_wp, vx_hist_wp, vy_hist_wp, init_vx_wp, init_vy_wp]dispositivo=dispositivo, ) wp.launch( kernel=projectile_step_kernel, dim=proj_steps, entradas=[proj_dt, proj_g]salidas =[x_hist_wp, y_hist_wp, vx_hist_wp, vy_hist_wp]dispositivo=dispositivo, ) wp.launch( kernel=projectile_loss_kernel, dim=1, entradas=[proj_steps, target_x, target_y]salidas =[x_hist_wp, y_hist_wp, loss_wp]dispositivo = dispositivo, ) tape.backward(loss=loss_wp) wp.synchronize() current_loss = float(loss_wp.numpy()[0]) grad_vx = flotante(init_vx_wp.grad.numpy()[0]) grad_vy = flotante(init_vy_wp.grad.numpy()[0]) vx_value = np.float32(vx_value – lr * grad_vx) vy_value = np.float32(vy_value – lr * grad_vy) loss_history.append(current_loss) vx_history.append(float(vx_value)) vy_history.append(float(vy_value)) si % 10 == 0 o == iteros – 1: print(f”iter={it:02d} pérdida={current_loss:.6f} vx={vx_value:.4f} vy={vy_value:.4f} grad=({grad_vx:.4f}, {grad_vy:.4f})”) final_init_vx_wp = wp.array(np.array([vx_value]dtype=np.float32), dtype=wp.float32, dispositivo=dispositivo) final_init_vy_wp = wp.array(np.array([vy_value]dtype=np.float32), dtype=wp.float32, dispositivo=dispositivo) x_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo) y_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo) vx_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo) vy_hist_wp = wp.zeros(proj_steps + 1, dtype=wp.float32, dispositivo=dispositivo) wp.launch( kernel=init_projectile_kernel, dim=1, entradas=[]salidas =[x_hist_wp, y_hist_wp, vx_hist_wp, vy_hist_wp, final_init_vx_wp, final_init_vy_wp]dispositivo=dispositivo, ) wp.launch( kernel=projectile_step_kernel, dim=proj_steps, entradas=[proj_dt, proj_g]salidas =[x_hist_wp, y_hist_wp, vx_hist_wp, vy_hist_wp]dispositivo = dispositivo, ) wp.synchronize() x_path = x_hist_wp.numpy() y_path = y_hist_wp.numpy() fig = plt.figure(figsize=(15, 4)) ax1 = fig.add_subplot(1, 3, 1) ax1.plot(loss_history) ax1.set_title(“Pérdida de optimización”) ax1.set_xlabel(“Iteración”) ax1.set_ylabel(“Distancia al cuadrado”) ax2 = fig.add_subplot(1, 3, 2) ax2.plot(vx_history, label=”vx”) ax2.plot(vy_history, label=”vy”) ax2.set_title(“Velocidad inicial aprendida”) ax2.set_xlabel(“Iteración”) ax2.legend() ax3 = fig.add_subplot(1, 3, 3) ax3.plot(x_path, y_path, linewidth=2) ax3.scatter([target_x], [target_y]s=80, marcador=”x”) ax3.set_title(“Trayectoria diferenciable del proyectil”) ax3.set_xlabel(“x”) ax3.set_ylabel(“y”) ax3.set_ylim(-0.1, max(1.0, float(np.max(y_path)) + 0.3)) plt.tight_layout() plt.show() final_dx = flotador(x_path[-1] – target_x) final_dy = float(y_path[-1] – target_y) final_dist = math.sqrt(final_dx * final_dx + final_dy * final_dy) print(f”Distancia final fallida del objetivo: {final_dist:.6f}”) print(f”Velocidad inicial optimizada: vx={vx_value:.6f}, vy={vy_value:.6f}”)