openscvx 0.5.3.dev16__tar.gz → 0.5.3.dev18__tar.gz

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.
Files changed (387) hide show
  1. {openscvx-0.5.3.dev16/openscvx.egg-info → openscvx-0.5.3.dev18}/PKG-INFO +1 -1
  2. openscvx-0.5.3.dev18/docs/UnderTheHood/batching_jit_grad.md +134 -0
  3. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UnderTheHood/lowering_architecture.md +7 -2
  4. openscvx-0.5.3.dev18/examples/abstract/brachistochrone_batched.py +126 -0
  5. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/arm/7_dof_arm.py +1 -1
  6. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/rocket/3DoF_pdg.py +17 -12
  7. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/spacecraft/let_transfer.py +1 -2
  8. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/mkdocs.yml +2 -1
  9. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/_version.py +3 -3
  10. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/optimization_results.py +233 -1
  11. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/scvx/iteration.py +18 -1
  12. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/problem.py +141 -46
  13. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/solvers/moreau_ptr_solver.py +16 -3
  14. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18/openscvx.egg-info}/PKG-INFO +1 -1
  15. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx.egg-info/SOURCES.txt +6 -0
  16. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_brachistochrone.py +1 -1
  17. openscvx-0.5.3.dev18/tests/test_solve_jax_bare_brachistochrone.py +70 -0
  18. openscvx-0.5.3.dev18/tests/test_solve_jax_jit_brachistochrone.py +36 -0
  19. openscvx-0.5.3.dev18/tests/test_solve_jax_vmap_brachistochrone.py +51 -0
  20. openscvx-0.5.3.dev18/tests/test_solve_jax_vmap_converged_no_drift.py +128 -0
  21. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/assets/logo.svg +0 -0
  22. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/release-drafter.yml +0 -0
  23. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/_docs.yml +0 -0
  24. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/branch-name.yml +0 -0
  25. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/docs.yml +0 -0
  26. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/lint.yml +0 -0
  27. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/nightly.yml +0 -0
  28. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/release-drafter.yml +0 -0
  29. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/release.yml +0 -0
  30. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/tests-integration.yml +0 -0
  31. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.github/workflows/tests-unit.yml +0 -0
  32. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.gitignore +0 -0
  33. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/.gitmodules +0 -0
  34. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/CONTRIBUTING.md +0 -0
  35. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/LICENSE +0 -0
  36. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/README.md +0 -0
  37. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/Foundations/constraint_reformulation.md +0 -0
  38. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/Foundations/control_parameterization.md +0 -0
  39. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/Foundations/discretization.md +0 -0
  40. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/Foundations/ocp.md +0 -0
  41. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/Foundations/scvx.md +0 -0
  42. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/Foundations/time_dilation.md +0 -0
  43. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  44. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/00_introduction.md +0 -0
  45. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  46. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  47. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  48. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  49. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/05_visualization.md +0 -0
  50. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/06_logic.md +0 -0
  51. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/07_lie.md +0 -0
  52. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/08_mpcc.md +0 -0
  53. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
  54. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/favicon.png +0 -0
  55. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/images/ct-scvx_dark.png +0 -0
  56. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/images/ct-scvx_light.png +0 -0
  57. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/images/ctcs_dark.png +0 -0
  58. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/images/ctcs_light.png +0 -0
  59. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/images/problem_class_dark.png +0 -0
  60. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/images/problem_class_light.png +0 -0
  61. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/logo.svg +0 -0
  62. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/openscvx_logo_square.png +0 -0
  63. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/viser-client/index.html +0 -0
  64. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/viser-recordings/drone_racing.viser +0 -0
  65. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
  66. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/citation.md +0 -0
  67. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/examples.md +0 -0
  68. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/index.md +0 -0
  69. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/javascripts/mathjax.js +0 -0
  70. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/docs/versions.json +0 -0
  71. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/_viser_embed_export.py +0 -0
  72. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/abstract/brachistochrone.py +0 -0
  73. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/abstract/hypersensitive.py +0 -0
  74. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/abstract/impulsive.py +0 -0
  75. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/abstract/stl_integer_variable.py +0 -0
  76. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/abstract/stl_or.py +0 -0
  77. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/7_dof_arm.py +0 -0
  78. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/_camera.py +0 -0
  79. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/_render.py +0 -0
  80. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/_sensor_view.py +0 -0
  81. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/dr_vp_polytope.py +0 -0
  82. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/franka_fr3v2_pick_place.py +0 -0
  83. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/logo.py +0 -0
  84. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/animations/obstacle_avoidance_vmap.py +0 -0
  85. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/arm/3_dof_arm.py +0 -0
  86. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/arm/7_dof_arm_collision.py +0 -0
  87. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/arm/7_dof_arm_vp.py +0 -0
  88. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/arm/franka_fr3v2_pick_place.py +0 -0
  89. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
  90. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/car/dubins_car.py +0 -0
  91. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/car/dubins_car_disjoint.py +0 -0
  92. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/car/dubins_car_obstacle_conditional.py +0 -0
  93. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/car/dubins_car_obstacle_stl.py +0 -0
  94. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/car/dubins_car_stl_or.py +0 -0
  95. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/car/dubins_car_waypoint_stl.py +0 -0
  96. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/cinema_vp.py +0 -0
  97. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/dr_double_integrator.py +0 -0
  98. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/dr_vp.py +0 -0
  99. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/dr_vp_nodal.py +0 -0
  100. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/dr_vp_polytope.py +0 -0
  101. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/drone_racing.py +0 -0
  102. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/logo.py +0 -0
  103. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/logo_utils/acl_logo.svg +0 -0
  104. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/logo_utils/svg_path_utils.py +0 -0
  105. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/obstacle_avoidance.py +0 -0
  106. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  107. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  108. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mjx/cartpole_mjx.py +0 -0
  109. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mjx/double_cartpole_mjx.py +0 -0
  110. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mjx/skydio_x2_mjx.py +0 -0
  111. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
  112. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mjx/triple_cartpole_game.py +0 -0
  113. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mjx/triple_cartpole_mjx.py +0 -0
  114. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mpc/double_integrator_discrete.py +0 -0
  115. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mpc/double_integrator_drone_racing.py +0 -0
  116. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mpc/dubins_car_circle_analytical.py +0 -0
  117. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mpc/dubins_car_circle_discrete.py +0 -0
  118. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
  119. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/plotting.py +0 -0
  120. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/plotting_viser.py +0 -0
  121. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/3DoF_pdg_realtime.py +0 -0
  122. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/6DoF_pdg_realtime.py +0 -0
  123. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
  124. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
  125. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
  126. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
  127. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
  128. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
  129. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/cinema_vp_realtime.py +0 -0
  130. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/drone_racing_realtime.py +0 -0
  131. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/dubins_car_realtime.py +0 -0
  132. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  133. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/rocket/6DoF_pdg.py +0 -0
  134. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/spacecraft/halo_orbit.py +0 -0
  135. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/spacecraft/hohmann_transfer.py +0 -0
  136. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/spacecraft/proxops_cw.py +0 -0
  137. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/examples/spacecraft/relative_loitering.py +0 -0
  138. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/ctlos_cine.gif +0 -0
  139. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/ctlos_dr.gif +0 -0
  140. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/dtlos_cine.gif +0 -0
  141. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/dtlos_dr.gif +0 -0
  142. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/openscvx_logo.svg +0 -0
  143. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/openscvx_logo_square.png +0 -0
  144. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/oscvx_structure_full_dark.svg +0 -0
  145. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/figures/video_preview.png +0 -0
  146. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/__init__.py +0 -0
  147. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/assets/stylesheets/custom.css +0 -0
  148. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
  149. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/assets/stylesheets/home-hero.css +0 -0
  150. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/assets/stylesheets/home-viser.css +0 -0
  151. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/home.html +0 -0
  152. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/main.html +0 -0
  153. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/partials/home-diagram.html +0 -0
  154. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/partials/home-dropin-banner.html +0 -0
  155. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/partials/home-hero.html +0 -0
  156. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/partials/home-pipeline.html +0 -0
  157. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/material/overrides/partials/home-viser-strip.html +0 -0
  158. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/__init__.py +0 -0
  159. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/__main__.py +0 -0
  160. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/__init__.py +0 -0
  161. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/autotuner/__init__.py +0 -0
  162. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
  163. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
  164. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
  165. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
  166. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/base.py +0 -0
  167. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/scvx/__init__.py +0 -0
  168. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
  169. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/algorithms/weights.py +0 -0
  170. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/config.py +0 -0
  171. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/__init__.py +0 -0
  172. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/base.py +0 -0
  173. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/discretize_linearize.py +0 -0
  174. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/linearize_discretize.py +0 -0
  175. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
  176. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/sparse_utils/__init__.py +0 -0
  177. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
  178. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
  179. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/expert/__init__.py +0 -0
  180. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/expert/byof.py +0 -0
  181. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/expert/lowering.py +0 -0
  182. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/expert/validation.py +0 -0
  183. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/init/__init__.py +0 -0
  184. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/init/interpolation.py +0 -0
  185. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/init/inverse_kinematics.py +0 -0
  186. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/integrations/__init__.py +0 -0
  187. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/integrations/base.py +0 -0
  188. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/integrations/menagerie.py +0 -0
  189. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/integrations/mjx.py +0 -0
  190. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/integrators/__init__.py +0 -0
  191. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/integrators/diffrax.py +0 -0
  192. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/integrators/runge_kutta.py +0 -0
  193. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/loader.py +0 -0
  194. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/__init__.py +0 -0
  195. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/cvxpy_constraints.py +0 -0
  196. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/cvxpy_variables.py +0 -0
  197. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/dynamics.py +0 -0
  198. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/jax_constraints.py +0 -0
  199. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/parameters.py +0 -0
  200. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/problem.py +0 -0
  201. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/lowered/unified.py +0 -0
  202. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/__init__.py +0 -0
  203. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/plotting.py +0 -0
  204. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/scp_iteration.py +0 -0
  205. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/viser/__init__.py +0 -0
  206. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/viser/animated.py +0 -0
  207. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/viser/orbits.py +0 -0
  208. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/viser/plotly_integration.py +0 -0
  209. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/viser/primitives.py +0 -0
  210. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/viser/scp.py +0 -0
  211. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/plotting/viser/server.py +0 -0
  212. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/propagation/__init__.py +0 -0
  213. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/propagation/post_processing.py +0 -0
  214. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/propagation/propagation.py +0 -0
  215. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/solvers/__init__.py +0 -0
  216. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/solvers/base.py +0 -0
  217. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
  218. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/solvers/ptr_solver.py +0 -0
  219. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/solvers/qpax_ptr_solver.py +0 -0
  220. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/__init__.py +0 -0
  221. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/augmentation.py +0 -0
  222. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/builder.py +0 -0
  223. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/constraint_set.py +0 -0
  224. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/__init__.py +0 -0
  225. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/arithmetic.py +0 -0
  226. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/array.py +0 -0
  227. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/constraint.py +0 -0
  228. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/control.py +0 -0
  229. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/expr.py +0 -0
  230. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  231. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  232. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/lie/se3.py +0 -0
  233. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/lie/so3.py +0 -0
  234. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/linalg.py +0 -0
  235. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/logic.py +0 -0
  236. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/math.py +0 -0
  237. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/parameter.py +0 -0
  238. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/spatial.py +0 -0
  239. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/state.py +0 -0
  240. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/stl.py +0 -0
  241. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/stljax.py +0 -0
  242. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/time.py +0 -0
  243. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/variable.py +0 -0
  244. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/expr/vmap.py +0 -0
  245. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/hashing.py +0 -0
  246. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lower.py +0 -0
  247. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/__init__.py +0 -0
  248. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  249. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  250. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  251. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  252. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  253. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  254. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  255. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  256. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  257. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  258. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  259. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  260. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  261. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  262. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  263. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  264. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  265. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
  266. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  267. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  268. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  269. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  270. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
  271. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  272. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  273. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  274. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  275. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
  276. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  277. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/__init__.py +0 -0
  278. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/_registry.py +0 -0
  279. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/array.py +0 -0
  280. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/constraint.py +0 -0
  281. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/lie.py +0 -0
  282. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/linalg.py +0 -0
  283. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/logic.py +0 -0
  284. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/math.py +0 -0
  285. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/parser.py +0 -0
  286. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/spatial.py +0 -0
  287. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/stl.py +0 -0
  288. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/stljax.py +0 -0
  289. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/parser/tokenizer.py +0 -0
  290. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/preprocessing.py +0 -0
  291. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/problem.py +0 -0
  292. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/sparsity.py +0 -0
  293. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/symbolic/unified.py +0 -0
  294. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/utils/__init__.py +0 -0
  295. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/utils/cache.py +0 -0
  296. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/utils/caching.py +0 -0
  297. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/utils/printing.py +0 -0
  298. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/utils/profiling.py +0 -0
  299. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx/utils/utils.py +0 -0
  300. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx.egg-info/dependency_links.txt +0 -0
  301. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx.egg-info/entry_points.txt +0 -0
  302. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx.egg-info/requires.txt +0 -0
  303. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/openscvx.egg-info/top_level.txt +0 -0
  304. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/pyproject.toml +0 -0
  305. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/scripts/gen_example_pages.py +0 -0
  306. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/scripts/gen_ref_pages.py +0 -0
  307. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
  308. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/setup.cfg +0 -0
  309. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/__init__.py +0 -0
  310. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/_marks.py +0 -0
  311. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/__init__.py +0 -0
  312. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/_iteration_helpers.py +0 -0
  313. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/autotuner/__init__.py +0 -0
  314. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
  315. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
  316. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/test_iteration_fn_jit.py +0 -0
  317. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/test_iteration_fn_parity.py +0 -0
  318. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/algorithms/test_make_solve_loop.py +0 -0
  319. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/brachistochrone_analytical.py +0 -0
  320. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/conftest.py +0 -0
  321. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/expr/__init__.py +0 -0
  322. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/expr/test_gmsr.py +0 -0
  323. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/fixtures/brachistochrone.json +0 -0
  324. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/fixtures/brachistochrone.yaml +0 -0
  325. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/hohmann_analytical.py +0 -0
  326. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/integrations/__init__.py +0 -0
  327. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/integrations/test_mjx.py +0 -0
  328. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/integrations/test_mjx_dynamics.py +0 -0
  329. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/__init__.py +0 -0
  330. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/_iteration_callback_helpers.py +0 -0
  331. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
  332. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
  333. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_iteration_callback_moreau.py +0 -0
  334. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_iteration_callback_qpax.py +0 -0
  335. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_iteration_callback_vmap.py +0 -0
  336. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_moreau_ptr_solver.py +0 -0
  337. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_qpax_ptr_solver.py +0 -0
  338. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/solvers/test_subproblem_pytree.py +0 -0
  339. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/__init__.py +0 -0
  340. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/__init__.py +0 -0
  341. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_arithmetic.py +0 -0
  342. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_array.py +0 -0
  343. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_constraint.py +0 -0
  344. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_expr.py +0 -0
  345. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_lie.py +0 -0
  346. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_linalg.py +0 -0
  347. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_logic.py +0 -0
  348. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_math.py +0 -0
  349. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_node_reference.py +0 -0
  350. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_parameters.py +0 -0
  351. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_scaling.py +0 -0
  352. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_spatial.py +0 -0
  353. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_stl.py +0 -0
  354. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_variable.py +0 -0
  355. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/expr/test_vmap.py +0 -0
  356. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/__init__.py +0 -0
  357. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_array.py +0 -0
  358. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_constraint.py +0 -0
  359. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_lie.py +0 -0
  360. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_linalg.py +0 -0
  361. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_load.py +0 -0
  362. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_logic.py +0 -0
  363. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_math.py +0 -0
  364. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_parser.py +0 -0
  365. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_spatial.py +0 -0
  366. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_stl.py +0 -0
  367. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_tokenizer.py +0 -0
  368. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/parser/test_vmap.py +0 -0
  369. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/test_augmentation.py +0 -0
  370. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/test_hashing.py +0 -0
  371. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/test_lower_cvxpy.py +0 -0
  372. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/test_lower_jax.py +0 -0
  373. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/test_preprocessing.py +0 -0
  374. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/test_sparsity.py +0 -0
  375. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/symbolic/test_unified.py +0 -0
  376. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_autotuning.py +0 -0
  377. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_cvxpygen_optional.py +0 -0
  378. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_discretization.py +0 -0
  379. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_examples.py +0 -0
  380. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_expert.py +0 -0
  381. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_impulsive.py +0 -0
  382. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_init.py +0 -0
  383. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_integrators.py +0 -0
  384. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_loader.py +0 -0
  385. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_optimization_results.py +0 -0
  386. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_plotting.py +0 -0
  387. {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev18}/tests/test_propagation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.5.3.dev16
3
+ Version: 0.5.3.dev18
4
4
  Summary: A general Python-based successive convexification implementation which uses a JAX backend.
5
5
  Author-email: Chris Hayner and Griffin Norris <haynec@uw.edu>
6
6
  License: Apache Software License
@@ -0,0 +1,134 @@
1
+ # Batching, JIT, and Grad with `Problem.solve_jax()`
2
+
3
+ `Problem` exposes two solve entry points. `solve()` is the familiar
4
+ Python-loop driver — real-time prints, wall-clock `time_limit`, `continuous`
5
+ mode, populated per-iteration history. `solve_jax()` is its JAX-pure
6
+ sibling: it drives the same fused `iteration_fn` body inside a
7
+ `lax.while_loop` and returns an `OptimizationResults`
8
+ (`openscvx/algorithms/optimization_results.py`) pytree, so it composes with
9
+ `jax.vmap`, `jax.jit`, and `jax.grad`.
10
+
11
+ ```python
12
+ problem = ox.Problem(..., solver={"backend": "qpax"})
13
+ problem.initialize()
14
+
15
+ # Familiar interactive solve — unchanged: prints, history, time limit.
16
+ result = problem.solve()
17
+
18
+ # JAX-pure single solve — silent, no per-iteration history, batchable.
19
+ result = problem.solve_jax()
20
+
21
+ # Batched — standard jax.vmap, no library-specific API.
22
+ batched = jax.vmap(problem.solve_jax, in_axes=(0, 0, None))(
23
+ x_initial_stack, x_final_stack, params
24
+ )
25
+
26
+ # JIT-compile once, solve forever (e.g. MPC inner loop).
27
+ fast_solve = jax.jit(problem.solve_jax)
28
+ ```
29
+
30
+ A worked example lives at `examples/abstract/brachistochrone_batched.py`
31
+ — four brachistochrone problems with shifted starting x-coordinates,
32
+ solved in parallel via `jax.vmap(problem.solve_jax)`.
33
+
34
+ ## When to choose `solve()` vs. `solve_jax()`
35
+
36
+ | Use case | Entry point |
37
+ |---|---|
38
+ | Interactive use, real-time prints, plotting per iteration | `solve()` |
39
+ | Wall-clock `time_limit` or `continuous=True` | `solve()` |
40
+ | Per-iteration trajectories / weights / diagnostics | `solve()` |
41
+ | Batched solve over many initial conditions or parameters | `solve_jax()` |
42
+ | JIT-compile once, solve forever (MPC inner loop, scenario sweeps) | `solve_jax()` |
43
+ | `jax.grad` through the solver (best-effort, untested) | `solve_jax()` |
44
+
45
+ The split exists to make user intent explicit — a single dispatched
46
+ `solve()` would have to infer from arguments which path the caller wants,
47
+ and silent routing failures (like `continuous=True` quietly skipping the
48
+ Python loop) are exactly the failure class the two-method design exists to
49
+ prevent. See `plans/jax-pure-solve.md`'s Decision Log for the longer
50
+ discussion.
51
+
52
+ ## What `solve_jax()` returns
53
+
54
+ `solve_jax()` returns the same `OptimizationResults` type as `solve()`,
55
+ but built via
56
+ `OptimizationResults.from_final_state(state, problem=...)` instead of
57
+ `from_history(history, final_state, ...)`. The differences:
58
+
59
+ * **Per-iteration history is empty.** `X = [state.x]` and `U = [state.u]`
60
+ are single-element lists so `result.x` / `result.u` continue to return
61
+ the final iterate; every `*_history` field is `[]`. List growth doesn't
62
+ fit inside `lax.while_loop`. If you need per-iteration trajectories, use
63
+ `solve()` or wrap `solve_jax()` in `lax.scan` manually.
64
+ * **Post-process fields stay `None`.** `t_full`, `x_full`, `u_full`,
65
+ `cost`, `ctcs_violation` are populated only by `post_process()`. They're
66
+ outside the JAX pytree's children surface, so a batched `solve_jax`
67
+ result doesn't force every consumer to handle `None` leaves. If you want
68
+ to post-process, call `post_process()` per batch element after the
69
+ batched solve.
70
+ * **`converged` is a `jnp.bool_`** under the JAX-pure path (a `(B,)` array
71
+ under vmap), not a Python `bool` like `solve()` returns. Most uses
72
+ (`if result.converged: ...`) work either way.
73
+
74
+ ## `solve_jax()` arguments
75
+
76
+ ```python
77
+ problem.solve_jax(
78
+ x_initial=None, # boundary-condition pin (full unified state vector,
79
+ # ``jnp.nan`` at non-Fix entries — see
80
+ # ``AlgorithmState.from_settings``); falls back
81
+ # to the default from settings
82
+ x_final=None, # terminal pin, same conventions
83
+ parameters=None, # parameters dict for this solve; falls back to
84
+ # ``self._parameters``
85
+ *,
86
+ max_iters=None, # SCP iteration cap; non-default rebuilds the
87
+ # cached ``lax.while_loop`` closure (one extra
88
+ # trace; subsequent calls at the same cap hit
89
+ # the cache)
90
+ )
91
+ ```
92
+
93
+ Positional kwargs (rather than a single `inputs` pytree) keep
94
+ `jax.vmap(problem.solve_jax, in_axes=(0, 0, None))` ergonomic;
95
+ multi-argument gradient uses `jax.grad(..., argnums=(0, 1))`.
96
+
97
+ ## Caveats
98
+
99
+ ### CVXPy under `vmap` is sequential
100
+
101
+ The CVXPy backend's `iteration_callback`
102
+ (`openscvx/solvers/cvxpy_ptr_solver.py`) host-calls CVXPy through
103
+ `jax.pure_callback` with `vmap_method="sequential"`. Host
104
+ CVXPy is not thread-safe, so a `jax.vmap(problem.solve_jax)` over the
105
+ CVXPy backend runs `B` sequential CVXPy solves. The QPAX and Moreau
106
+ backends are pure JAX end-to-end and run in parallel under vmap.
107
+
108
+ ### Moreau warm-start is bypassed
109
+
110
+ The Moreau backend's `_warm_start` carry is host-side mutable state that
111
+ `lax.while_loop` doesn't thread. Both `solve()` and `solve_jax()` cold-start
112
+ the inner Moreau solve every SCP iteration (see the Moreau module
113
+ docstring and `plans/jax-pure-solve.md`'s Decision Log 2026-05-27).
114
+ Restoring warm-start to the SCP loop requires threading `(x, z, s)`
115
+ through an `AlgorithmState.moreau_carry` field — a Future Extension.
116
+
117
+ ### `jax.grad` is best-effort, untested
118
+
119
+ The QPAX backend's `solve_qp` is not differentiable (its convergence flag
120
+ costs reverse-mode autodiff). The CVXPy backend's `pure_callback` is
121
+ non-differentiable by default. End-to-end gradient validation through
122
+ `solve_jax` is a follow-up — `jax.grad(loss_fn)(x0)` will run without
123
+ erroring under QPAX (`solve_qp_primal` would be `custom_vjp`, but the
124
+ backend currently uses `solve_qp`), but the gradient's correctness is not
125
+ yet pinned down by a test against finite differences.
126
+
127
+ ### Convergence under `vmap` with mixed-rate elements
128
+
129
+ `lax.while_loop` continues while *any* batch element still needs
130
+ iterations — converged elements would otherwise keep receiving body calls
131
+ while their peers iterate. `make_solve_loop`'s body selects the unchanged
132
+ state for converged elements (`jax.tree.map(jnp.where, ...)`), so a
133
+ batched solve agrees with the single-problem `solve_jax` on each element.
134
+ The cost is per-iteration work × slowest-element iteration count.
@@ -188,8 +188,13 @@ JAX export, used only by `post_process()` — so a compiled problem holds **one
188
188
  `iteration_fn` plus one propagation export**, not three discretization /
189
189
  propagation exports stitched together in Python. Because `iteration_fn` is a
190
190
  registered pytree in and out, it also composes with `jax.jit`; `make_solve_loop`
191
- wraps it in `lax.while_loop` as the primitive for a future fully-JAX-driven
192
- solve.
191
+ wraps it in `lax.while_loop` to drive the **JAX-pure** `Problem.solve_jax()`
192
+ entry point, which composes with `jax.vmap`, `jax.jit`, and `jax.grad` (see
193
+ `docs/UnderTheHood/batching_jit_grad.md`). The familiar
194
+ `Problem.solve()` keeps its Python-loop shape — real-time prints, wall-clock
195
+ `time_limit`, populated per-iteration history — and is the right entry
196
+ point for interactive use. `solve_jax()` is the right entry point for
197
+ batched / JIT'd / differentiable workflows.
193
198
 
194
199
  Picking a backend at construction time:
195
200
 
@@ -0,0 +1,126 @@
1
+ """Batched brachistochrone via ``jax.vmap(problem.solve_jax)``.
2
+
3
+ Solves four brachistochrone problems in parallel — each with a different
4
+ starting x-coordinate — using the JAX-pure
5
+ :meth:`~openscvx.problem.Problem.solve_jax` entry point. ``solve_jax``
6
+ returns an :class:`~openscvx.algorithms.optimization_results.OptimizationResults`
7
+ pytree, so ``jax.vmap`` over the boundary-condition pins is the standard JAX
8
+ composition pattern; no library-specific batching API is needed.
9
+
10
+ Run with ``python examples/abstract/brachistochrone_batched.py``.
11
+
12
+ Notes:
13
+
14
+ * Under the QPAX backend used here, vmap'd subproblem solves run in
15
+ parallel (no host callback). The CVXPy backend is single-threaded under
16
+ vmap because :func:`jax.pure_callback` uses ``vmap_method="sequential"``
17
+ for thread safety — see :meth:`solve_jax`'s docstring.
18
+ * The Moreau warm-start is bypassed on the JAX-pure path (its carry is
19
+ host-side state that ``lax.while_loop`` doesn't thread). Use QPAX or
20
+ CVXPy when you want batched solves.
21
+ """
22
+
23
+ import os
24
+ import sys
25
+
26
+ import jax
27
+ import jax.numpy as jnp
28
+ import numpy as np
29
+
30
+ current_dir = os.path.dirname(os.path.abspath(__file__))
31
+ grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
32
+ sys.path.append(grandparent_dir)
33
+
34
+ import openscvx as ox
35
+ from openscvx import Problem
36
+
37
+
38
+ def build_problem(n: int = 8):
39
+ g = 9.81
40
+
41
+ position = ox.State("position", shape=(2,))
42
+ position.max = np.array([10.0, 10.0])
43
+ position.min = np.array([0.0, 0.0])
44
+ position.initial = np.array([0.0, 10.0])
45
+ position.final = [10.0, 5.0]
46
+
47
+ velocity = ox.State("velocity", shape=(1,))
48
+ velocity.max = np.array([10.0])
49
+ velocity.min = np.array([0.0])
50
+ velocity.initial = np.array([0.0])
51
+ velocity.final = [("free", 10.0)]
52
+
53
+ theta = ox.Control("theta", shape=(1,))
54
+ theta.max = np.array([100.5 * jnp.pi / 180])
55
+ theta.min = np.array([0.0])
56
+ theta.guess = np.linspace(5 * jnp.pi / 180, 100.5 * jnp.pi / 180, n).reshape(-1, 1)
57
+
58
+ states = [position, velocity]
59
+ controls = [theta]
60
+
61
+ dynamics = {
62
+ "position": ox.Concat(
63
+ velocity[0] * ox.Sin(theta[0]),
64
+ -velocity[0] * ox.Cos(theta[0]),
65
+ ),
66
+ "velocity": g * ox.Cos(theta[0]),
67
+ }
68
+
69
+ constraint_exprs = []
70
+ for state in states:
71
+ constraint_exprs.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
72
+
73
+ time = ox.Time(
74
+ initial=0.0,
75
+ final=("minimize", 2.0),
76
+ min=0.0,
77
+ max=2.0,
78
+ uniform_time_grid=True,
79
+ )
80
+
81
+ return Problem(
82
+ dynamics=dynamics,
83
+ states=states,
84
+ controls=controls,
85
+ time=time,
86
+ constraints=constraint_exprs,
87
+ N=n,
88
+ float_dtype="float64",
89
+ algorithm={
90
+ "autotuner": "ConstantProximalWeight",
91
+ "lam_prox": 1e0,
92
+ "lam_cost": 6e-1,
93
+ "ep_tr": 1e-5,
94
+ "ep_vb": 1e-5,
95
+ "ep_vc": 1e-9,
96
+ "k_max": 30,
97
+ },
98
+ solver={"backend": "qpax"},
99
+ )
100
+
101
+
102
+ if __name__ == "__main__":
103
+ problem = build_problem()
104
+ problem.initialize()
105
+
106
+ # The default boundary-condition pin (the full unified state vector with
107
+ # ``jnp.nan`` at non-Fix entries — see
108
+ # :meth:`AlgorithmState.from_settings`).
109
+ default_pin = problem.state.x_init_pin
110
+
111
+ # Stack four ICs by varying the starting x-coordinate (component 0 of
112
+ # the unified state vector is the x-position).
113
+ shifts = jnp.array([0.0, 0.3, 0.6, 0.9])
114
+ x_initial_stack = jnp.stack([default_pin.at[0].set(default_pin[0] + s) for s in shifts])
115
+
116
+ # Bare ``jax.vmap`` — the library exposes no batching wrapper of its own.
117
+ batched_solve = jax.vmap(problem.solve_jax, in_axes=(0, None, None))
118
+ results = batched_solve(x_initial_stack, None, None)
119
+
120
+ # ``results`` is a batched ``OptimizationResults`` pytree — every child
121
+ # (``X``, ``U``, ``t_final``, ``converged``, ...) has a leading batch axis.
122
+ print(f"Batched solve over {x_initial_stack.shape[0]} initial conditions:")
123
+ print(f" result.x.shape: {results.x.shape}")
124
+ print(f" result.u.shape: {results.u.shape}")
125
+ print(f" result.t_final: {np.asarray(results.t_final).reshape(-1)}")
126
+ print(f" result.converged: {np.asarray(results.converged)}")
@@ -48,7 +48,7 @@ a4 = 0.150 # Wrist to end-effector
48
48
  inertia = np.array([0.08, 0.06, 0.05, 0.04, 0.02, 0.01, 0.005])
49
49
 
50
50
  # Number of discretization nodes (parameterized by segments between waypoints)
51
- nodes_per_segment = 2
51
+ nodes_per_segment = 1
52
52
  total_time = 4.0
53
53
 
54
54
  # =============================================================================
@@ -59,17 +59,26 @@ mass.guess = np.linspace(mass.initial, 1690, n).reshape(-1, 1)
59
59
  # Define control — thrust force vector [Tx, Ty, Tz]
60
60
  thrust = ox.Control("thrust", shape=(3,), parameterization="ZOH")
61
61
 
62
- T_bar = 3.1 * 1e3
62
+ T_bar = 3.1 * 1e3 # N, per-engine maximum thrust
63
63
  T1 = 0.3 * T_bar
64
64
  T2 = 0.8 * T_bar
65
65
  n_eng = 6
66
+ T_max = n_eng * T_bar # N, total cluster maximum thrust
67
+ theta_val = 27 * np.pi / 180
66
68
 
67
- # Set bounds on control
68
- thrust.min = n_eng * np.array([-T_bar, -T_bar, -T_bar])
69
- thrust.max = n_eng * np.array([T_bar, T_bar, T_bar])
69
+ # Thrust magnitude envelope (used for CTCS and normalized box/scaling bounds)
70
+ rho_min = n_eng * T1 * np.cos(theta_val)
71
+ rho_max = n_eng * T2 * np.cos(theta_val)
72
+
73
+ # Box bounds on thrust components (physical authority)
74
+ thrust.min = -T_max * np.ones(3)
75
+ thrust.max = T_max * np.ones(3)
76
+ # Tighter scaling bounds for SCP conditioning (active thrust envelope)
77
+ thrust.scaling_min = -rho_max * np.ones(3)
78
+ thrust.scaling_max = rho_max * np.ones(3)
70
79
 
71
80
  # Set initial control guess
72
- thrust.guess = np.repeat(np.expand_dims(np.array([0, 0, n_eng * (T2) / 2]), axis=0), n, axis=0)
81
+ thrust.guess = np.repeat(np.expand_dims(np.array([0, 0, rho_max / 2]), axis=0), n, axis=0)
73
82
 
74
83
  # Define list of all states and controls
75
84
  states = [position, velocity, mass]
@@ -82,11 +91,7 @@ g_e = 9.807 # Gravitational acceleration on Earth in m/s^2
82
91
  # Create parameters for the problem
83
92
  I_sp = ox.Parameter("I_sp", value=225.0)
84
93
  g = ox.Parameter("g", value=3.7114)
85
- theta = ox.Parameter("theta", value=27 * np.pi / 180)
86
-
87
- # These will be computed symbolically in constraints
88
- rho_min = n_eng * T1 * np.cos(theta.value) # Minimum thrust-to-weight ratio
89
- rho_max = n_eng * T2 * np.cos(theta.value) # Maximum thrust-to-weight ratio
94
+ theta = ox.Parameter("theta", value=theta_val)
90
95
 
91
96
  # Generate box constraints for all states
92
97
  constraints = []
@@ -101,8 +106,8 @@ for state in states:
101
106
  # Thrust magnitude constraints
102
107
  constraints.extend(
103
108
  [
104
- ox.ctcs(rho_min <= ox.linalg.Norm(thrust), idx=1),
105
- ox.ctcs(ox.linalg.Norm(thrust) <= rho_max, idx=1),
109
+ ox.ctcs(rho_min <= ox.linalg.Norm(thrust), idx=1, penalty="huber"),
110
+ ox.ctcs(ox.linalg.Norm(thrust) <= rho_max, idx=1, penalty="huber"),
106
111
  ]
107
112
  )
108
113
 
@@ -857,12 +857,11 @@ def _build_let_problem_bundle(float_dtype: str = LET_FLOAT_DTYPE) -> dict:
857
857
  "diffrax_kwargs": {"atol": integration_tol, "rtol": integration_tol},
858
858
  }
859
859
  algorithm = {
860
- "k_max": 150,
861
860
  "lam_prox": 5e-2,
862
861
  "lam_vc": 3e1,
863
862
  "lam_vb": 2e-1,
864
863
  "lam_cost": 0.5,
865
- "ep_tr": 1e-9,
864
+ "ep_tr": 1e-8,
866
865
  "ep_vc": 1e-6,
867
866
  "autotuner": ox.AugmentedLagrangian(),
868
867
  }
@@ -197,4 +197,5 @@ nav:
197
197
  - 'Foundations/discretization.md'
198
198
  - Under the Hood:
199
199
  - 'UnderTheHood/vectorization_and_vmapping.md'
200
- - 'UnderTheHood/lowering_architecture.md'
200
+ - 'UnderTheHood/lowering_architecture.md'
201
+ - 'UnderTheHood/batching_jit_grad.md'
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.5.3.dev16'
22
- __version_tuple__ = version_tuple = (0, 5, 3, 'dev16')
21
+ __version__ = version = '0.5.3.dev18'
22
+ __version_tuple__ = version_tuple = (0, 5, 3, 'dev18')
23
23
 
24
- __commit_id__ = commit_id = 'gb2479bf05'
24
+ __commit_id__ = commit_id = 'gae09f2b29'
@@ -1,10 +1,17 @@
1
1
  from dataclasses import dataclass, field
2
2
  from pathlib import Path
3
- from typing import Any, Optional, Union
3
+ from typing import TYPE_CHECKING, Any, Optional, Union
4
4
 
5
+ import jax
6
+ import jax.numpy as jnp
5
7
  import numpy as np
6
8
 
9
+ if TYPE_CHECKING:
10
+ from openscvx.algorithms.base import AlgorithmHistory, AlgorithmState
11
+ from openscvx.problem import Problem
7
12
 
13
+
14
+ @jax.tree_util.register_pytree_node_class
8
15
  @dataclass
9
16
  class OptimizationResults:
10
17
  """
@@ -44,6 +51,30 @@ class OptimizationResults:
44
51
  Added by propagate_trajectory_results.
45
52
  plotting_data (dict[str, Any]): Flexible storage for plotting and application data.
46
53
 
54
+ !!! note "JAX pytree registration"
55
+ ``OptimizationResults`` is a registered JAX pytree, so the output of
56
+ :meth:`~openscvx.problem.Problem.solve_jax` composes with
57
+ ``jax.vmap`` / ``jax.jit`` / ``jax.grad``. The pytree *children* are
58
+ ``converged``, ``t_final``, ``nodes``, ``trajectory``, ``X``, ``U``,
59
+ and every ``*_history`` field — the data the user actually consumes.
60
+ Post-process fields (``t_full``, ``x_full``, ``u_full``, ``cost``,
61
+ ``ctcs_violation``), ``plotting_data``, and the internal ``_states`` /
62
+ ``_controls`` metadata are stashed in the treedef's *aux* instead, so
63
+ a batched ``solve_jax`` doesn't force callers to handle ``None``
64
+ leaves and ``post_process()`` outputs stay outside the vmap surface
65
+ (post-process per element after the batched solve).
66
+
67
+ The two construction paths produce different histories:
68
+
69
+ * :meth:`from_history` (``solve()``) populates the per-iteration
70
+ ``X`` / ``U`` / ``*_history`` lists from the Python loop's
71
+ :class:`~openscvx.algorithms.base.AlgorithmHistory`.
72
+ * :meth:`from_final_state` (``solve_jax()``) wraps only the final
73
+ iterate: ``X = [state.x]`` and ``U = [state.u]`` (single-element
74
+ lists so ``result.x`` / ``result.u`` continue to return the final
75
+ iterate), and every ``*_history`` field is empty — per-iteration
76
+ history requires Python-side list growth that can't run inside
77
+ ``lax.while_loop``.
47
78
 
48
79
  !!! note "For Developers"
49
80
  The ``metadata={"npz": ...}`` parameter on each field below is a built-in feature of
@@ -66,6 +97,30 @@ class OptimizationResults:
66
97
  _OPT_ARRAY = "optional_array"
67
98
  _OPT_SCALAR = "optional_scalar"
68
99
 
100
+ # Pytree children: fields whose contents flow through ``jax.vmap`` /
101
+ # ``jax.jit`` / ``jax.grad``. Excludes ``_states`` / ``_controls`` (Python
102
+ # symbolic metadata), ``plotting_data`` (user scratch), and the
103
+ # post-process fields (``None`` until ``post_process()`` runs and not part
104
+ # of the ``solve_jax`` vmap surface — see the class docstring).
105
+ _PYTREE_CHILDREN = (
106
+ "converged",
107
+ "t_final",
108
+ "nodes",
109
+ "trajectory",
110
+ "X",
111
+ "U",
112
+ "discretization_history",
113
+ "J_tr_history",
114
+ "J_vb_history",
115
+ "J_vc_history",
116
+ "TR_history",
117
+ "VC_history",
118
+ "lam_prox_history",
119
+ "actual_reduction_history",
120
+ "pred_reduction_history",
121
+ "acceptance_ratio_history",
122
+ )
123
+
69
124
  # Core optimization results
70
125
  converged: bool = field(metadata={"npz": "scalar"})
71
126
  t_final: float = field(metadata={"npz": "scalar"})
@@ -138,6 +193,183 @@ class OptimizationResults:
138
193
  """Initialize the results object."""
139
194
  pass
140
195
 
196
+ # ------------------------------------------------------------------
197
+ # JAX pytree registration
198
+ # ------------------------------------------------------------------
199
+
200
+ def tree_flatten(self):
201
+ """Split into JAX-traceable children and host-side aux.
202
+
203
+ See :attr:`_PYTREE_CHILDREN` for the children list. Aux carries the
204
+ symbolic metadata, scratch dict, and post-process fields verbatim so
205
+ the round-trip ``tree_unflatten(*tree_flatten(self))`` preserves the
206
+ instance.
207
+ """
208
+ children = tuple(getattr(self, name) for name in self._PYTREE_CHILDREN)
209
+ aux = (
210
+ self._states,
211
+ self._controls,
212
+ self.t_full,
213
+ self.x_full,
214
+ self.u_full,
215
+ self.cost,
216
+ self.ctcs_violation,
217
+ self.plotting_data,
218
+ )
219
+ return children, aux
220
+
221
+ @classmethod
222
+ def tree_unflatten(cls, aux, children):
223
+ kwargs = dict(zip(cls._PYTREE_CHILDREN, children))
224
+ (
225
+ _states,
226
+ _controls,
227
+ t_full,
228
+ x_full,
229
+ u_full,
230
+ cost,
231
+ ctcs_violation,
232
+ plotting_data,
233
+ ) = aux
234
+ return cls(
235
+ **kwargs,
236
+ _states=_states,
237
+ _controls=_controls,
238
+ t_full=t_full,
239
+ x_full=x_full,
240
+ u_full=u_full,
241
+ cost=cost,
242
+ ctcs_violation=ctcs_violation,
243
+ plotting_data=plotting_data,
244
+ )
245
+
246
+ # ------------------------------------------------------------------
247
+ # Constructors
248
+ # ------------------------------------------------------------------
249
+
250
+ @classmethod
251
+ def from_final_state(
252
+ cls,
253
+ state: "AlgorithmState",
254
+ *,
255
+ problem: "Problem",
256
+ ) -> "OptimizationResults":
257
+ """JAX-pure construction from the final SCP iterate.
258
+
259
+ Used by :meth:`~openscvx.problem.Problem.solve_jax`. Every leaf is a
260
+ ``jnp.ndarray`` derived from ``state`` via static slicing, so the
261
+ result composes with ``jax.vmap`` / ``jax.jit`` / ``jax.grad`` over a
262
+ batched ``state``. ``X = [state.x]`` and ``U = [state.u]`` are
263
+ single-element lists so ``result.x`` / ``result.u`` continue to
264
+ return the final iterate; every ``*_history`` field is empty — the
265
+ Python-loop ``.solve()`` is the path that populates them.
266
+ """
267
+ settings = problem.settings
268
+ symbolic = problem.symbolic
269
+ algorithm = problem._algorithm
270
+
271
+ has_impulsive_controls = any(
272
+ control.parameterization == "impulsive" for control in symbolic.controls
273
+ )
274
+
275
+ nodes_dict: dict[str, jnp.ndarray] = {}
276
+ for sym_state in symbolic.states:
277
+ state_nodes = state.x[..., sym_state._slice]
278
+ if has_impulsive_controls:
279
+ bc = jnp.asarray(settings.sim.x.initial[sym_state._slice])
280
+ state_nodes = state_nodes.at[..., 0, :].set(bc)
281
+ nodes_dict[sym_state.name] = state_nodes
282
+
283
+ for control in symbolic.controls:
284
+ nodes_dict[control.name] = state.u[..., control._slice]
285
+
286
+ # Match the host-path ``t_final`` shape: a 1-vector along the time
287
+ # slice of the last node (vmap'd to ``(B, 1)``).
288
+ t_final = state.x[..., -1, :][..., settings.sim.time_slice]
289
+
290
+ converged = (
291
+ (state.J_tr < algorithm.ep_tr)
292
+ & (state.J_vb < algorithm.ep_vb)
293
+ & (state.J_vc < algorithm.ep_vc)
294
+ )
295
+
296
+ return cls(
297
+ converged=converged,
298
+ t_final=t_final,
299
+ nodes=nodes_dict,
300
+ trajectory={},
301
+ _states=symbolic.states_prop,
302
+ _controls=symbolic.controls,
303
+ X=[state.x],
304
+ U=[state.u],
305
+ discretization_history=[],
306
+ J_tr_history=[],
307
+ J_vb_history=[],
308
+ J_vc_history=[],
309
+ TR_history=[],
310
+ VC_history=[],
311
+ lam_prox_history=[],
312
+ actual_reduction_history=[],
313
+ pred_reduction_history=[],
314
+ acceptance_ratio_history=[],
315
+ )
316
+
317
+ @classmethod
318
+ def from_history(
319
+ cls,
320
+ history: "AlgorithmHistory",
321
+ final_state: "AlgorithmState",
322
+ *,
323
+ problem: "Problem",
324
+ converged: bool,
325
+ ) -> "OptimizationResults":
326
+ """Host-side construction from the Python-loop iteration history.
327
+
328
+ Used by :meth:`~openscvx.problem.Problem.solve` to package the full
329
+ per-iteration log produced by the Python ``while`` loop. Symmetric
330
+ with :meth:`from_final_state` — same return type, populated history.
331
+ """
332
+ settings = problem.settings
333
+ symbolic = problem.symbolic
334
+
335
+ has_impulsive_controls = any(
336
+ control.parameterization == "impulsive" for control in symbolic.controls
337
+ )
338
+
339
+ x_arr = np.asarray(final_state.x)
340
+ u_arr = np.asarray(final_state.u)
341
+
342
+ nodes_dict: dict[str, np.ndarray] = {}
343
+ for sym_state in symbolic.states:
344
+ state_nodes = x_arr[:, sym_state._slice].copy()
345
+ if has_impulsive_controls:
346
+ state_nodes[0] = settings.sim.x.initial[sym_state._slice]
347
+ nodes_dict[sym_state.name] = state_nodes
348
+
349
+ for control in symbolic.controls:
350
+ nodes_dict[control.name] = u_arr[:, control._slice]
351
+
352
+ return cls(
353
+ converged=converged,
354
+ t_final=x_arr[:, settings.sim.time_slice][-1],
355
+ nodes=nodes_dict,
356
+ trajectory={},
357
+ _states=symbolic.states_prop,
358
+ _controls=symbolic.controls,
359
+ X=history.X,
360
+ U=history.U,
361
+ discretization_history=history.V_history,
362
+ J_tr_history=float(final_state.J_tr),
363
+ J_vb_history=float(final_state.J_vb),
364
+ J_vc_history=float(final_state.J_vc),
365
+ TR_history=history.TR,
366
+ VC_history=history.VC,
367
+ lam_prox_history=history.lam_prox.copy(),
368
+ actual_reduction_history=history.actual_reduction.copy(),
369
+ pred_reduction_history=history.pred_reduction.copy(),
370
+ acceptance_ratio_history=history.acceptance_ratio.copy(),
371
+ )
372
+
141
373
  def update_plotting_data(self, **kwargs: Any) -> None:
142
374
  """
143
375
  Update the plotting data with additional information.
@@ -361,8 +361,25 @@ def make_solve_loop(
361
361
  return (state.k <= k_max) & jnp.logical_not(_converged(state, ep_tr, ep_vb, ep_vc))
362
362
 
363
363
  def body(state: AlgorithmState) -> AlgorithmState:
364
+ # Under ``jax.vmap`` the ``lax.while_loop`` keeps running until
365
+ # every batch element has converged; without a freeze, the body
366
+ # would keep mutating already-converged elements (their iterates
367
+ # drift through repeated subproblem solves, and the autotuner
368
+ # would keep advancing ``lam_prox`` / ``lam_cost``). Selecting
369
+ # ``state`` for converged elements pins them to their first
370
+ # post-convergence iterate, so a batched solve agrees with the
371
+ # single-problem ``solve_jax`` on each element.
372
+ is_converged = _converged(state, ep_tr, ep_vb, ep_vc)
364
373
  next_state, _ = iteration_fn(state, params)
365
- return next_state
374
+
375
+ def freeze(nxt, prev):
376
+ # ``is_converged`` is scalar (single-problem) or shape ``(B,)``
377
+ # (vmap'd); reshape with trailing 1-axes so it broadcasts over
378
+ # each leaf's remaining dims.
379
+ mask_shape = is_converged.shape + (1,) * (nxt.ndim - is_converged.ndim)
380
+ return jnp.where(is_converged.reshape(mask_shape), prev, nxt)
381
+
382
+ return jax.tree.map(freeze, next_state, state)
366
383
 
367
384
  return jax.lax.while_loop(cond, body, state)
368
385