openscvx 0.5.2.dev12__tar.gz → 0.5.2.dev14__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.dev12/openscvx.egg-info → openscvx-0.5.2.dev14}/PKG-INFO +1 -1
  2. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UnderTheHood/lowering_architecture.md +2 -2
  3. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/_version.py +3 -3
  4. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/solvers/base.py +97 -6
  5. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/solvers/moreau_ptr_solver.py +198 -51
  6. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/solvers/ptr_solver.py +47 -1
  7. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/solvers/qpax_ptr_solver.py +208 -71
  8. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14/openscvx.egg-info}/PKG-INFO +1 -1
  9. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/solvers/test_moreau_ptr_solver.py +76 -1
  10. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/solvers/test_qpax_ptr_solver.py +72 -1
  11. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_impulsive.py +112 -16
  12. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/assets/logo.svg +0 -0
  13. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/release-drafter.yml +0 -0
  14. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/_docs.yml +0 -0
  15. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/branch-name.yml +0 -0
  16. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/docs.yml +0 -0
  17. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/lint.yml +0 -0
  18. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/nightly.yml +0 -0
  19. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/release-drafter.yml +0 -0
  20. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/release.yml +0 -0
  21. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/tests-integration.yml +0 -0
  22. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.github/workflows/tests-unit.yml +0 -0
  23. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.gitignore +0 -0
  24. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/.gitmodules +0 -0
  25. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/CONTRIBUTING.md +0 -0
  26. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/LICENSE +0 -0
  27. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/README.md +0 -0
  28. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/Foundations/constraint_reformulation.md +0 -0
  29. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/Foundations/control_parameterization.md +0 -0
  30. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/Foundations/discretization.md +0 -0
  31. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/Foundations/ocp.md +0 -0
  32. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/Foundations/scvx.md +0 -0
  33. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/Foundations/time_dilation.md +0 -0
  34. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  35. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/00_introduction.md +0 -0
  36. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  37. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  38. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  39. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  40. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/05_visualization.md +0 -0
  41. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/06_logic.md +0 -0
  42. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/07_lie.md +0 -0
  43. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/UsersGuide/08_mpcc.md +0 -0
  44. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/favicon.png +0 -0
  45. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/images/ct-scvx_dark.png +0 -0
  46. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/images/ct-scvx_light.png +0 -0
  47. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/images/ctcs_dark.png +0 -0
  48. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/images/ctcs_light.png +0 -0
  49. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/images/problem_class_dark.png +0 -0
  50. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/images/problem_class_light.png +0 -0
  51. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/logo.svg +0 -0
  52. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/openscvx_logo_square.png +0 -0
  53. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/viser-client/index.html +0 -0
  54. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/viser-recordings/drone_racing.viser +0 -0
  55. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
  56. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/citation.md +0 -0
  57. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/examples.md +0 -0
  58. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/index.md +0 -0
  59. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/javascripts/mathjax.js +0 -0
  60. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/docs/versions.json +0 -0
  61. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/_viser_embed_export.py +0 -0
  62. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/abstract/brachistochrone.py +0 -0
  63. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/abstract/hypersensitive.py +0 -0
  64. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/abstract/impulsive.py +0 -0
  65. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/abstract/stl_integer_variable.py +0 -0
  66. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/abstract/stl_or.py +0 -0
  67. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/7_dof_arm.py +0 -0
  68. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/_camera.py +0 -0
  69. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/_render.py +0 -0
  70. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/_sensor_view.py +0 -0
  71. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/dr_vp_polytope.py +0 -0
  72. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/franka_fr3v2_pick_place.py +0 -0
  73. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/logo.py +0 -0
  74. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/animations/obstacle_avoidance_vmap.py +0 -0
  75. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/arm/3_dof_arm.py +0 -0
  76. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/arm/7_dof_arm.py +0 -0
  77. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/arm/7_dof_arm_collision.py +0 -0
  78. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/arm/7_dof_arm_vp.py +0 -0
  79. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/arm/franka_fr3v2_pick_place.py +0 -0
  80. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
  81. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/car/dubins_car.py +0 -0
  82. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/car/dubins_car_disjoint.py +0 -0
  83. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/car/dubins_car_obstacle_conditional.py +0 -0
  84. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/car/dubins_car_obstacle_stl.py +0 -0
  85. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/car/dubins_car_stl_or.py +0 -0
  86. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/car/dubins_car_waypoint_stl.py +0 -0
  87. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/cinema_vp.py +0 -0
  88. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/dr_double_integrator.py +0 -0
  89. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/dr_vp.py +0 -0
  90. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/dr_vp_nodal.py +0 -0
  91. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/dr_vp_polytope.py +0 -0
  92. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/drone_racing.py +0 -0
  93. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/logo.py +0 -0
  94. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/logo_utils/acl_logo.svg +0 -0
  95. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/logo_utils/svg_path_utils.py +0 -0
  96. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/obstacle_avoidance.py +0 -0
  97. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  98. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  99. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mjx/cartpole_mjx.py +0 -0
  100. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mjx/double_cartpole_mjx.py +0 -0
  101. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mjx/skydio_x2_mjx.py +0 -0
  102. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
  103. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mjx/triple_cartpole_game.py +0 -0
  104. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mjx/triple_cartpole_mjx.py +0 -0
  105. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mpc/double_integrator_discrete.py +0 -0
  106. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mpc/double_integrator_drone_racing.py +0 -0
  107. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mpc/dubins_car_circle_analytical.py +0 -0
  108. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mpc/dubins_car_circle_discrete.py +0 -0
  109. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
  110. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/plotting.py +0 -0
  111. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/plotting_viser.py +0 -0
  112. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/3DoF_pdg_realtime.py +0 -0
  113. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/6DoF_pdg_realtime.py +0 -0
  114. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
  115. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
  116. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
  117. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
  118. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
  119. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
  120. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/cinema_vp_realtime.py +0 -0
  121. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/drone_racing_realtime.py +0 -0
  122. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/dubins_car_realtime.py +0 -0
  123. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  124. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/rocket/3DoF_pdg.py +0 -0
  125. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/rocket/6DoF_pdg.py +0 -0
  126. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/spacecraft/halo_orbit.py +0 -0
  127. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/spacecraft/hohmann_transfer.py +0 -0
  128. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/spacecraft/let_transfer.py +0 -0
  129. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/examples/spacecraft/proxops_cw.py +0 -0
  130. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/ctlos_cine.gif +0 -0
  131. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/ctlos_dr.gif +0 -0
  132. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/dtlos_cine.gif +0 -0
  133. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/dtlos_dr.gif +0 -0
  134. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/openscvx_logo.svg +0 -0
  135. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/openscvx_logo_square.png +0 -0
  136. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/oscvx_structure_full_dark.svg +0 -0
  137. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/figures/video_preview.png +0 -0
  138. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/__init__.py +0 -0
  139. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/assets/stylesheets/custom.css +0 -0
  140. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
  141. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/assets/stylesheets/home-hero.css +0 -0
  142. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/assets/stylesheets/home-viser.css +0 -0
  143. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/home.html +0 -0
  144. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/main.html +0 -0
  145. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/partials/home-diagram.html +0 -0
  146. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/partials/home-dropin-banner.html +0 -0
  147. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/partials/home-hero.html +0 -0
  148. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/partials/home-pipeline.html +0 -0
  149. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/material/overrides/partials/home-viser-strip.html +0 -0
  150. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/mkdocs.yml +0 -0
  151. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/__init__.py +0 -0
  152. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/__main__.py +0 -0
  153. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/__init__.py +0 -0
  154. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/autotuner/__init__.py +0 -0
  155. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
  156. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
  157. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
  158. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
  159. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/base.py +0 -0
  160. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/optimization_results.py +0 -0
  161. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/scvx/__init__.py +0 -0
  162. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
  163. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/algorithms/weights.py +0 -0
  164. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/config.py +0 -0
  165. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/__init__.py +0 -0
  166. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/base.py +0 -0
  167. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/discretize_linearize.py +0 -0
  168. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/linearize_discretize.py +0 -0
  169. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
  170. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/sparse_utils/__init__.py +0 -0
  171. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
  172. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
  173. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/expert/__init__.py +0 -0
  174. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/expert/byof.py +0 -0
  175. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/expert/lowering.py +0 -0
  176. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/expert/validation.py +0 -0
  177. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/init/__init__.py +0 -0
  178. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/init/interpolation.py +0 -0
  179. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/init/inverse_kinematics.py +0 -0
  180. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/integrations/__init__.py +0 -0
  181. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/integrations/base.py +0 -0
  182. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/integrations/menagerie.py +0 -0
  183. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/integrations/mjx.py +0 -0
  184. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/integrators/__init__.py +0 -0
  185. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/integrators/diffrax.py +0 -0
  186. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/integrators/runge_kutta.py +0 -0
  187. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/loader.py +0 -0
  188. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/__init__.py +0 -0
  189. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/cvxpy_constraints.py +0 -0
  190. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/cvxpy_variables.py +0 -0
  191. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/dynamics.py +0 -0
  192. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/jax_constraints.py +0 -0
  193. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/parameters.py +0 -0
  194. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/problem.py +0 -0
  195. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/lowered/unified.py +0 -0
  196. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/__init__.py +0 -0
  197. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/plotting.py +0 -0
  198. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/scp_iteration.py +0 -0
  199. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/viser/__init__.py +0 -0
  200. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/viser/animated.py +0 -0
  201. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/viser/orbits.py +0 -0
  202. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/viser/plotly_integration.py +0 -0
  203. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/viser/primitives.py +0 -0
  204. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/viser/scp.py +0 -0
  205. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/plotting/viser/server.py +0 -0
  206. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/problem.py +0 -0
  207. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/propagation/__init__.py +0 -0
  208. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/propagation/post_processing.py +0 -0
  209. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/propagation/propagation.py +0 -0
  210. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/solvers/__init__.py +0 -0
  211. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
  212. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/__init__.py +0 -0
  213. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/augmentation.py +0 -0
  214. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/builder.py +0 -0
  215. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/constraint_set.py +0 -0
  216. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/__init__.py +0 -0
  217. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/arithmetic.py +0 -0
  218. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/array.py +0 -0
  219. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/constraint.py +0 -0
  220. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/control.py +0 -0
  221. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/expr.py +0 -0
  222. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  223. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  224. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/lie/se3.py +0 -0
  225. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/lie/so3.py +0 -0
  226. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/linalg.py +0 -0
  227. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/logic.py +0 -0
  228. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/math.py +0 -0
  229. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/parameter.py +0 -0
  230. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/spatial.py +0 -0
  231. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/state.py +0 -0
  232. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/stl.py +0 -0
  233. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/stljax.py +0 -0
  234. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/time.py +0 -0
  235. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/variable.py +0 -0
  236. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/expr/vmap.py +0 -0
  237. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/hashing.py +0 -0
  238. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lower.py +0 -0
  239. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/__init__.py +0 -0
  240. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  241. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  242. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  243. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  244. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  245. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  246. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  247. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  248. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  249. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  250. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  251. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  252. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  253. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  254. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  255. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  256. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  257. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
  258. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  259. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  260. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  261. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  262. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
  263. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  264. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  265. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  266. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  267. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
  268. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  269. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/__init__.py +0 -0
  270. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/_registry.py +0 -0
  271. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/array.py +0 -0
  272. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/constraint.py +0 -0
  273. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/lie.py +0 -0
  274. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/linalg.py +0 -0
  275. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/logic.py +0 -0
  276. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/math.py +0 -0
  277. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/parser.py +0 -0
  278. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/spatial.py +0 -0
  279. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/stl.py +0 -0
  280. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/stljax.py +0 -0
  281. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/parser/tokenizer.py +0 -0
  282. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/preprocessing.py +0 -0
  283. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/problem.py +0 -0
  284. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/sparsity.py +0 -0
  285. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/symbolic/unified.py +0 -0
  286. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/utils/__init__.py +0 -0
  287. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/utils/cache.py +0 -0
  288. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/utils/caching.py +0 -0
  289. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/utils/printing.py +0 -0
  290. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/utils/profiling.py +0 -0
  291. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx/utils/utils.py +0 -0
  292. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx.egg-info/SOURCES.txt +0 -0
  293. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx.egg-info/dependency_links.txt +0 -0
  294. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx.egg-info/entry_points.txt +0 -0
  295. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx.egg-info/requires.txt +0 -0
  296. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/openscvx.egg-info/top_level.txt +0 -0
  297. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/pyproject.toml +0 -0
  298. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/scripts/gen_example_pages.py +0 -0
  299. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/scripts/gen_ref_pages.py +0 -0
  300. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
  301. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/setup.cfg +0 -0
  302. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/__init__.py +0 -0
  303. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/_marks.py +0 -0
  304. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/brachistochrone_analytical.py +0 -0
  305. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/conftest.py +0 -0
  306. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/expr/__init__.py +0 -0
  307. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/expr/test_gmsr.py +0 -0
  308. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/fixtures/brachistochrone.json +0 -0
  309. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/fixtures/brachistochrone.yaml +0 -0
  310. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/hohmann_analytical.py +0 -0
  311. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/integrations/__init__.py +0 -0
  312. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/integrations/test_mjx.py +0 -0
  313. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/integrations/test_mjx_dynamics.py +0 -0
  314. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/solvers/__init__.py +0 -0
  315. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/__init__.py +0 -0
  316. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/__init__.py +0 -0
  317. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_arithmetic.py +0 -0
  318. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_array.py +0 -0
  319. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_constraint.py +0 -0
  320. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_expr.py +0 -0
  321. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_lie.py +0 -0
  322. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_linalg.py +0 -0
  323. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_logic.py +0 -0
  324. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_math.py +0 -0
  325. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_node_reference.py +0 -0
  326. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_parameters.py +0 -0
  327. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_scaling.py +0 -0
  328. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_spatial.py +0 -0
  329. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_stl.py +0 -0
  330. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_variable.py +0 -0
  331. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/expr/test_vmap.py +0 -0
  332. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/__init__.py +0 -0
  333. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_array.py +0 -0
  334. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_constraint.py +0 -0
  335. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_lie.py +0 -0
  336. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_linalg.py +0 -0
  337. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_load.py +0 -0
  338. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_logic.py +0 -0
  339. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_math.py +0 -0
  340. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_parser.py +0 -0
  341. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_spatial.py +0 -0
  342. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_stl.py +0 -0
  343. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_tokenizer.py +0 -0
  344. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/parser/test_vmap.py +0 -0
  345. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/test_augmentation.py +0 -0
  346. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/test_hashing.py +0 -0
  347. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/test_lower_cvxpy.py +0 -0
  348. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/test_lower_jax.py +0 -0
  349. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/test_preprocessing.py +0 -0
  350. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/test_sparsity.py +0 -0
  351. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/symbolic/test_unified.py +0 -0
  352. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_autotuning.py +0 -0
  353. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_brachistochrone.py +0 -0
  354. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_cvxpygen_optional.py +0 -0
  355. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_discretization.py +0 -0
  356. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_examples.py +0 -0
  357. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_expert.py +0 -0
  358. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_init.py +0 -0
  359. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_integrators.py +0 -0
  360. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_loader.py +0 -0
  361. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_optimization_results.py +0 -0
  362. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/tests/test_plotting.py +0 -0
  363. {openscvx-0.5.2.dev12 → openscvx-0.5.2.dev14}/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.dev12
3
+ Version: 0.5.2.dev14
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.dev12'
22
- __version_tuple__ = version_tuple = (0, 5, 2, 'dev12')
21
+ __version__ = version = '0.5.2.dev14'
22
+ __version_tuple__ = version_tuple = (0, 5, 2, 'dev14')
23
23
 
24
- __commit_id__ = commit_id = 'ga71a6db14'
24
+ __commit_id__ = commit_id = 'g78ea9d746'
@@ -281,9 +281,24 @@ class PTRSolverSpec(BaseModel):
281
281
  or ``"moreau"``
282
282
  (:class:`openscvx.solvers.moreau_ptr_solver.MoreauPTRSolver`).
283
283
 
284
- ``cvx_solver``, ``cvxpygen``, and ``cvxpygen_override`` are CVXPy-only;
285
- setting them under ``backend="qpax"`` or ``backend="moreau"`` is a
286
- configuration error.
284
+ Backend-specific fields mirror the constructor parameters of each class.
285
+ Fields that don't belong to the active backend are a configuration error
286
+ and raise ``ValidationError``.
287
+
288
+ CVXPy-only:
289
+ ``cvx_solver``, ``cvxpygen``, ``cvxpygen_override``.
290
+
291
+ QPAX-only:
292
+ ``solver_tol``.
293
+
294
+ Moreau-only:
295
+ ``verbose``, ``device``, ``tol_gap_abs``, ``tol_feas``.
296
+
297
+ Shared between QPAX and Moreau:
298
+ ``max_iter``.
299
+
300
+ All backends:
301
+ ``solver_args`` (escape hatch for settings not covered by named fields).
287
302
 
288
303
  !!! warning
289
304
  Enabling ``cvxpygen`` currently disables sparse parameter declarations.
@@ -294,11 +309,27 @@ class PTRSolverSpec(BaseModel):
294
309
 
295
310
  type: Literal["PTRSolver"] = "PTRSolver"
296
311
  backend: Literal["cvxpy", "qpax", "moreau"] = "cvxpy"
312
+
313
+ # CVXPy-only
297
314
  cvx_solver: Optional[str] = None
298
- solver_args: Optional[Dict[str, Any]] = None
299
315
  cvxpygen: bool = False
300
316
  cvxpygen_override: bool = False
301
317
 
318
+ # QPAX-specific
319
+ solver_tol: Optional[float] = None
320
+
321
+ # Shared between QPAX and Moreau
322
+ max_iter: Optional[int] = None
323
+
324
+ # Moreau-specific
325
+ verbose: Optional[bool] = None
326
+ device: Optional[str] = None
327
+ tol_gap_abs: Optional[float] = None
328
+ tol_feas: Optional[float] = None
329
+
330
+ # Escape hatch for all backends
331
+ solver_args: Optional[Dict[str, Any]] = None
332
+
302
333
  model_config = ConfigDict(extra="forbid")
303
334
 
304
335
  @model_validator(mode="after")
@@ -318,6 +349,45 @@ class PTRSolverSpec(BaseModel):
318
349
  f"{offenders} only valid for backend='cvxpy'; "
319
350
  "remove these fields or set backend='cvxpy'."
320
351
  )
352
+ if self.backend == "cvxpy":
353
+ offenders = [
354
+ name
355
+ for name, value in (
356
+ ("solver_tol", self.solver_tol),
357
+ ("max_iter", self.max_iter),
358
+ ("verbose", self.verbose),
359
+ ("device", self.device),
360
+ ("tol_gap_abs", self.tol_gap_abs),
361
+ ("tol_feas", self.tol_feas),
362
+ )
363
+ if value is not None
364
+ ]
365
+ if offenders:
366
+ raise ValueError(
367
+ f"{offenders} not valid for backend='cvxpy'; pass these "
368
+ "via solver_args or switch to the qpax / moreau backend."
369
+ )
370
+ if self.backend == "qpax":
371
+ offenders = [
372
+ name
373
+ for name, value in (
374
+ ("verbose", self.verbose),
375
+ ("device", self.device),
376
+ ("tol_gap_abs", self.tol_gap_abs),
377
+ ("tol_feas", self.tol_feas),
378
+ )
379
+ if value is not None
380
+ ]
381
+ if offenders:
382
+ raise ValueError(
383
+ f"{offenders} only valid for backend='moreau'; "
384
+ "remove these fields or set backend='moreau'."
385
+ )
386
+ if self.backend == "moreau" and self.solver_tol is not None:
387
+ raise ValueError(
388
+ "solver_tol is only valid for backend='qpax'; "
389
+ "remove this field or set backend='qpax'."
390
+ )
321
391
  return self
322
392
 
323
393
  def build(self) -> ConvexSolver:
@@ -335,10 +405,31 @@ class PTRSolverSpec(BaseModel):
335
405
  if self.backend == "moreau":
336
406
  from .moreau_ptr_solver import MoreauPTRSolver
337
407
 
338
- return MoreauPTRSolver(solver_args=self.solver_args)
408
+ kwargs: Dict[str, Any] = {}
409
+ if self.max_iter is not None:
410
+ kwargs["max_iter"] = self.max_iter
411
+ if self.verbose is not None:
412
+ kwargs["verbose"] = self.verbose
413
+ if self.device is not None:
414
+ kwargs["device"] = self.device
415
+ if self.tol_gap_abs is not None:
416
+ kwargs["tol_gap_abs"] = self.tol_gap_abs
417
+ if self.tol_feas is not None:
418
+ kwargs["tol_feas"] = self.tol_feas
419
+ if self.solver_args is not None:
420
+ kwargs["solver_args"] = self.solver_args
421
+ return MoreauPTRSolver(**kwargs)
422
+
339
423
  from .qpax_ptr_solver import QPAXPTRSolver
340
424
 
341
- return QPAXPTRSolver(solver_args=self.solver_args)
425
+ kwargs = {}
426
+ if self.solver_tol is not None:
427
+ kwargs["solver_tol"] = self.solver_tol
428
+ if self.max_iter is not None:
429
+ kwargs["max_iter"] = self.max_iter
430
+ if self.solver_args is not None:
431
+ kwargs["solver_args"] = self.solver_args
432
+ return QPAXPTRSolver(**kwargs)
342
433
 
343
434
 
344
435
  def __getattr__(name: str):
@@ -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:
@@ -233,24 +241,75 @@ class MoreauPTRSolver(PTRSolver):
233
241
  through a full SCvx solve.
234
242
 
235
243
  Args:
236
- solver_args: Keyword arguments forwarded to :class:`moreau.Settings`.
237
- Useful keys include ``max_iter`` (default 200), ``verbose``
238
- (default False), ``device`` (``'auto'``, ``'cpu'``, or
239
- ``'cuda'``), and a nested ``ipm_settings`` dict for IPM tolerances
240
- (e.g. ``{"tol_gap_abs": 1e-8, "tol_feas": 1e-8}``).
244
+ max_iter: Maximum number of IPM iterations forwarded to
245
+ :class:`moreau.Settings`. Defaults to ``200``.
246
+ verbose: Whether Moreau prints per-iteration diagnostics.
247
+ Forwarded to :class:`moreau.Settings`. Defaults to ``False``.
248
+ device: Compute device for Moreau's JAX kernels. One of
249
+ ``"auto"``, ``"cpu"``, or ``"cuda"``. Forwarded to
250
+ :class:`moreau.Settings`. Defaults to ``"auto"``.
251
+ tol_gap_abs: Absolute duality-gap tolerance forwarded to
252
+ :class:`moreau.IPMSettings`. ``None`` uses Moreau's default.
253
+ tol_feas: Primal/dual feasibility tolerance forwarded to
254
+ :class:`moreau.IPMSettings`. ``None`` uses Moreau's default.
255
+ solver_args: Additional keyword arguments forwarded verbatim to
256
+ :class:`moreau.Settings`. Use for settings not covered by the
257
+ named params above. ``solver_args["ipm_settings"]`` may be a
258
+ dict or a :class:`moreau.IPMSettings` object; if it is a dict,
259
+ ``tol_gap_abs`` / ``tol_feas`` are merged into it. Raises
260
+ ``ValueError`` at construction time if any top-level key or
261
+ IPM tolerance overlaps with a named param.
241
262
 
242
263
  Attributes:
243
264
  layout: :class:`_ConicLayout` describing flat decision-vector slot
244
265
  ranges. Populated by :meth:`create_variables`.
245
266
  """
246
267
 
247
- def __init__(self, solver_args: Optional[Dict] = None):
268
+ def __init__(
269
+ self,
270
+ *,
271
+ max_iter: int = 200,
272
+ verbose: bool = False,
273
+ device: str = "auto",
274
+ tol_gap_abs: Optional[float] = None,
275
+ tol_feas: Optional[float] = None,
276
+ solver_args: Optional[Dict] = None,
277
+ ):
248
278
  if not _MOREAU_AVAILABLE:
249
279
  raise ImportError(
250
280
  "MoreauPTRSolver requires the `moreau` package. "
251
281
  "Install it with: pip install openscvx[moreau]"
252
282
  )
253
- self.solver_args = dict(solver_args) if solver_args else {}
283
+
284
+ _named = {"max_iter": max_iter, "verbose": verbose, "device": device}
285
+ _extra = dict(solver_args) if solver_args else {}
286
+ _overlap = _named.keys() & _extra.keys()
287
+ if _overlap:
288
+ raise ValueError(
289
+ f"Moreau settings {sorted(_overlap)} appear as both named arguments "
290
+ "and inside solver_args; use one or the other."
291
+ )
292
+ merged = {**_named, **_extra}
293
+
294
+ _ipm = {
295
+ k: v for k, v in [("tol_gap_abs", tol_gap_abs), ("tol_feas", tol_feas)] if v is not None
296
+ }
297
+ if _ipm:
298
+ existing_ipm = merged.get("ipm_settings", {})
299
+ if not isinstance(existing_ipm, dict):
300
+ raise ValueError(
301
+ "Cannot combine tol_gap_abs / tol_feas named arguments with an "
302
+ "ipm_settings object in solver_args; use one form or the other."
303
+ )
304
+ ipm_overlap = _ipm.keys() & existing_ipm.keys()
305
+ if ipm_overlap:
306
+ raise ValueError(
307
+ f"Moreau IPM settings {sorted(ipm_overlap)} appear as both named "
308
+ "arguments and inside solver_args['ipm_settings']; use one or the other."
309
+ )
310
+ merged["ipm_settings"] = {**_ipm, **existing_ipm}
311
+
312
+ self.solver_args = merged
254
313
 
255
314
  self.layout: Optional[_ConicLayout] = None
256
315
  self._S_x: Optional[np.ndarray] = None
@@ -274,6 +333,11 @@ class MoreauPTRSolver(PTRSolver):
274
333
  self._coo_cols: Optional[np.ndarray] = None
275
334
  self._n_con: int = 0
276
335
 
336
+ # Populated by lower_convex_constraints. Auto-augmented impulsive
337
+ # zero-pin constraints land here; any genuine user .convex() trips
338
+ # the default refusal.
339
+ self._impulsive_pins: List[Tuple[List[int], slice]] = []
340
+
277
341
  # Per-iteration data, set by update_* methods.
278
342
  self._dyn: dict = {}
279
343
  self._cons: dict = {}
@@ -313,23 +377,12 @@ class MoreauPTRSolver(PTRSolver):
313
377
  jax_constraints: Lowered JAX constraints (nodal structure only).
314
378
  dynamics_sparsity: Ignored.
315
379
  constraint_sparsity: Ignored.
316
-
317
- Raises:
318
- NotImplementedError: If impulsive controls are present.
319
380
  """
320
381
  del dynamics_sparsity, constraint_sparsity
321
382
 
322
383
  n_x = len(x_unified.max)
323
384
  n_u = len(u_unified.max)
324
385
 
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
386
  S_x, c_x = self._scaling(x_unified)
334
387
  S_u, c_u = self._scaling(u_unified)
335
388
 
@@ -345,6 +398,28 @@ class MoreauPTRSolver(PTRSolver):
345
398
  self.layout = _ConicLayout(N=N, n_x=n_x, n_u=n_u, n_nodal=len(jax_constraints.nodal))
346
399
  self._jax_constraints = jax_constraints
347
400
 
401
+ def lower_convex_constraints(
402
+ self,
403
+ constraints: "ConstraintSet",
404
+ parameters: Optional[Dict] = None,
405
+ ) -> Tuple[List, Dict]:
406
+ """Absorb auto-generated impulsive zero-pin constraints; refuse the rest.
407
+
408
+ :func:`openscvx.symbolic.lower._augment_impulsive_constraints` injects
409
+ ``Control == 0`` equalities at non-impulse nodes for every impulsive
410
+ control. Those constraints live in ``constraints.nodal_convex`` even
411
+ though no user ``.convex()`` was written; we recognize their fixed
412
+ shape and stash a pin list for the structural pass / assembler to
413
+ emit as plain zero-cone rows. Anything that doesn't match the
414
+ auto-augmentation shape (e.g. a genuine user ``.convex()`` SOC) falls
415
+ through to the default refusal in :class:`ConvexSolver`.
416
+ """
417
+ pins = self._extract_impulsive_pins(constraints)
418
+ if pins is None:
419
+ return super().lower_convex_constraints(constraints, parameters)
420
+ self._impulsive_pins = pins
421
+ return [], {}
422
+
348
423
  def initialize(self, lowered: "LoweredProblem", settings: "Config") -> None:
349
424
  """Build the static conic structure and construct ``moreau.jax.Solver``.
350
425
 
@@ -359,7 +434,7 @@ class MoreauPTRSolver(PTRSolver):
359
434
 
360
435
  Raises:
361
436
  RuntimeError: If :meth:`create_variables` was not called first.
362
- NotImplementedError: If cross-node or impulsive controls are present.
437
+ NotImplementedError: If cross-node constraints are present.
363
438
  """
364
439
  if self.layout is None:
365
440
  raise RuntimeError(
@@ -373,11 +448,6 @@ class MoreauPTRSolver(PTRSolver):
373
448
  f"({len(lowered.jax_constraints.cross_node)} defined). "
374
449
  "Use CVXPyPTRSolver."
375
450
  )
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
451
 
382
452
  self._settings = settings
383
453
  # Reset warm-start on new initialization (problem structure may change).
@@ -456,15 +526,42 @@ class MoreauPTRSolver(PTRSolver):
456
526
  D_d: Optional[np.ndarray] = None,
457
527
  E_d: Optional[np.ndarray] = None,
458
528
  ) -> None:
459
- # Impulsive rejected at initialize(); D_d / E_d / x_prop_plus dropped.
460
- del x_prop_plus, D_d, E_d
529
+ A_eff = np.asarray(A_d, dtype=float)
530
+ B_eff = np.asarray(B_d, dtype=float)
531
+ C_eff = np.asarray(C_d, dtype=float)
532
+
533
+ # Absorb the impulsive state Jacobian into the continuous step
534
+ # matrices so the assembly row keeps a single A_d·dx + B_d·du term,
535
+ # matching the recipe in CVXPyPTRSolver.update_dynamics_linearization
536
+ # at openscvx/solvers/cvxpy_ptr_solver.py:636-654.
537
+ if D_d is not None:
538
+ D_arr = np.asarray(D_d, dtype=float)
539
+ if D_arr.ndim == 3 and D_arr.shape[0] == A_eff.shape[0] + 1:
540
+ D_steps = D_arr[1:]
541
+ elif D_arr.ndim == 3 and D_arr.shape[0] == A_eff.shape[0]:
542
+ D_steps = D_arr
543
+ else:
544
+ raise ValueError(
545
+ "Unexpected D_d shape for dynamics update: "
546
+ f"{D_arr.shape}, expected "
547
+ f"{(A_eff.shape[0] + 1, A_eff.shape[1], A_eff.shape[2])} "
548
+ f"or {(A_eff.shape[0], A_eff.shape[1], A_eff.shape[2])}."
549
+ )
550
+ A_eff = np.einsum("kij,kjl->kil", D_steps, A_eff)
551
+ B_eff = np.einsum("kij,kjl->kil", D_steps, B_eff)
552
+ C_eff = np.einsum("kij,kjl->kil", D_steps, C_eff)
553
+
461
554
  self._dyn = {
462
555
  "x_bar": np.asarray(x_bar, dtype=float),
463
556
  "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),
557
+ "A_d": A_eff,
558
+ "B_d": B_eff,
559
+ "C_d": C_eff,
467
560
  "x_prop": np.asarray(x_prop, dtype=float),
561
+ "x_prop_plus": (
562
+ np.asarray(x_prop_plus, dtype=float) if x_prop_plus is not None else None
563
+ ),
564
+ "E_d": np.asarray(E_d, dtype=float) if E_d is not None else None,
468
565
  }
469
566
 
470
567
  def update_constraint_linearizations(
@@ -591,15 +688,32 @@ class MoreauPTRSolver(PTRSolver):
591
688
  add(row, L.nu_vb_idx(c_idx, node))
592
689
  row += 1
593
690
 
594
- # Fix boundary conditions (initial / terminal)
691
+ # Fix boundary conditions (initial / terminal). Under impulsive
692
+ # control the initial Fix row couples the post-impulse state at
693
+ # node 0 to du[0, slice_imp]; declare those columns structurally
694
+ # nonzero here so the static CSR pattern accommodates the
695
+ # numerical values emitted in _assemble_conic.
696
+ slice_imp = settings.sim.u.slice_impulsive
697
+ has_impulsive = slice_imp.stop > slice_imp.start
595
698
  for i in range(settings.sim.true_state_slice.start, settings.sim.true_state_slice.stop):
596
699
  if settings.sim.x.initial_type[i] == "Fix":
597
700
  add(row, L.x_idx(0, i))
701
+ if has_impulsive:
702
+ for j in range(slice_imp.start, slice_imp.stop):
703
+ add(row, L.du_idx(0, j))
598
704
  row += 1
599
705
  if settings.sim.x.final_type[i] == "Fix":
600
706
  add(row, L.x_idx(N - 1, i))
601
707
  row += 1
602
708
 
709
+ # Impulsive zero-pin equalities: u[node, j] = const, one row per
710
+ # (node, j) absorbed by lower_convex_constraints.
711
+ for nodes, ctrl_slice in self._impulsive_pins:
712
+ for node in nodes:
713
+ for j in range(ctrl_slice.start, ctrl_slice.stop):
714
+ add(row, L.u_idx(node, j))
715
+ row += 1
716
+
603
717
  # Uniform time grid: u[k,j] − u[k-1,j] = 0
604
718
  if settings.sim._uniform_time_grid:
605
719
  td = settings.sim.time_dilation_slice
@@ -725,8 +839,11 @@ class MoreauPTRSolver(PTRSolver):
725
839
  inv_S_x = self._inv_S_x_diag
726
840
  inv_S_u = self._inv_S_u_diag
727
841
  S_x = self._S_x_diag
842
+ S_u = self._S_u_diag
728
843
  c_x = self._c_x
729
844
  c_u = self._c_u
845
+ slice_imp = settings.sim.u.slice_impulsive
846
+ has_impulsive = slice_imp.stop > slice_imp.start
730
847
 
731
848
  lam_prox = self._pen["lam_prox"] # (N, n_x + n_u)
732
849
  lam_cost = self._pen["lam_cost"] # scalar or (n_x,)
@@ -739,6 +856,8 @@ class MoreauPTRSolver(PTRSolver):
739
856
  B_d = self._dyn["B_d"] # (N-1, n_x, n_u)
740
857
  C_d = self._dyn["C_d"] # (N-1, n_x, n_u)
741
858
  x_prop = self._dyn["x_prop"] # (N-1, n_x)
859
+ E_d_arr = self._dyn["E_d"] # (N, n_x, n_u) or None
860
+ x_prop_plus_arr = self._dyn["x_prop_plus"] # (N, n_x) or None
742
861
 
743
862
  lam_cost_arr = np.broadcast_to(lam_cost, (settings.sim.n_states,))
744
863
 
@@ -801,15 +920,23 @@ class MoreauPTRSolver(PTRSolver):
801
920
  for j in range(n_u):
802
921
  emit([1.0, -1.0], inv_S_u[j] * (u_bar[k, j] - c_u[j]))
803
922
 
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)
923
+ # Dynamics (continuous FOH, with optional impulsive coupling at node k):
924
+ # x[k] − A_blk·dx[k-1] − B_blk·du[k-1] − C_blk·du[k]
925
+ # E_blk·du[k][slice_imp]nu[k-1]
926
+ # = inv_S_x·(x_prop_plus[k] − c_x) if has_impulsive
927
+ # = inv_S_x·(x_prop[k-1] − c_x) otherwise
928
+ # Mirrors CVXPyPTRSolver.constraints at cvxpy_ptr_solver.py:506-530.
807
929
  for k in range(1, N):
808
930
  kp = k - 1
809
931
  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)
932
+ B_blk = (inv_S_x[:, None] * B_d[kp]) * S_u[None, :]
933
+ C_blk = (inv_S_x[:, None] * C_d[kp]) * S_u[None, :]
934
+ if has_impulsive:
935
+ E_blk = (inv_S_x[:, None] * E_d_arr[k]) * S_u[None, :]
936
+ rhs_k = inv_S_x * (x_prop_plus_arr[k] - c_x)
937
+ else:
938
+ E_blk = None
939
+ rhs_k = inv_S_x * (x_prop[kp] - c_x)
813
940
  for i in range(n_x):
814
941
  # Coefficients in the same col order as _structural_pass added them,
815
942
  # then sorted by scipy within the row — values line up correctly.
@@ -818,7 +945,10 @@ class MoreauPTRSolver(PTRSolver):
818
945
  coeffs.append(-A_blk[i, j]) # dx[kp, j]
819
946
  for j in range(n_u):
820
947
  coeffs.append(-B_blk[i, j]) # du[kp, j]
821
- coeffs.append(-C_blk[i, j]) # du[k, j]
948
+ c_kj = -C_blk[i, j]
949
+ if has_impulsive and slice_imp.start <= j < slice_imp.stop:
950
+ c_kj -= E_blk[i, j]
951
+ coeffs.append(c_kj) # du[k, j]
822
952
  coeffs.append(-1.0) # nu[kp, i]
823
953
  emit(coeffs, rhs_k[i])
824
954
 
@@ -839,15 +969,25 @@ class MoreauPTRSolver(PTRSolver):
839
969
  coeffs.append(-1.0) # nu_vb[c, node]
840
970
  emit(coeffs, -g[node])
841
971
 
842
- # Fix boundary conditions
972
+ # Fix boundary conditions. Under impulsive control the initial Fix
973
+ # row couples x[0, i] to du[0, slice_imp] via the linearized impulse
974
+ # Jacobian (CVXPy reference: cvxpy_ptr_solver.py:484-495). Emit
975
+ # coefficients in ascending column-index order so they match the
976
+ # CSR sort applied to the structural pass.
843
977
  for i in range(settings.sim.true_state_slice.start, settings.sim.true_state_slice.stop):
844
978
  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])
979
+ if has_impulsive:
980
+ coeffs = [S_x[i]]
981
+ for j in range(slice_imp.start, slice_imp.stop):
982
+ coeffs.append(-E_d_arr[0, i, j] * S_u[j])
983
+ emit(coeffs, x_prop_plus_arr[0, i] - c_x[i])
984
+ else:
985
+ if self._x_init is None:
986
+ raise RuntimeError(
987
+ f"Fix initial condition on state {i} requires x_init; "
988
+ "call update_boundary_conditions() before solve()."
989
+ )
990
+ emit([S_x[i]], self._x_init[i] - c_x[i])
851
991
  if settings.sim.x.final_type[i] == "Fix":
852
992
  if self._x_term is None:
853
993
  raise RuntimeError(
@@ -856,6 +996,13 @@ class MoreauPTRSolver(PTRSolver):
856
996
  )
857
997
  emit([S_x[i]], self._x_term[i] - c_x[i])
858
998
 
999
+ # Impulsive zero-pin equalities: u[node, j] = −inv_S_u[j]·c_u[j].
1000
+ # Mirrors CVXPy's lowering of ``u_nonscaled[node][slice_imp] == 0``.
1001
+ for nodes, ctrl_slice in self._impulsive_pins:
1002
+ for node in nodes:
1003
+ for j in range(ctrl_slice.start, ctrl_slice.stop):
1004
+ emit([1.0], -inv_S_u[j] * c_u[j])
1005
+
859
1006
  # Uniform time grid: u[k,j] − u[k-1,j] = 0
860
1007
  if settings.sim._uniform_time_grid:
861
1008
  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