openscvx 0.5.2.dev11__tar.gz → 0.5.2.dev13__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 (363) hide show
  1. {openscvx-0.5.2.dev11/openscvx.egg-info → openscvx-0.5.2.dev13}/PKG-INFO +1 -1
  2. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UnderTheHood/lowering_architecture.md +2 -2
  3. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/_version.py +3 -3
  4. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/solvers/moreau_ptr_solver.py +140 -44
  5. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/solvers/ptr_solver.py +47 -1
  6. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/solvers/qpax_ptr_solver.py +198 -65
  7. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13/openscvx.egg-info}/PKG-INFO +1 -1
  8. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/solvers/test_moreau_ptr_solver.py +1 -1
  9. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/solvers/test_qpax_ptr_solver.py +35 -1
  10. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_brachistochrone.py +138 -17
  11. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_impulsive.py +112 -16
  12. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/assets/logo.svg +0 -0
  13. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/release-drafter.yml +0 -0
  14. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/_docs.yml +0 -0
  15. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/branch-name.yml +0 -0
  16. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/docs.yml +0 -0
  17. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/lint.yml +0 -0
  18. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/nightly.yml +0 -0
  19. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/release-drafter.yml +0 -0
  20. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/release.yml +0 -0
  21. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/tests-integration.yml +0 -0
  22. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.github/workflows/tests-unit.yml +0 -0
  23. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.gitignore +0 -0
  24. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/.gitmodules +0 -0
  25. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/CONTRIBUTING.md +0 -0
  26. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/LICENSE +0 -0
  27. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/README.md +0 -0
  28. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/Foundations/constraint_reformulation.md +0 -0
  29. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/Foundations/control_parameterization.md +0 -0
  30. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/Foundations/discretization.md +0 -0
  31. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/Foundations/ocp.md +0 -0
  32. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/Foundations/scvx.md +0 -0
  33. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/Foundations/time_dilation.md +0 -0
  34. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  35. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/00_introduction.md +0 -0
  36. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  37. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  38. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  39. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  40. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/05_visualization.md +0 -0
  41. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/06_logic.md +0 -0
  42. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/07_lie.md +0 -0
  43. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/UsersGuide/08_mpcc.md +0 -0
  44. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/favicon.png +0 -0
  45. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/images/ct-scvx_dark.png +0 -0
  46. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/images/ct-scvx_light.png +0 -0
  47. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/images/ctcs_dark.png +0 -0
  48. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/images/ctcs_light.png +0 -0
  49. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/images/problem_class_dark.png +0 -0
  50. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/images/problem_class_light.png +0 -0
  51. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/logo.svg +0 -0
  52. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/openscvx_logo_square.png +0 -0
  53. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/viser-client/index.html +0 -0
  54. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/viser-recordings/drone_racing.viser +0 -0
  55. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
  56. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/citation.md +0 -0
  57. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/examples.md +0 -0
  58. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/index.md +0 -0
  59. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/javascripts/mathjax.js +0 -0
  60. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/docs/versions.json +0 -0
  61. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/_viser_embed_export.py +0 -0
  62. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/abstract/brachistochrone.py +0 -0
  63. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/abstract/hypersensitive.py +0 -0
  64. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/abstract/impulsive.py +0 -0
  65. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/abstract/stl_integer_variable.py +0 -0
  66. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/abstract/stl_or.py +0 -0
  67. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/7_dof_arm.py +0 -0
  68. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/_camera.py +0 -0
  69. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/_render.py +0 -0
  70. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/_sensor_view.py +0 -0
  71. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/dr_vp_polytope.py +0 -0
  72. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/franka_fr3v2_pick_place.py +0 -0
  73. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/logo.py +0 -0
  74. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/animations/obstacle_avoidance_vmap.py +0 -0
  75. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/arm/3_dof_arm.py +0 -0
  76. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/arm/7_dof_arm.py +0 -0
  77. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/arm/7_dof_arm_collision.py +0 -0
  78. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/arm/7_dof_arm_vp.py +0 -0
  79. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/arm/franka_fr3v2_pick_place.py +0 -0
  80. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
  81. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/car/dubins_car.py +0 -0
  82. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/car/dubins_car_disjoint.py +0 -0
  83. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/car/dubins_car_obstacle_conditional.py +0 -0
  84. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/car/dubins_car_obstacle_stl.py +0 -0
  85. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/car/dubins_car_stl_or.py +0 -0
  86. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/car/dubins_car_waypoint_stl.py +0 -0
  87. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/cinema_vp.py +0 -0
  88. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/dr_double_integrator.py +0 -0
  89. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/dr_vp.py +0 -0
  90. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/dr_vp_nodal.py +0 -0
  91. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/dr_vp_polytope.py +0 -0
  92. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/drone_racing.py +0 -0
  93. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/logo.py +0 -0
  94. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/logo_utils/acl_logo.svg +0 -0
  95. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/logo_utils/svg_path_utils.py +0 -0
  96. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/obstacle_avoidance.py +0 -0
  97. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  98. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  99. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mjx/cartpole_mjx.py +0 -0
  100. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mjx/double_cartpole_mjx.py +0 -0
  101. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mjx/skydio_x2_mjx.py +0 -0
  102. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
  103. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mjx/triple_cartpole_game.py +0 -0
  104. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mjx/triple_cartpole_mjx.py +0 -0
  105. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mpc/double_integrator_discrete.py +0 -0
  106. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mpc/double_integrator_drone_racing.py +0 -0
  107. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mpc/dubins_car_circle_analytical.py +0 -0
  108. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mpc/dubins_car_circle_discrete.py +0 -0
  109. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
  110. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/plotting.py +0 -0
  111. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/plotting_viser.py +0 -0
  112. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/3DoF_pdg_realtime.py +0 -0
  113. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/6DoF_pdg_realtime.py +0 -0
  114. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
  115. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
  116. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
  117. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
  118. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
  119. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
  120. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/cinema_vp_realtime.py +0 -0
  121. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/drone_racing_realtime.py +0 -0
  122. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/dubins_car_realtime.py +0 -0
  123. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  124. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/rocket/3DoF_pdg.py +0 -0
  125. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/rocket/6DoF_pdg.py +0 -0
  126. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/spacecraft/halo_orbit.py +0 -0
  127. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/spacecraft/hohmann_transfer.py +0 -0
  128. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/spacecraft/let_transfer.py +0 -0
  129. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/examples/spacecraft/proxops_cw.py +0 -0
  130. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/ctlos_cine.gif +0 -0
  131. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/ctlos_dr.gif +0 -0
  132. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/dtlos_cine.gif +0 -0
  133. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/dtlos_dr.gif +0 -0
  134. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/openscvx_logo.svg +0 -0
  135. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/openscvx_logo_square.png +0 -0
  136. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/oscvx_structure_full_dark.svg +0 -0
  137. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/figures/video_preview.png +0 -0
  138. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/__init__.py +0 -0
  139. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/assets/stylesheets/custom.css +0 -0
  140. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
  141. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/assets/stylesheets/home-hero.css +0 -0
  142. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/assets/stylesheets/home-viser.css +0 -0
  143. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/home.html +0 -0
  144. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/main.html +0 -0
  145. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/partials/home-diagram.html +0 -0
  146. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/partials/home-dropin-banner.html +0 -0
  147. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/partials/home-hero.html +0 -0
  148. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/partials/home-pipeline.html +0 -0
  149. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/material/overrides/partials/home-viser-strip.html +0 -0
  150. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/mkdocs.yml +0 -0
  151. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/__init__.py +0 -0
  152. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/__main__.py +0 -0
  153. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/__init__.py +0 -0
  154. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/autotuner/__init__.py +0 -0
  155. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
  156. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
  157. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
  158. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
  159. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/base.py +0 -0
  160. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/optimization_results.py +0 -0
  161. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/scvx/__init__.py +0 -0
  162. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
  163. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/algorithms/weights.py +0 -0
  164. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/config.py +0 -0
  165. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/__init__.py +0 -0
  166. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/base.py +0 -0
  167. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/discretize_linearize.py +0 -0
  168. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/linearize_discretize.py +0 -0
  169. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
  170. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/sparse_utils/__init__.py +0 -0
  171. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
  172. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
  173. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/expert/__init__.py +0 -0
  174. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/expert/byof.py +0 -0
  175. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/expert/lowering.py +0 -0
  176. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/expert/validation.py +0 -0
  177. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/init/__init__.py +0 -0
  178. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/init/interpolation.py +0 -0
  179. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/init/inverse_kinematics.py +0 -0
  180. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/integrations/__init__.py +0 -0
  181. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/integrations/base.py +0 -0
  182. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/integrations/menagerie.py +0 -0
  183. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/integrations/mjx.py +0 -0
  184. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/integrators/__init__.py +0 -0
  185. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/integrators/diffrax.py +0 -0
  186. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/integrators/runge_kutta.py +0 -0
  187. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/loader.py +0 -0
  188. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/__init__.py +0 -0
  189. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/cvxpy_constraints.py +0 -0
  190. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/cvxpy_variables.py +0 -0
  191. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/dynamics.py +0 -0
  192. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/jax_constraints.py +0 -0
  193. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/parameters.py +0 -0
  194. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/problem.py +0 -0
  195. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/lowered/unified.py +0 -0
  196. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/__init__.py +0 -0
  197. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/plotting.py +0 -0
  198. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/scp_iteration.py +0 -0
  199. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/viser/__init__.py +0 -0
  200. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/viser/animated.py +0 -0
  201. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/viser/orbits.py +0 -0
  202. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/viser/plotly_integration.py +0 -0
  203. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/viser/primitives.py +0 -0
  204. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/viser/scp.py +0 -0
  205. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/plotting/viser/server.py +0 -0
  206. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/problem.py +0 -0
  207. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/propagation/__init__.py +0 -0
  208. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/propagation/post_processing.py +0 -0
  209. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/propagation/propagation.py +0 -0
  210. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/solvers/__init__.py +0 -0
  211. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/solvers/base.py +0 -0
  212. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
  213. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/__init__.py +0 -0
  214. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/augmentation.py +0 -0
  215. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/builder.py +0 -0
  216. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/constraint_set.py +0 -0
  217. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/__init__.py +0 -0
  218. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/arithmetic.py +0 -0
  219. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/array.py +0 -0
  220. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/constraint.py +0 -0
  221. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/control.py +0 -0
  222. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/expr.py +0 -0
  223. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  224. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  225. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/lie/se3.py +0 -0
  226. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/lie/so3.py +0 -0
  227. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/linalg.py +0 -0
  228. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/logic.py +0 -0
  229. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/math.py +0 -0
  230. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/parameter.py +0 -0
  231. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/spatial.py +0 -0
  232. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/state.py +0 -0
  233. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/stl.py +0 -0
  234. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/stljax.py +0 -0
  235. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/time.py +0 -0
  236. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/variable.py +0 -0
  237. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/expr/vmap.py +0 -0
  238. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/hashing.py +0 -0
  239. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lower.py +0 -0
  240. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/__init__.py +0 -0
  241. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  242. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  243. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  244. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  245. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  246. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  247. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  248. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  249. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  250. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  251. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  252. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  253. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  254. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  255. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  256. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  257. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  258. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
  259. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  260. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  261. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  262. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  263. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
  264. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  265. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  266. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  267. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  268. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
  269. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  270. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/__init__.py +0 -0
  271. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/_registry.py +0 -0
  272. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/array.py +0 -0
  273. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/constraint.py +0 -0
  274. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/lie.py +0 -0
  275. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/linalg.py +0 -0
  276. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/logic.py +0 -0
  277. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/math.py +0 -0
  278. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/parser.py +0 -0
  279. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/spatial.py +0 -0
  280. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/stl.py +0 -0
  281. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/stljax.py +0 -0
  282. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/parser/tokenizer.py +0 -0
  283. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/preprocessing.py +0 -0
  284. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/problem.py +0 -0
  285. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/sparsity.py +0 -0
  286. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/symbolic/unified.py +0 -0
  287. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/utils/__init__.py +0 -0
  288. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/utils/cache.py +0 -0
  289. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/utils/caching.py +0 -0
  290. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/utils/printing.py +0 -0
  291. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/utils/profiling.py +0 -0
  292. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx/utils/utils.py +0 -0
  293. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx.egg-info/SOURCES.txt +0 -0
  294. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx.egg-info/dependency_links.txt +0 -0
  295. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx.egg-info/entry_points.txt +0 -0
  296. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx.egg-info/requires.txt +0 -0
  297. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/openscvx.egg-info/top_level.txt +0 -0
  298. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/pyproject.toml +0 -0
  299. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/scripts/gen_example_pages.py +0 -0
  300. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/scripts/gen_ref_pages.py +0 -0
  301. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
  302. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/setup.cfg +0 -0
  303. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/__init__.py +0 -0
  304. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/_marks.py +0 -0
  305. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/brachistochrone_analytical.py +0 -0
  306. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/conftest.py +0 -0
  307. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/expr/__init__.py +0 -0
  308. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/expr/test_gmsr.py +0 -0
  309. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/fixtures/brachistochrone.json +0 -0
  310. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/fixtures/brachistochrone.yaml +0 -0
  311. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/hohmann_analytical.py +0 -0
  312. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/integrations/__init__.py +0 -0
  313. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/integrations/test_mjx.py +0 -0
  314. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/integrations/test_mjx_dynamics.py +0 -0
  315. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/solvers/__init__.py +0 -0
  316. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/__init__.py +0 -0
  317. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/__init__.py +0 -0
  318. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_arithmetic.py +0 -0
  319. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_array.py +0 -0
  320. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_constraint.py +0 -0
  321. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_expr.py +0 -0
  322. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_lie.py +0 -0
  323. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_linalg.py +0 -0
  324. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_logic.py +0 -0
  325. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_math.py +0 -0
  326. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_node_reference.py +0 -0
  327. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_parameters.py +0 -0
  328. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_scaling.py +0 -0
  329. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_spatial.py +0 -0
  330. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_stl.py +0 -0
  331. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_variable.py +0 -0
  332. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/expr/test_vmap.py +0 -0
  333. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/__init__.py +0 -0
  334. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_array.py +0 -0
  335. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_constraint.py +0 -0
  336. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_lie.py +0 -0
  337. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_linalg.py +0 -0
  338. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_load.py +0 -0
  339. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_logic.py +0 -0
  340. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_math.py +0 -0
  341. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_parser.py +0 -0
  342. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_spatial.py +0 -0
  343. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_stl.py +0 -0
  344. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_tokenizer.py +0 -0
  345. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/parser/test_vmap.py +0 -0
  346. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/test_augmentation.py +0 -0
  347. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/test_hashing.py +0 -0
  348. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/test_lower_cvxpy.py +0 -0
  349. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/test_lower_jax.py +0 -0
  350. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/test_preprocessing.py +0 -0
  351. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/test_sparsity.py +0 -0
  352. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/symbolic/test_unified.py +0 -0
  353. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_autotuning.py +0 -0
  354. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_cvxpygen_optional.py +0 -0
  355. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_discretization.py +0 -0
  356. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_examples.py +0 -0
  357. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_expert.py +0 -0
  358. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_init.py +0 -0
  359. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_integrators.py +0 -0
  360. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_loader.py +0 -0
  361. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_optimization_results.py +0 -0
  362. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_plotting.py +0 -0
  363. {openscvx-0.5.2.dev11 → openscvx-0.5.2.dev13}/tests/test_propagation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.5.2.dev11
3
+ Version: 0.5.2.dev13
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
@@ -168,8 +168,8 @@ of the abstract `PTRSolver` base. Three backends ship today:
168
168
  | Backend | Class | Selector | Notes |
169
169
  |---------|-------|----------|-------|
170
170
  | CVXPy (default) | `CVXPyPTRSolver` | `solver={"backend": "cvxpy"}` (default) | DCP graph via CVXPy, dispatched to QOCO / CLARABEL / etc. Supports user `.convex()` constraints, cross-node constraints, CTCS, and impulsive controls. Optional cvxpygen code generation. |
171
- | QPAX | `QPAXPTRSolver` | `solver={"backend": "qpax"}` | JAX-native QP via `qpax.solve_qp`. Flat `(Q, q, A, b, G, h)` assembly. Supports box / dynamics / CTCS / boundary-Fix; **rejects** user `.convex()`, cross-node, and impulsive at `initialize()` with a clear "use `CVXPyPTRSolver`" message. Enables a path toward an end-to-end JAX-differentiable SCP loop in follow-up work. |
172
- | Moreau | `MoreauPTRSolver` | `solver={"backend": "moreau"}` | JAX-native conic solver (`moreau.jax.Solver`). Sparse CSR assembly; SOC epigraphs for the L1 / pos PTR penalties (fewer variables and rows than QPAX). Warm-starts between SCP iterations. Same supported subset as QPAX. Paves the way for user `.convex()` SOC support in a follow-up. |
171
+ | QPAX | `QPAXPTRSolver` | `solver={"backend": "qpax"}` | JAX-native QP via `qpax.solve_qp`. Flat `(Q, q, A, b, G, h)` assembly. Supports box / dynamics (continuous and impulsive) / CTCS / boundary-Fix; **rejects** user `.convex()` and cross-node at `initialize()` with a clear "use `CVXPyPTRSolver`" message. Enables a path toward an end-to-end JAX-differentiable SCP loop in follow-up work. |
172
+ | Moreau | `MoreauPTRSolver` | `solver={"backend": "moreau"}` | JAX-native conic solver (`moreau.jax.Solver`). Sparse CSR assembly; SOC epigraphs for the L1 / pos PTR penalties (fewer variables and rows than QPAX). Warm-starts between SCP iterations. Same supported subset as QPAX (continuous and impulsive dynamics). Paves the way for user `.convex()` SOC support in a follow-up. |
173
173
 
174
174
  Picking a backend at construction time:
175
175
 
@@ -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.2.dev11'
22
- __version_tuple__ = version_tuple = (0, 5, 2, 'dev11')
21
+ __version__ = version = '0.5.2.dev13'
22
+ __version_tuple__ = version_tuple = (0, 5, 2, 'dev13')
23
23
 
24
- __commit_id__ = commit_id = 'gcff8e4c61'
24
+ __commit_id__ = commit_id = 'ga445685bf'
@@ -22,8 +22,15 @@ in ``jit``, ``jax.grad`` / ``jax.vmap`` can reach through a full SCvx solve
22
22
  Scope:
23
23
  * No user ``.convex()`` constraints — rejected upstream by
24
24
  :meth:`ConvexSolver.lower_convex_constraints`.
25
- * No cross-node or impulsive controls each raises
26
- :class:`NotImplementedError` at :meth:`MoreauPTRSolver.initialize`.
25
+ * No cross-node constraints raises :class:`NotImplementedError` at
26
+ :meth:`MoreauPTRSolver.initialize`.
27
+ * Impulsive controls (``parameterization="impulsive"``) are supported.
28
+ ``D_d`` is absorbed numerically into ``A_d / B_d / C_d`` at update
29
+ time and ``E_d`` enters the dynamics row on the impulsive control
30
+ slice; the initial Fix boundary condition picks up the linearized
31
+ impulse at node 0. The static CSR pattern reserves the
32
+ ``du[0, slice_imp]`` columns in the initial-Fix rows so warm-start
33
+ structure stays valid across iterations.
27
34
  * CTCS constraints are supported; LICQ-style absolute-value inequalities
28
35
  are affine and fit in the nonneg cone.
29
36
 
@@ -75,6 +82,7 @@ if TYPE_CHECKING:
75
82
  from openscvx.lowered import LoweredProblem
76
83
  from openscvx.lowered.jax_constraints import LoweredJaxConstraints
77
84
  from openscvx.lowered.unified import UnifiedControl, UnifiedState
85
+ from openscvx.symbolic.constraint_set import ConstraintSet
78
86
 
79
87
  # Tiny diagonal regularisation added to P on dx/du slots. Moreau's IPM
80
88
  # Cholesky factors (P + Gᵀ diag(z/s) G); keeping the diagonal positive avoids
@@ -218,12 +226,12 @@ class MoreauPTRSolver(PTRSolver):
218
226
  SOCP support in a follow-up.
219
227
 
220
228
  Scope:
221
- Supported — state/control box, dynamics linearization, boundary Fix,
222
- uniform time grid, linearized nodal nonconvex, CTCS LICQ rows.
229
+ Supported — state/control box, dynamics linearization (continuous
230
+ and impulsive), boundary Fix, uniform time grid, linearized nodal
231
+ nonconvex, CTCS LICQ rows.
223
232
 
224
- Not supported — user ``.convex()`` constraints, cross-node
225
- constraints, and impulsive controls. Each raises
226
- :class:`NotImplementedError` with a "use
233
+ Not supported — user ``.convex()`` constraints and cross-node
234
+ constraints. Each raises :class:`NotImplementedError` with a "use
227
235
  :class:`openscvx.solvers.cvxpy_ptr_solver.CVXPyPTRSolver`" pointer.
228
236
 
229
237
  Differentiability hook for future work:
@@ -274,6 +282,11 @@ class MoreauPTRSolver(PTRSolver):
274
282
  self._coo_cols: Optional[np.ndarray] = None
275
283
  self._n_con: int = 0
276
284
 
285
+ # Populated by lower_convex_constraints. Auto-augmented impulsive
286
+ # zero-pin constraints land here; any genuine user .convex() trips
287
+ # the default refusal.
288
+ self._impulsive_pins: List[Tuple[List[int], slice]] = []
289
+
277
290
  # Per-iteration data, set by update_* methods.
278
291
  self._dyn: dict = {}
279
292
  self._cons: dict = {}
@@ -313,23 +326,12 @@ class MoreauPTRSolver(PTRSolver):
313
326
  jax_constraints: Lowered JAX constraints (nodal structure only).
314
327
  dynamics_sparsity: Ignored.
315
328
  constraint_sparsity: Ignored.
316
-
317
- Raises:
318
- NotImplementedError: If impulsive controls are present.
319
329
  """
320
330
  del dynamics_sparsity, constraint_sparsity
321
331
 
322
332
  n_x = len(x_unified.max)
323
333
  n_u = len(u_unified.max)
324
334
 
325
- slice_imp = u_unified.slice_impulsive
326
- if slice_imp.stop > slice_imp.start:
327
- raise NotImplementedError(
328
- "MoreauPTRSolver does not support impulsive controls "
329
- f"(u.slice_impulsive = {slice_imp!r}). "
330
- "Use CVXPyPTRSolver for problems with impulsive dynamics."
331
- )
332
-
333
335
  S_x, c_x = self._scaling(x_unified)
334
336
  S_u, c_u = self._scaling(u_unified)
335
337
 
@@ -345,6 +347,28 @@ class MoreauPTRSolver(PTRSolver):
345
347
  self.layout = _ConicLayout(N=N, n_x=n_x, n_u=n_u, n_nodal=len(jax_constraints.nodal))
346
348
  self._jax_constraints = jax_constraints
347
349
 
350
+ def lower_convex_constraints(
351
+ self,
352
+ constraints: "ConstraintSet",
353
+ parameters: Optional[Dict] = None,
354
+ ) -> Tuple[List, Dict]:
355
+ """Absorb auto-generated impulsive zero-pin constraints; refuse the rest.
356
+
357
+ :func:`openscvx.symbolic.lower._augment_impulsive_constraints` injects
358
+ ``Control == 0`` equalities at non-impulse nodes for every impulsive
359
+ control. Those constraints live in ``constraints.nodal_convex`` even
360
+ though no user ``.convex()`` was written; we recognize their fixed
361
+ shape and stash a pin list for the structural pass / assembler to
362
+ emit as plain zero-cone rows. Anything that doesn't match the
363
+ auto-augmentation shape (e.g. a genuine user ``.convex()`` SOC) falls
364
+ through to the default refusal in :class:`ConvexSolver`.
365
+ """
366
+ pins = self._extract_impulsive_pins(constraints)
367
+ if pins is None:
368
+ return super().lower_convex_constraints(constraints, parameters)
369
+ self._impulsive_pins = pins
370
+ return [], {}
371
+
348
372
  def initialize(self, lowered: "LoweredProblem", settings: "Config") -> None:
349
373
  """Build the static conic structure and construct ``moreau.jax.Solver``.
350
374
 
@@ -359,7 +383,7 @@ class MoreauPTRSolver(PTRSolver):
359
383
 
360
384
  Raises:
361
385
  RuntimeError: If :meth:`create_variables` was not called first.
362
- NotImplementedError: If cross-node or impulsive controls are present.
386
+ NotImplementedError: If cross-node constraints are present.
363
387
  """
364
388
  if self.layout is None:
365
389
  raise RuntimeError(
@@ -373,11 +397,6 @@ class MoreauPTRSolver(PTRSolver):
373
397
  f"({len(lowered.jax_constraints.cross_node)} defined). "
374
398
  "Use CVXPyPTRSolver."
375
399
  )
376
- slice_imp = settings.sim.u.slice_impulsive
377
- if slice_imp.stop > slice_imp.start:
378
- raise NotImplementedError(
379
- "MoreauPTRSolver does not support impulsive controls. Use CVXPyPTRSolver."
380
- )
381
400
 
382
401
  self._settings = settings
383
402
  # Reset warm-start on new initialization (problem structure may change).
@@ -456,15 +475,42 @@ class MoreauPTRSolver(PTRSolver):
456
475
  D_d: Optional[np.ndarray] = None,
457
476
  E_d: Optional[np.ndarray] = None,
458
477
  ) -> None:
459
- # Impulsive rejected at initialize(); D_d / E_d / x_prop_plus dropped.
460
- del x_prop_plus, D_d, E_d
478
+ A_eff = np.asarray(A_d, dtype=float)
479
+ B_eff = np.asarray(B_d, dtype=float)
480
+ C_eff = np.asarray(C_d, dtype=float)
481
+
482
+ # Absorb the impulsive state Jacobian into the continuous step
483
+ # matrices so the assembly row keeps a single A_d·dx + B_d·du term,
484
+ # matching the recipe in CVXPyPTRSolver.update_dynamics_linearization
485
+ # at openscvx/solvers/cvxpy_ptr_solver.py:636-654.
486
+ if D_d is not None:
487
+ D_arr = np.asarray(D_d, dtype=float)
488
+ if D_arr.ndim == 3 and D_arr.shape[0] == A_eff.shape[0] + 1:
489
+ D_steps = D_arr[1:]
490
+ elif D_arr.ndim == 3 and D_arr.shape[0] == A_eff.shape[0]:
491
+ D_steps = D_arr
492
+ else:
493
+ raise ValueError(
494
+ "Unexpected D_d shape for dynamics update: "
495
+ f"{D_arr.shape}, expected "
496
+ f"{(A_eff.shape[0] + 1, A_eff.shape[1], A_eff.shape[2])} "
497
+ f"or {(A_eff.shape[0], A_eff.shape[1], A_eff.shape[2])}."
498
+ )
499
+ A_eff = np.einsum("kij,kjl->kil", D_steps, A_eff)
500
+ B_eff = np.einsum("kij,kjl->kil", D_steps, B_eff)
501
+ C_eff = np.einsum("kij,kjl->kil", D_steps, C_eff)
502
+
461
503
  self._dyn = {
462
504
  "x_bar": np.asarray(x_bar, dtype=float),
463
505
  "u_bar": np.asarray(u_bar, dtype=float),
464
- "A_d": np.asarray(A_d, dtype=float),
465
- "B_d": np.asarray(B_d, dtype=float),
466
- "C_d": np.asarray(C_d, dtype=float),
506
+ "A_d": A_eff,
507
+ "B_d": B_eff,
508
+ "C_d": C_eff,
467
509
  "x_prop": np.asarray(x_prop, dtype=float),
510
+ "x_prop_plus": (
511
+ np.asarray(x_prop_plus, dtype=float) if x_prop_plus is not None else None
512
+ ),
513
+ "E_d": np.asarray(E_d, dtype=float) if E_d is not None else None,
468
514
  }
469
515
 
470
516
  def update_constraint_linearizations(
@@ -591,15 +637,32 @@ class MoreauPTRSolver(PTRSolver):
591
637
  add(row, L.nu_vb_idx(c_idx, node))
592
638
  row += 1
593
639
 
594
- # Fix boundary conditions (initial / terminal)
640
+ # Fix boundary conditions (initial / terminal). Under impulsive
641
+ # control the initial Fix row couples the post-impulse state at
642
+ # node 0 to du[0, slice_imp]; declare those columns structurally
643
+ # nonzero here so the static CSR pattern accommodates the
644
+ # numerical values emitted in _assemble_conic.
645
+ slice_imp = settings.sim.u.slice_impulsive
646
+ has_impulsive = slice_imp.stop > slice_imp.start
595
647
  for i in range(settings.sim.true_state_slice.start, settings.sim.true_state_slice.stop):
596
648
  if settings.sim.x.initial_type[i] == "Fix":
597
649
  add(row, L.x_idx(0, i))
650
+ if has_impulsive:
651
+ for j in range(slice_imp.start, slice_imp.stop):
652
+ add(row, L.du_idx(0, j))
598
653
  row += 1
599
654
  if settings.sim.x.final_type[i] == "Fix":
600
655
  add(row, L.x_idx(N - 1, i))
601
656
  row += 1
602
657
 
658
+ # Impulsive zero-pin equalities: u[node, j] = const, one row per
659
+ # (node, j) absorbed by lower_convex_constraints.
660
+ for nodes, ctrl_slice in self._impulsive_pins:
661
+ for node in nodes:
662
+ for j in range(ctrl_slice.start, ctrl_slice.stop):
663
+ add(row, L.u_idx(node, j))
664
+ row += 1
665
+
603
666
  # Uniform time grid: u[k,j] − u[k-1,j] = 0
604
667
  if settings.sim._uniform_time_grid:
605
668
  td = settings.sim.time_dilation_slice
@@ -725,8 +788,11 @@ class MoreauPTRSolver(PTRSolver):
725
788
  inv_S_x = self._inv_S_x_diag
726
789
  inv_S_u = self._inv_S_u_diag
727
790
  S_x = self._S_x_diag
791
+ S_u = self._S_u_diag
728
792
  c_x = self._c_x
729
793
  c_u = self._c_u
794
+ slice_imp = settings.sim.u.slice_impulsive
795
+ has_impulsive = slice_imp.stop > slice_imp.start
730
796
 
731
797
  lam_prox = self._pen["lam_prox"] # (N, n_x + n_u)
732
798
  lam_cost = self._pen["lam_cost"] # scalar or (n_x,)
@@ -739,6 +805,8 @@ class MoreauPTRSolver(PTRSolver):
739
805
  B_d = self._dyn["B_d"] # (N-1, n_x, n_u)
740
806
  C_d = self._dyn["C_d"] # (N-1, n_x, n_u)
741
807
  x_prop = self._dyn["x_prop"] # (N-1, n_x)
808
+ E_d_arr = self._dyn["E_d"] # (N, n_x, n_u) or None
809
+ x_prop_plus_arr = self._dyn["x_prop_plus"] # (N, n_x) or None
742
810
 
743
811
  lam_cost_arr = np.broadcast_to(lam_cost, (settings.sim.n_states,))
744
812
 
@@ -801,15 +869,23 @@ class MoreauPTRSolver(PTRSolver):
801
869
  for j in range(n_u):
802
870
  emit([1.0, -1.0], inv_S_u[j] * (u_bar[k, j] - c_u[j]))
803
871
 
804
- # Dynamics (continuous FOH):
805
- # x[k] − A_blk·dx[k-1] − B_blk·du[k-1] − C_blk·du[k] − nu[k-1]
806
- # = inv_S_x·(x_prop[k-1] − c_x)
872
+ # Dynamics (continuous FOH, with optional impulsive coupling at node k):
873
+ # x[k] − A_blk·dx[k-1] − B_blk·du[k-1] − C_blk·du[k]
874
+ # E_blk·du[k][slice_imp]nu[k-1]
875
+ # = inv_S_x·(x_prop_plus[k] − c_x) if has_impulsive
876
+ # = inv_S_x·(x_prop[k-1] − c_x) otherwise
877
+ # Mirrors CVXPyPTRSolver.constraints at cvxpy_ptr_solver.py:506-530.
807
878
  for k in range(1, N):
808
879
  kp = k - 1
809
880
  A_blk = (inv_S_x[:, None] * A_d[kp]) * S_x[None, :]
810
- B_blk = (inv_S_x[:, None] * B_d[kp]) * self._S_u_diag[None, :]
811
- C_blk = (inv_S_x[:, None] * C_d[kp]) * self._S_u_diag[None, :]
812
- rhs_k = inv_S_x * (x_prop[kp] - c_x)
881
+ B_blk = (inv_S_x[:, None] * B_d[kp]) * S_u[None, :]
882
+ C_blk = (inv_S_x[:, None] * C_d[kp]) * S_u[None, :]
883
+ if has_impulsive:
884
+ E_blk = (inv_S_x[:, None] * E_d_arr[k]) * S_u[None, :]
885
+ rhs_k = inv_S_x * (x_prop_plus_arr[k] - c_x)
886
+ else:
887
+ E_blk = None
888
+ rhs_k = inv_S_x * (x_prop[kp] - c_x)
813
889
  for i in range(n_x):
814
890
  # Coefficients in the same col order as _structural_pass added them,
815
891
  # then sorted by scipy within the row — values line up correctly.
@@ -818,7 +894,10 @@ class MoreauPTRSolver(PTRSolver):
818
894
  coeffs.append(-A_blk[i, j]) # dx[kp, j]
819
895
  for j in range(n_u):
820
896
  coeffs.append(-B_blk[i, j]) # du[kp, j]
821
- coeffs.append(-C_blk[i, j]) # du[k, j]
897
+ c_kj = -C_blk[i, j]
898
+ if has_impulsive and slice_imp.start <= j < slice_imp.stop:
899
+ c_kj -= E_blk[i, j]
900
+ coeffs.append(c_kj) # du[k, j]
822
901
  coeffs.append(-1.0) # nu[kp, i]
823
902
  emit(coeffs, rhs_k[i])
824
903
 
@@ -839,15 +918,25 @@ class MoreauPTRSolver(PTRSolver):
839
918
  coeffs.append(-1.0) # nu_vb[c, node]
840
919
  emit(coeffs, -g[node])
841
920
 
842
- # Fix boundary conditions
921
+ # Fix boundary conditions. Under impulsive control the initial Fix
922
+ # row couples x[0, i] to du[0, slice_imp] via the linearized impulse
923
+ # Jacobian (CVXPy reference: cvxpy_ptr_solver.py:484-495). Emit
924
+ # coefficients in ascending column-index order so they match the
925
+ # CSR sort applied to the structural pass.
843
926
  for i in range(settings.sim.true_state_slice.start, settings.sim.true_state_slice.stop):
844
927
  if settings.sim.x.initial_type[i] == "Fix":
845
- if self._x_init is None:
846
- raise RuntimeError(
847
- f"Fix initial condition on state {i} requires x_init; "
848
- "call update_boundary_conditions() before solve()."
849
- )
850
- emit([S_x[i]], self._x_init[i] - c_x[i])
928
+ if has_impulsive:
929
+ coeffs = [S_x[i]]
930
+ for j in range(slice_imp.start, slice_imp.stop):
931
+ coeffs.append(-E_d_arr[0, i, j] * S_u[j])
932
+ emit(coeffs, x_prop_plus_arr[0, i] - c_x[i])
933
+ else:
934
+ if self._x_init is None:
935
+ raise RuntimeError(
936
+ f"Fix initial condition on state {i} requires x_init; "
937
+ "call update_boundary_conditions() before solve()."
938
+ )
939
+ emit([S_x[i]], self._x_init[i] - c_x[i])
851
940
  if settings.sim.x.final_type[i] == "Fix":
852
941
  if self._x_term is None:
853
942
  raise RuntimeError(
@@ -856,6 +945,13 @@ class MoreauPTRSolver(PTRSolver):
856
945
  )
857
946
  emit([S_x[i]], self._x_term[i] - c_x[i])
858
947
 
948
+ # Impulsive zero-pin equalities: u[node, j] = −inv_S_u[j]·c_u[j].
949
+ # Mirrors CVXPy's lowering of ``u_nonscaled[node][slice_imp] == 0``.
950
+ for nodes, ctrl_slice in self._impulsive_pins:
951
+ for node in nodes:
952
+ for j in range(ctrl_slice.start, ctrl_slice.stop):
953
+ emit([1.0], -inv_S_u[j] * c_u[j])
954
+
859
955
  # Uniform time grid: u[k,j] − u[k-1,j] = 0
860
956
  if settings.sim._uniform_time_grid:
861
957
  td = settings.sim.time_dilation_slice
@@ -22,7 +22,7 @@ Backends:
22
22
 
23
23
  from abc import abstractmethod
24
24
  from dataclasses import dataclass
25
- from typing import TYPE_CHECKING, List, Tuple, Union
25
+ from typing import TYPE_CHECKING, List, Optional, Tuple, Union
26
26
 
27
27
  import numpy as np
28
28
 
@@ -30,6 +30,7 @@ from .base import ConvexSolver
30
30
 
31
31
  if TYPE_CHECKING:
32
32
  from openscvx.lowered.unified import UnifiedControl, UnifiedState
33
+ from openscvx.symbolic.constraint_set import ConstraintSet
33
34
 
34
35
 
35
36
  @dataclass
@@ -174,6 +175,51 @@ class PTRSolver(ConvexSolver):
174
175
  """
175
176
  raise NotImplementedError
176
177
 
178
+ @staticmethod
179
+ def _extract_impulsive_pins(
180
+ constraints: "ConstraintSet",
181
+ ) -> Optional[List[Tuple[List[int], slice]]]:
182
+ """Recognize auto-generated impulsive zero-pin constraints.
183
+
184
+ :func:`openscvx.symbolic.lower._augment_impulsive_constraints` injects
185
+ a ``Control == 0`` equality at every non-impulse node for each
186
+ impulsive control. CVXPy lowers these alongside user ``.convex()``
187
+ constraints, but JAX backends that otherwise refuse user
188
+ ``.convex()`` constraints still need to honor them.
189
+
190
+ This helper detects that exact shape — a ``NodalConstraint`` wrapping
191
+ ``Equality(Control, Constant(0))`` over a list of nodes — and
192
+ returns the implied ``(nodes, slice)`` pin list. If any
193
+ ``nodal_convex`` entry doesn't match the auto-augmentation shape,
194
+ returns ``None`` so the caller can fall back to the default
195
+ refusal.
196
+
197
+ Cross-node convex constraints are never produced by the
198
+ auto-augmentation, so any presence aborts recognition.
199
+ """
200
+ from openscvx.symbolic.expr.constraint import Equality, NodalConstraint
201
+ from openscvx.symbolic.expr.control import Control
202
+ from openscvx.symbolic.expr.expr import Constant
203
+
204
+ if constraints.cross_node_convex:
205
+ return None
206
+
207
+ pins: List[Tuple[List[int], slice]] = []
208
+ for entry in constraints.nodal_convex:
209
+ if not isinstance(entry, NodalConstraint):
210
+ return None
211
+ inner = entry.constraint
212
+ if not isinstance(inner, Equality):
213
+ return None
214
+ rhs = inner.rhs
215
+ if not isinstance(rhs, Constant) or not np.all(np.asarray(rhs.value) == 0):
216
+ return None
217
+ lhs = inner.lhs
218
+ if not isinstance(lhs, Control) or lhs._slice is None:
219
+ return None
220
+ pins.append(([int(k) for k in entry.nodes], lhs._slice))
221
+ return pins
222
+
177
223
  @staticmethod
178
224
  def _scaling(unified: Union["UnifiedState", "UnifiedControl"]) -> Tuple[np.ndarray, np.ndarray]:
179
225
  """Compute the affine scaling matrices ``(S, c)`` for a unified