openscvx 0.5.3.dev38__tar.gz → 0.5.3.dev39__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 (456) hide show
  1. {openscvx-0.5.3.dev38/openscvx.egg-info → openscvx-0.5.3.dev39}/PKG-INFO +1 -1
  2. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/__init__.py +3 -0
  3. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/_version.py +3 -3
  4. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/problem.py +106 -1
  5. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/expr.py +19 -0
  6. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lower.py +42 -0
  7. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/__init__.py +49 -0
  8. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/_lowerer.py +263 -0
  9. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/_registry.py +82 -0
  10. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/arithmetic.py +58 -0
  11. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/array.py +62 -0
  12. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/constraint.py +73 -0
  13. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/control.py +14 -0
  14. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/expr.py +27 -0
  15. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/formulation.py +465 -0
  16. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/lie.py +97 -0
  17. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/linalg.py +55 -0
  18. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/logic.py +78 -0
  19. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/math.py +182 -0
  20. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/spatial.py +41 -0
  21. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/state.py +31 -0
  22. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/stl.py +120 -0
  23. openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/vmap.py +37 -0
  24. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39/openscvx.egg-info}/PKG-INFO +1 -1
  25. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/SOURCES.txt +19 -0
  26. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/scm_file_list.json +19 -0
  27. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/scm_version.json +2 -2
  28. openscvx-0.5.3.dev39/tests/symbolic/test_latex_formulation.py +314 -0
  29. openscvx-0.5.3.dev39/tests/symbolic/test_lower_latex.py +786 -0
  30. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/assets/logo.svg +0 -0
  31. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/release-drafter.yml +0 -0
  32. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/_docs.yml +0 -0
  33. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/_extra.yml +0 -0
  34. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/branch-name.yml +0 -0
  35. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/docs.yml +0 -0
  36. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/lint.yml +0 -0
  37. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/nightly.yml +0 -0
  38. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/release-drafter.yml +0 -0
  39. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/release.yml +0 -0
  40. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-cvxpygen.yml +0 -0
  41. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-examples.yml +0 -0
  42. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-lie.yml +0 -0
  43. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-mjx.yml +0 -0
  44. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-qpax.yml +0 -0
  45. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests.yml +0 -0
  46. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.gitignore +0 -0
  47. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.gitmodules +0 -0
  48. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/CONTRIBUTING.md +0 -0
  49. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/LICENSE +0 -0
  50. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/README.md +0 -0
  51. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/constraint_reformulation.md +0 -0
  52. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/control_parameterization.md +0 -0
  53. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/discretization.md +0 -0
  54. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/ocp.md +0 -0
  55. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/scvx.md +0 -0
  56. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/time_dilation.md +0 -0
  57. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/batching_jit_grad.md +0 -0
  58. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/custom_algorithms_autotuners.md +0 -0
  59. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/lowering_architecture.md +0 -0
  60. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  61. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/00_introduction.md +0 -0
  62. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  63. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  64. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  65. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  66. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/05_visualization.md +0 -0
  67. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/06_logic.md +0 -0
  68. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/07_lie.md +0 -0
  69. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/08_mpcc.md +0 -0
  70. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
  71. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/favicon.png +0 -0
  72. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ct-scvx_dark.png +0 -0
  73. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ct-scvx_light.png +0 -0
  74. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ctcs_dark.png +0 -0
  75. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ctcs_light.png +0 -0
  76. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/problem_class_dark.png +0 -0
  77. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/problem_class_light.png +0 -0
  78. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/logo.svg +0 -0
  79. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/openscvx_logo_square.png +0 -0
  80. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/viser-client/index.html +0 -0
  81. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/viser-recordings/drone_racing.viser +0 -0
  82. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
  83. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/citation.md +0 -0
  84. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/examples.md +0 -0
  85. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/index.md +0 -0
  86. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/javascripts/mathjax.js +0 -0
  87. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/versions.json +0 -0
  88. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/_viser_embed_export.py +0 -0
  89. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/brachistochrone.py +0 -0
  90. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/brachistochrone_batched.py +0 -0
  91. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/flappy_bird.py +0 -0
  92. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/hypersensitive.py +0 -0
  93. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/impulsive.py +0 -0
  94. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/stl_integer_variable.py +0 -0
  95. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/stl_or.py +0 -0
  96. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/aircraft/dynamic_soaring.py +0 -0
  97. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/aircraft/supersonic_time_to_climb.py +0 -0
  98. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/7_dof_arm.py +0 -0
  99. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/_camera.py +0 -0
  100. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/_render.py +0 -0
  101. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/_sensor_view.py +0 -0
  102. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/dr_vp_polytope.py +0 -0
  103. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/franka_fr3v2_pick_place.py +0 -0
  104. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/logo.py +0 -0
  105. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/obstacle_avoidance_vmap.py +0 -0
  106. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/3_dof_arm.py +0 -0
  107. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/7_dof_arm.py +0 -0
  108. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/7_dof_arm_collision.py +0 -0
  109. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/7_dof_arm_vp.py +0 -0
  110. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/franka_fr3v2_pick_place.py +0 -0
  111. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
  112. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/franka_fr3v2_viewplanning_nodal.py +0 -0
  113. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car.py +0 -0
  114. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_disjoint.py +0 -0
  115. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_obstacle_conditional.py +0 -0
  116. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_obstacle_stl.py +0 -0
  117. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_stl_or.py +0 -0
  118. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_waypoint_stl.py +0 -0
  119. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/double_integrator/_plotting.py +0 -0
  120. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/double_integrator/moving_safe_zones.py +0 -0
  121. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/2d_obstacle_avoidance_batched_ic.py +0 -0
  122. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/cinema_vp.py +0 -0
  123. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/cinema_vp_nodal.py +0 -0
  124. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_double_integrator.py +0 -0
  125. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_vp.py +0 -0
  126. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_vp_nodal.py +0 -0
  127. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_vp_polytope.py +0 -0
  128. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/drone_racing.py +0 -0
  129. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/drone_racing_batched_gates.py +0 -0
  130. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo.py +0 -0
  131. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/acl_logo.svg +0 -0
  132. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/openscvx_logo_single.svg +0 -0
  133. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/quadrotor_mesh.py +0 -0
  134. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/svg_path_utils.py +0 -0
  135. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/multi_agent_circle_swap.py +0 -0
  136. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance.py +0 -0
  137. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  138. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  139. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance_vmap_2d.py +0 -0
  140. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/openscvx_logo.py +0 -0
  141. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax.py +0 -0
  142. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax_pick_place.py +0 -0
  143. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax_viewplanning.py +0 -0
  144. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax_waypoint.py +0 -0
  145. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/cartpole_mjx.py +0 -0
  146. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/double_cartpole_mjx.py +0 -0
  147. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/skydio_x2_mjx.py +0 -0
  148. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
  149. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/triple_cartpole_game.py +0 -0
  150. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/triple_cartpole_mjx.py +0 -0
  151. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/double_integrator_discrete.py +0 -0
  152. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/double_integrator_drone_racing.py +0 -0
  153. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/dubins_car_circle_analytical.py +0 -0
  154. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/dubins_car_circle_discrete.py +0 -0
  155. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
  156. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/plotting.py +0 -0
  157. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/plotting_viser.py +0 -0
  158. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/3DoF_pdg_realtime.py +0 -0
  159. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/6DoF_pdg_realtime.py +0 -0
  160. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
  161. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
  162. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
  163. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
  164. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
  165. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
  166. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/cinema_vp_realtime.py +0 -0
  167. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/drone_racing_realtime.py +0 -0
  168. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/dubins_car_realtime.py +0 -0
  169. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  170. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/3DoF_pdg.py +0 -0
  171. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/6DoF_pdg.py +0 -0
  172. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/6DoF_pdg_batched_ic.py +0 -0
  173. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/ascent_launch_vehicle.py +0 -0
  174. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/halo_orbit.py +0 -0
  175. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/hohmann_transfer.py +0 -0
  176. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/let_transfer.py +0 -0
  177. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/proxops_cw.py +0 -0
  178. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/relative_loitering.py +0 -0
  179. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/ctlos_cine.gif +0 -0
  180. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/ctlos_dr.gif +0 -0
  181. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/dtlos_cine.gif +0 -0
  182. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/dtlos_dr.gif +0 -0
  183. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/openscvx_logo.svg +0 -0
  184. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/openscvx_logo_square.png +0 -0
  185. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/oscvx_structure_full_dark.svg +0 -0
  186. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/video_preview.png +0 -0
  187. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/__init__.py +0 -0
  188. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/custom.css +0 -0
  189. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
  190. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/home-hero.css +0 -0
  191. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/home-viser.css +0 -0
  192. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/home.html +0 -0
  193. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/main.html +0 -0
  194. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-diagram.html +0 -0
  195. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-dropin-banner.html +0 -0
  196. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-hero.html +0 -0
  197. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-pipeline.html +0 -0
  198. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-viser-strip.html +0 -0
  199. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/mkdocs.yml +0 -0
  200. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/__main__.py +0 -0
  201. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/__init__.py +0 -0
  202. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/__init__.py +0 -0
  203. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/acceptance_ratio.py +0 -0
  204. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
  205. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
  206. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/base.py +0 -0
  207. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
  208. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
  209. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/base.py +0 -0
  210. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/history.py +0 -0
  211. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/hyperparams.py +0 -0
  212. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/loop.py +0 -0
  213. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/optimization_results.py +0 -0
  214. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/penalty.py +0 -0
  215. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/scvx/__init__.py +0 -0
  216. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/scvx/iteration.py +0 -0
  217. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
  218. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/state.py +0 -0
  219. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/weights.py +0 -0
  220. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/config.py +0 -0
  221. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/__init__.py +0 -0
  222. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/base.py +0 -0
  223. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/discretize_linearize.py +0 -0
  224. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/linearize_discretize.py +0 -0
  225. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
  226. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/sparse_utils/__init__.py +0 -0
  227. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
  228. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
  229. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/__init__.py +0 -0
  230. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/byof.py +0 -0
  231. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/lowering.py +0 -0
  232. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/validation.py +0 -0
  233. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/init/__init__.py +0 -0
  234. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/init/interpolation.py +0 -0
  235. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/init/inverse_kinematics.py +0 -0
  236. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/__init__.py +0 -0
  237. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/_utils.py +0 -0
  238. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/base.py +0 -0
  239. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/frax.py +0 -0
  240. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/menagerie.py +0 -0
  241. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/mjx.py +0 -0
  242. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrators/__init__.py +0 -0
  243. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrators/diffrax.py +0 -0
  244. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrators/runge_kutta.py +0 -0
  245. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/loader.py +0 -0
  246. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/__init__.py +0 -0
  247. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/cvxpy_constraints.py +0 -0
  248. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/cvxpy_variables.py +0 -0
  249. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/dynamics.py +0 -0
  250. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/jax_constraints.py +0 -0
  251. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/parameters.py +0 -0
  252. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/problem.py +0 -0
  253. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/unified.py +0 -0
  254. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/__init__.py +0 -0
  255. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/plotting.py +0 -0
  256. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/publication.py +0 -0
  257. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/scp_iteration.py +0 -0
  258. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/__init__.py +0 -0
  259. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/animated.py +0 -0
  260. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/coordinates.py +0 -0
  261. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/orbits.py +0 -0
  262. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/plotly_integration.py +0 -0
  263. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/primitives.py +0 -0
  264. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/scp.py +0 -0
  265. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/server.py +0 -0
  266. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/propagation/__init__.py +0 -0
  267. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/propagation/post_processing.py +0 -0
  268. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/propagation/propagation.py +0 -0
  269. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/__init__.py +0 -0
  270. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/base.py +0 -0
  271. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/cones.py +0 -0
  272. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
  273. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/moreau_ptr_solver.py +0 -0
  274. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/ptr_solver.py +0 -0
  275. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/qpax_ptr_solver.py +0 -0
  276. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/__init__.py +0 -0
  277. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/affine.py +0 -0
  278. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/augmentation.py +0 -0
  279. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/builder.py +0 -0
  280. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/canonicalize.py +0 -0
  281. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/constraint_set.py +0 -0
  282. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/__init__.py +0 -0
  283. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/arithmetic.py +0 -0
  284. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/array.py +0 -0
  285. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/constraint.py +0 -0
  286. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/control.py +0 -0
  287. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  288. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  289. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/se3.py +0 -0
  290. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/so3.py +0 -0
  291. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/linalg.py +0 -0
  292. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/logic.py +0 -0
  293. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/math.py +0 -0
  294. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/parameter.py +0 -0
  295. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/spatial.py +0 -0
  296. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/state.py +0 -0
  297. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/stl.py +0 -0
  298. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/stljax.py +0 -0
  299. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/time.py +0 -0
  300. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/variable.py +0 -0
  301. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/vmap.py +0 -0
  302. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/hashing.py +0 -0
  303. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/__init__.py +0 -0
  304. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  305. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  306. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  307. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  308. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  309. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  310. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  311. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  312. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  313. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  314. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  315. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  316. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  317. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  318. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  319. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  320. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  321. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
  322. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  323. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  324. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  325. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  326. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
  327. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  328. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  329. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  330. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  331. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
  332. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  333. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/__init__.py +0 -0
  334. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/_registry.py +0 -0
  335. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/array.py +0 -0
  336. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/constraint.py +0 -0
  337. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/lie.py +0 -0
  338. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/linalg.py +0 -0
  339. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/logic.py +0 -0
  340. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/math.py +0 -0
  341. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/parser.py +0 -0
  342. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/spatial.py +0 -0
  343. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/stl.py +0 -0
  344. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/stljax.py +0 -0
  345. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/tokenizer.py +0 -0
  346. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/preprocessing.py +0 -0
  347. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/problem.py +0 -0
  348. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/sparsity.py +0 -0
  349. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/unified.py +0 -0
  350. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/__init__.py +0 -0
  351. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/cache.py +0 -0
  352. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/caching.py +0 -0
  353. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/printing.py +0 -0
  354. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/profiling.py +0 -0
  355. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/utils.py +0 -0
  356. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/dependency_links.txt +0 -0
  357. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/entry_points.txt +0 -0
  358. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/requires.txt +0 -0
  359. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/top_level.txt +0 -0
  360. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/pyproject.toml +0 -0
  361. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/scripts/gen_example_pages.py +0 -0
  362. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/scripts/gen_ref_pages.py +0 -0
  363. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
  364. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/setup.cfg +0 -0
  365. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/__init__.py +0 -0
  366. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/__init__.py +0 -0
  367. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/_iteration_helpers.py +0 -0
  368. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/__init__.py +0 -0
  369. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/test_defaults_agreement.py +0 -0
  370. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/test_hyper_overrides.py +0 -0
  371. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
  372. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
  373. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_iteration_fn_jit.py +0 -0
  374. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_iteration_fn_parity.py +0 -0
  375. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_make_solve_loop.py +0 -0
  376. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/brachistochrone_analytical.py +0 -0
  377. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/conftest.py +0 -0
  378. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/__init__.py +0 -0
  379. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_brachistochrone.py +0 -0
  380. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_impulsive.py +0 -0
  381. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_multishot_propagation.py +0 -0
  382. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_batched_brachistochrone.py +0 -0
  383. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_batched_cvxpy_export_errors.py +0 -0
  384. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_batched_export_roundtrip.py +0 -0
  385. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_bare_brachistochrone.py +0 -0
  386. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_jit_brachistochrone.py +0 -0
  387. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_vmap_brachistochrone.py +0 -0
  388. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_vmap_converged_no_drift.py +0 -0
  389. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/fixtures/brachistochrone.json +0 -0
  390. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/fixtures/brachistochrone.yaml +0 -0
  391. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/hohmann_analytical.py +0 -0
  392. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/integrations/__init__.py +0 -0
  393. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/integrations/test_mjx.py +0 -0
  394. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/integrations/test_mjx_dynamics.py +0 -0
  395. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/__init__.py +0 -0
  396. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/_iteration_callback_helpers.py +0 -0
  397. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
  398. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
  399. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_moreau.py +0 -0
  400. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_qpax.py +0 -0
  401. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_vmap.py +0 -0
  402. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_moreau_ptr_solver.py +0 -0
  403. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_qpax_ptr_solver.py +0 -0
  404. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_subproblem_pytree.py +0 -0
  405. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/__init__.py +0 -0
  406. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/__init__.py +0 -0
  407. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_arithmetic.py +0 -0
  408. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_array.py +0 -0
  409. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_constraint.py +0 -0
  410. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_expr.py +0 -0
  411. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_lie.py +0 -0
  412. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_linalg.py +0 -0
  413. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_logic.py +0 -0
  414. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_math.py +0 -0
  415. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_node_reference.py +0 -0
  416. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_parameters.py +0 -0
  417. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_scaling.py +0 -0
  418. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_spatial.py +0 -0
  419. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_stl.py +0 -0
  420. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_variable.py +0 -0
  421. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_vmap.py +0 -0
  422. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/__init__.py +0 -0
  423. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_array.py +0 -0
  424. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_constraint.py +0 -0
  425. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_lie.py +0 -0
  426. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_linalg.py +0 -0
  427. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_load.py +0 -0
  428. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_logic.py +0 -0
  429. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_math.py +0 -0
  430. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_parser.py +0 -0
  431. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_spatial.py +0 -0
  432. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_stl.py +0 -0
  433. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_tokenizer.py +0 -0
  434. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_vmap.py +0 -0
  435. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_augmentation.py +0 -0
  436. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_gmsr.py +0 -0
  437. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_hashing.py +0 -0
  438. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_lower_cvxpy.py +0 -0
  439. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_lower_jax.py +0 -0
  440. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_preprocessing.py +0 -0
  441. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_sparsity.py +0 -0
  442. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_unified.py +0 -0
  443. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_autotuning.py +0 -0
  444. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_cvxpygen_optional.py +0 -0
  445. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_discretization.py +0 -0
  446. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_examples.py +0 -0
  447. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_expert.py +0 -0
  448. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_init.py +0 -0
  449. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_integrators.py +0 -0
  450. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_loader.py +0 -0
  451. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_optimization_results.py +0 -0
  452. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_plotting.py +0 -0
  453. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_post_process_batched_tau_clamp.py +0 -0
  454. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_propagation.py +0 -0
  455. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_results_parameters.py +0 -0
  456. {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_solve_batched_inference.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.5.3.dev38
3
+ Version: 0.5.3.dev39
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
@@ -90,6 +90,7 @@ from openscvx.symbolic.expr import (
90
90
  ctcs,
91
91
  )
92
92
  from openscvx.symbolic.expr.time import Time
93
+ from openscvx.symbolic.lower import to_latex
93
94
  from openscvx.utils.cache import clear_cache, get_cache_dir, get_cache_size
94
95
 
95
96
  load_results = OptimizationResults.load
@@ -186,6 +187,8 @@ __all__ = [
186
187
  "ctcs",
187
188
  # Data parallelism
188
189
  "Vmap",
190
+ # LaTeX rendering
191
+ "to_latex",
189
192
  # Submodules
190
193
  "init",
191
194
  "stl",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.5.3.dev38'
22
- __version_tuple__ = version_tuple = (0, 5, 3, 'dev38')
21
+ __version__ = version = '0.5.3.dev39'
22
+ __version_tuple__ = version_tuple = (0, 5, 3, 'dev39')
23
23
 
24
- __commit_id__ = commit_id = 'g235acbb25'
24
+ __commit_id__ = commit_id = 'g56a68269b'
@@ -26,7 +26,7 @@ import time
26
26
  import warnings
27
27
  from dataclasses import fields as dc_fields
28
28
  from dataclasses import replace as dc_replace
29
- from typing import Any, Dict, List, Optional, Tuple, Union
29
+ from typing import Any, Dict, List, Literal, Optional, Tuple, Union
30
30
 
31
31
  import jax
32
32
  import numpy as np
@@ -525,6 +525,10 @@ class Problem:
525
525
  dynamics, adapter_byof = dynamics.expand()
526
526
  byof = _merge_byof(byof, adapter_byof)
527
527
 
528
+ # Stash the user-authored dynamics (pre-augmentation) for `to_latex`.
529
+ # Shallow-copy so later user mutation of their dict doesn't alias.
530
+ self._dynamics_dict: dict = dict(dynamics)
531
+
528
532
  # Resolve byof: dict → ByofSpec (validates keys and nested specs)
529
533
  if byof is not None:
530
534
  byof = ByofSpec.model_validate(byof)
@@ -678,6 +682,107 @@ class Problem:
678
682
 
679
683
  # SCP algorithm (resolved from `algorithm` parameter above)
680
684
 
685
+ @property
686
+ def dynamics(self) -> dict:
687
+ """The user-authored dynamics dict, ``{state_name: expr}``.
688
+
689
+ This is the problem as written, *before* the solver augments it with a
690
+ time-dilation control and per-CTCS penalty states. Handy for rendering
691
+ or inspecting a single equation, e.g. ``ox.to_latex(problem.dynamics["v"])``.
692
+
693
+ Returns:
694
+ A dict mapping each user state name to its symbolic time derivative.
695
+ """
696
+ return self._dynamics_dict
697
+
698
+ def to_latex(
699
+ self,
700
+ *,
701
+ dynamics: Literal["inline", "separate"] = "separate",
702
+ constraints: Literal["inline", "symbolic", "separate"] = "inline",
703
+ weights: Literal["symbolic", "numeric"] = "symbolic",
704
+ ) -> str:
705
+ """Render the problem as a Mayer-form LaTeX formulation.
706
+
707
+ The rendered problem is the one you wrote — pre-augmentation, so no
708
+ time-dilation control or CTCS penalty states appear; CTCS constraints
709
+ show up as continuous-time path constraints. The result is a complete
710
+ display-math fragment — a ``subequations`` + ``align`` block (with
711
+ ``"separate"`` definitions as bare ``align`` blocks) that pastes into a
712
+ paper as-is, no ``$$`` wrapping.
713
+
714
+ Dynamics render at one of two detail levels:
715
+
716
+ - ``"separate"`` (default): the formulation carries ``\\dot{x} = f(x, u)``
717
+ and the per-state ``\\dot{x}_{i} = \\ldots`` definitions follow as
718
+ their own ``align`` block.
719
+ - ``"inline"``: one ``\\dot{x}_{i} = \\ldots`` row per equation inside
720
+ the formulation.
721
+
722
+ Constraints add a middle level:
723
+
724
+ - ``"inline"`` (default): full constraint bodies with annotations.
725
+ - ``"symbolic"``: numbered ``g_i(x, u) \\le 0`` / ``h_j(x, u) = 0``
726
+ references only (with their ``\\forall t`` / node annotations).
727
+ - ``"separate"``: symbolic references plus an appended definition block.
728
+
729
+ Args:
730
+ dynamics: Detail level for the dynamics section.
731
+ constraints: Detail level for the constraint section.
732
+ weights: How objective coefficients render. ``"symbolic"`` (the
733
+ default) shows each as a ``\\lambda`` subscripted by the state's
734
+ symbol and element (``\\lambda_{t}``, ``\\lambda_{\\mathrm{v},1}``);
735
+ ``"numeric"`` substitutes the tuned ``lam_cost`` values (``%g``,
736
+ omitted when equal to 1).
737
+
738
+ Returns:
739
+ The formulation as a single LaTeX string; ``"separate"`` modes append
740
+ the definition blocks after it.
741
+
742
+ Example:
743
+ Default (separate dynamics, inline constraints, symbolic weights)::
744
+
745
+ print(problem.to_latex())
746
+
747
+ Everything expanded in a single block::
748
+
749
+ print(problem.to_latex(dynamics="inline"))
750
+
751
+ Substitute the tuned objective weights::
752
+
753
+ print(problem.to_latex(weights="numeric"))
754
+ """
755
+ from openscvx.symbolic.lowerers.latex import problem_to_latex
756
+
757
+ return problem_to_latex(
758
+ self.symbolic,
759
+ self._dynamics_dict,
760
+ self.algorithm.lam_cost,
761
+ dynamics=dynamics,
762
+ constraints=constraints,
763
+ weights=weights,
764
+ )
765
+
766
+ def _repr_latex_(self) -> str:
767
+ """Render as display math in Jupyter (default detail levels, in ``$$``).
768
+
769
+ Uses the builder's ``aligned`` envelope rather than the ``subequations``
770
+ + ``align`` form ``to_latex`` returns: MathJax/KaTeX (the notebook and
771
+ docs renderers) do not implement ``subequations``, and ``align`` cannot
772
+ nest inside ``$$``, whereas ``aligned`` can.
773
+ """
774
+ from openscvx.symbolic.lowerers.latex import problem_to_latex
775
+
776
+ body = problem_to_latex(
777
+ self.symbolic,
778
+ self._dynamics_dict,
779
+ self.algorithm.lam_cost,
780
+ dynamics="separate",
781
+ constraints="inline",
782
+ env="aligned",
783
+ )
784
+ return f"$$\n{body}\n$$"
785
+
681
786
  @property
682
787
  def solver(self) -> ConvexSolver:
683
788
  """Access the convex subproblem solver instance.
@@ -420,6 +420,25 @@ class Expr:
420
420
  lines.append(child.pretty(indent + 1))
421
421
  return "\n".join(lines)
422
422
 
423
+ def _repr_latex_(self) -> Union[str, None]:
424
+ """Render this expression as inline LaTeX for Jupyter.
425
+
426
+ Jupyter calls this hook to display an object as math. The expression
427
+ is lowered to LaTeX and wrapped in ``$...$``. Nodes without a
428
+ registered LaTeX visitor return ``None`` so Jupyter falls back to the
429
+ plain ``__repr__``.
430
+
431
+ Returns:
432
+ The inline-math string ``"$...$"``, or ``None`` when the
433
+ expression contains a node the LaTeX backend does not support.
434
+ """
435
+ from openscvx.symbolic.lower import to_latex
436
+
437
+ try:
438
+ return f"${to_latex(self)}$"
439
+ except NotImplementedError:
440
+ return None
441
+
423
442
  def _hash_into(self, hasher: "hashlib._Hash") -> None:
424
443
  """Contribute this expression's structural identity to a hash.
425
444
 
@@ -84,6 +84,7 @@ if TYPE_CHECKING:
84
84
  __all__ = [
85
85
  "lower",
86
86
  "lower_to_jax",
87
+ "to_latex",
87
88
  "lower_cvxpy_constraints",
88
89
  "create_cvxpy_variables",
89
90
  "lower_symbolic_problem",
@@ -169,6 +170,47 @@ def lower_to_jax(exprs: Union[Expr, Sequence[Expr]]) -> Union[callable, list[cal
169
170
  return fns
170
171
 
171
172
 
173
+ def to_latex(exprs: Union[Expr, Sequence[Expr]]) -> Union[str, list[str]]:
174
+ """Lower symbolic expression(s) to LaTeX math string(s).
175
+
176
+ Convenience wrapper that creates a LatexLowerer and renders one or more
177
+ symbolic expressions as LaTeX. This is the escape hatch for rendering a
178
+ single expression (or a handful) — e.g. one entry of a problem's dynamics
179
+ dict — without going through ``Problem.to_latex``.
180
+
181
+ Args:
182
+ exprs: Single expression or sequence of expressions to lower
183
+
184
+ Returns:
185
+ - If exprs is a single Expr: Returns a single LaTeX string.
186
+ - If exprs is a sequence: Returns a list of LaTeX strings in order.
187
+
188
+ Example:
189
+ Single expression::
190
+
191
+ x = ox.State("x", shape=(3,))
192
+ to_latex(ox.Norm(x) - 5.0)
193
+ # '\\left\\| x \\right\\| - 5'
194
+
195
+ Multiple expressions::
196
+
197
+ exprs = [ox.Norm(x), ox.Sum(x)]
198
+ to_latex(exprs)
199
+ # ['\\left\\| x \\right\\|', '\\sum x']
200
+
201
+ Note:
202
+ The returned strings carry no ``$`` delimiters — callers add their own.
203
+ Nodes without a registered LaTeX visitor (STL, Lie, spatial, ...) raise
204
+ ``NotImplementedError``.
205
+ """
206
+ from openscvx.symbolic.lowerers.latex import LatexLowerer
207
+
208
+ ll = LatexLowerer()
209
+ if isinstance(exprs, Expr):
210
+ return lower(exprs, ll)
211
+ return [lower(e, ll) for e in exprs]
212
+
213
+
172
214
  def _tile_sparsity(pattern_2d: np.ndarray, K: int):
173
215
  """Convert a 2-D boolean pattern to CVXPY sparsity indices tiled over *K* slices.
174
216
 
@@ -0,0 +1,49 @@
1
+ """LaTeX backend for lowering symbolic expressions to math strings.
2
+
3
+ This package implements the LaTeX lowering backend that converts symbolic
4
+ expression AST nodes into LaTeX math strings. Like the JAX and CVXPy
5
+ backends, lowering uses a visitor pattern where each expression type has a
6
+ corresponding visitor function registered via ``@visitor``.
7
+
8
+ The visitor functions are split across submodules that mirror the
9
+ ``openscvx.symbolic.expr`` package structure. Importing this package
10
+ triggers registration of all visitors, which now cover every shipped node
11
+ type — arithmetic, linear algebra, elementwise math, arrays, constraints,
12
+ logic, spatial, Lie-group, STL, and ``Vmap``. A future node added without a
13
+ visitor raises ``NotImplementedError`` — the designed fallback until its
14
+ visitor lands. (The legacy ``stljax`` operators are intentionally left
15
+ unregistered; the GMSR ``ox.stl`` surface is the rendered one.)
16
+
17
+ Example::
18
+
19
+ from openscvx.symbolic.lowerers.latex import LatexLowerer
20
+
21
+ lowerer = LatexLowerer()
22
+ s = lowerer.lower(ox.Norm(x) - 5.0) # '\\left\\| x \\right\\| - 5'
23
+ """
24
+
25
+ # Import visitor modules to trigger @visitor registration.
26
+ # Each module populates _LATEX_VISITORS as a side effect of import.
27
+ from openscvx.symbolic.lowerers.latex import (
28
+ arithmetic, # noqa: F401
29
+ array, # noqa: F401
30
+ constraint, # noqa: F401
31
+ control, # noqa: F401
32
+ expr, # noqa: F401
33
+ lie, # noqa: F401
34
+ linalg, # noqa: F401
35
+ logic, # noqa: F401
36
+ math, # noqa: F401
37
+ spatial, # noqa: F401
38
+ state, # noqa: F401
39
+ stl, # noqa: F401
40
+ vmap, # noqa: F401
41
+ )
42
+ from openscvx.symbolic.lowerers.latex._lowerer import (
43
+ LatexLowerer,
44
+ format_constant,
45
+ latex_symbol,
46
+ )
47
+ from openscvx.symbolic.lowerers.latex.formulation import problem_to_latex
48
+
49
+ __all__ = ["LatexLowerer", "latex_symbol", "format_constant", "problem_to_latex"]
@@ -0,0 +1,263 @@
1
+ """LatexLowerer class definition and shared string helpers.
2
+
3
+ The visitor methods that populate the registry live in sibling modules
4
+ (``arithmetic``, ``math``, ``linalg``, etc.) and are registered via
5
+ ``@visitor`` at import time. This module also carries the pieces every
6
+ visitor leans on: precedence-aware parenthesization (:data:`_PRECEDENCE`,
7
+ :func:`wrap`), symbol rendering (:func:`latex_symbol`), and constant rendering
8
+ (:func:`format_constant`).
9
+ """
10
+
11
+ import numpy as np
12
+
13
+ from openscvx.symbolic.expr import Expr
14
+ from openscvx.symbolic.expr.arithmetic import Add, MatMul, Mul, Neg, Power, Sub
15
+ from openscvx.symbolic.lowerers.latex._registry import dispatch
16
+
17
+
18
+ class LatexLowerer:
19
+ """LaTeX backend for lowering symbolic expressions to math strings.
20
+
21
+ This class implements the visitor pattern for converting symbolic
22
+ expression AST nodes to LaTeX. Each expression type has a corresponding
23
+ visitor function decorated with ``@visitor`` that returns a ``str``.
24
+
25
+ The lowering is recursive: each visitor lowers its children first, then
26
+ composes them into a LaTeX fragment, calling :func:`wrap` on children that
27
+ may need parentheses for the surrounding precedence.
28
+
29
+ Unlike :class:`~openscvx.symbolic.lowerers.jax.JaxLowerer`, the LaTeX
30
+ lowerer is stateless — rendering is pure string assembly with no trace,
31
+ node cache, or memoization.
32
+
33
+ Example:
34
+ Lower an expression to a LaTeX string::
35
+
36
+ lowerer = LatexLowerer()
37
+ s = lowerer.lower(ox.Norm(x) - 5.0)
38
+ # '\\left\\| x \\right\\| - 5'
39
+ """
40
+
41
+ def lower(self, expr: Expr) -> str:
42
+ """Lower a symbolic expression to a LaTeX string.
43
+
44
+ Main entry point for lowering. Delegates to :func:`dispatch`, which
45
+ looks up the appropriate visitor based on the expression type.
46
+
47
+ Args:
48
+ expr: Symbolic expression to lower (any Expr subclass)
49
+
50
+ Returns:
51
+ LaTeX math string (no ``$`` delimiters — callers add their own)
52
+
53
+ Raises:
54
+ NotImplementedError: If no visitor exists for the expression type
55
+ """
56
+ return dispatch(self, expr)
57
+
58
+
59
+ # Precedence-aware parenthesization: visitors call wrap() on children, passing
60
+ # the precedence of the surrounding context. Leaves and function-style nodes
61
+ # (Norm, Sin, Div-as-\frac, ...) are self-delimiting and default to 10.
62
+ _PRECEDENCE = {Add: 1, Sub: 1, Mul: 2, MatMul: 2, Neg: 3, Power: 4}
63
+
64
+
65
+ def wrap(lowerer: LatexLowerer, child: Expr, parent_precedence: int) -> str:
66
+ """Lower ``child`` and parenthesize it if the parent binds tighter.
67
+
68
+ A child whose precedence is strictly lower than ``parent_precedence`` is
69
+ wrapped in ``\\left( ... \\right)``. Leaves and function-style nodes
70
+ default to precedence 10 and are never wrapped.
71
+
72
+ Args:
73
+ lowerer: The LatexLowerer performing the lowering.
74
+ child: The child expression to render.
75
+ parent_precedence: Precedence of the surrounding operator; pass a value
76
+ one higher than the operator's own precedence for the right operand
77
+ of a non-associative operator (e.g. ``Sub``) so equal-precedence
78
+ children are still parenthesized.
79
+
80
+ Returns:
81
+ The child's LaTeX, parenthesized when required.
82
+ """
83
+ s = lowerer.lower(child)
84
+ if _PRECEDENCE.get(type(child), 10) < parent_precedence:
85
+ return rf"\left( {s} \right)"
86
+ return s
87
+
88
+
89
+ # Greek-word names that map to a LaTeX command of the same name.
90
+ _GREEK = {
91
+ "alpha",
92
+ "beta",
93
+ "gamma",
94
+ "delta",
95
+ "epsilon",
96
+ "zeta",
97
+ "eta",
98
+ "theta",
99
+ "iota",
100
+ "kappa",
101
+ "lambda",
102
+ "mu",
103
+ "nu",
104
+ "xi",
105
+ "pi",
106
+ "rho",
107
+ "sigma",
108
+ "tau",
109
+ "upsilon",
110
+ "phi",
111
+ "chi",
112
+ "psi",
113
+ "omega",
114
+ "Gamma",
115
+ "Delta",
116
+ "Theta",
117
+ "Lambda",
118
+ "Xi",
119
+ "Pi",
120
+ "Sigma",
121
+ "Upsilon",
122
+ "Phi",
123
+ "Psi",
124
+ "Omega",
125
+ }
126
+
127
+
128
+ def latex_symbol(name: str) -> str:
129
+ """Render a variable name as a LaTeX symbol.
130
+
131
+ Rules, in order:
132
+
133
+ - A Greek-word name renders as its command: ``'alpha'`` -> ``'\\alpha'``.
134
+ - A single character renders bare: ``'x'`` -> ``'x'``.
135
+ - A ``base_sub`` name splits on the first underscore into a subscript:
136
+ ``'x_pos'`` -> ``'x_{\\mathrm{pos}}'`` (base and subscript are rendered
137
+ recursively).
138
+ - Any other multi-letter name renders upright: ``'position'`` ->
139
+ ``'\\mathrm{position}'``, with underscores escaped.
140
+
141
+ Args:
142
+ name: The variable name to render.
143
+
144
+ Returns:
145
+ The LaTeX fragment for the name.
146
+ """
147
+ if name in _GREEK:
148
+ return rf"\{name}"
149
+ if len(name) == 1:
150
+ return name
151
+ # base_sub -> subscript, when the split yields non-empty halves.
152
+ if "_" in name and not name.startswith("_") and not name.endswith("_"):
153
+ base, sub = name.split("_", 1)
154
+ return rf"{latex_symbol(base)}_{{{latex_symbol(sub)}}}"
155
+ escaped = name.replace("_", r"\_")
156
+ return rf"\mathrm{{{escaped}}}"
157
+
158
+
159
+ def merge_subscript(base: str, sub) -> str:
160
+ """Append a subscript to a symbol, merging into an existing subscript group.
161
+
162
+ If ``base`` already ends in a ``_{...}`` group, ``sub`` is spliced in before
163
+ the closing brace as ``,<sub>`` (``x_{\\mathrm{velocity}}`` + ``0`` ->
164
+ ``x_{\\mathrm{velocity},0}``); otherwise ``_{<sub>}`` is appended
165
+ (``\\theta`` + ``0`` -> ``\\theta_{0}``). This never emits a double
166
+ subscript like ``x_{a}_{b}``, which is invalid LaTeX.
167
+
168
+ The trailing group is matched by depth-scanning braces (not a naive
169
+ ``[^{}]`` regex), so nested groups like ``\\mathrm{velocity}`` inside the
170
+ subscript are handled, and a non-subscript trailing group such as the
171
+ ``\\dot{x}`` accent or ``\\mathrm{position}`` correctly appends instead of
172
+ splicing.
173
+
174
+ Args:
175
+ base: The symbol to subscript (already-rendered LaTeX).
176
+ sub: The subscript to add (stringified).
177
+
178
+ Returns:
179
+ ``base`` with ``sub`` merged into its subscript.
180
+ """
181
+ sub = str(sub)
182
+ if base.endswith("}"):
183
+ depth = 0
184
+ open_idx = None
185
+ for i in range(len(base) - 1, -1, -1):
186
+ if base[i] == "}":
187
+ depth += 1
188
+ elif base[i] == "{":
189
+ depth -= 1
190
+ if depth == 0:
191
+ open_idx = i
192
+ break
193
+ # A trailing group is a subscript group only when its `{` follows `_`.
194
+ if open_idx is not None and open_idx >= 1 and base[open_idx - 1] == "_":
195
+ return f"{base[:-1]},{sub}}}"
196
+ return rf"{base}_{{{sub}}}"
197
+
198
+
199
+ def state_symbol(name: str) -> str:
200
+ """Render a state's role-prefixed symbol: ``x_{<sym>}``.
201
+
202
+ States are grounded in the skeleton's ``f(x, u)`` by prefixing the role
203
+ letter ``x``; ``<sym>`` is the :func:`latex_symbol` rendering of the name
204
+ (``x_{\\mathrm{velocity}}``, ``x_{\\theta}``). A state literally named
205
+ ``x`` renders bare as ``x``.
206
+ """
207
+ if name == "x":
208
+ return "x"
209
+ return merge_subscript("x", latex_symbol(name))
210
+
211
+
212
+ def control_symbol(name: str) -> str:
213
+ """Render a control's role-prefixed symbol: ``u_{<sym>}``.
214
+
215
+ Mirrors :func:`state_symbol` with the role letter ``u``; a control literally
216
+ named ``u`` renders bare as ``u``.
217
+ """
218
+ if name == "u":
219
+ return "u"
220
+ return merge_subscript("u", latex_symbol(name))
221
+
222
+
223
+ def _format_scalar(value: float) -> str:
224
+ """Render a scalar with ``%g`` (compact, no trailing zeros)."""
225
+ return "%g" % value
226
+
227
+
228
+ def format_constant(value: np.ndarray) -> str:
229
+ """Render a numeric constant as LaTeX.
230
+
231
+ Scalars render via ``%g``. Vectors render as a column ``bmatrix`` and
232
+ matrices as a 2-D ``bmatrix``, up to 6 entries per axis. Larger arrays
233
+ render as a placeholder ``\\mathrm{const} \\in \\mathbb{R}^{...}`` rather
234
+ than an unreadable wall of numbers.
235
+
236
+ Args:
237
+ value: The constant value (scalar, vector, matrix, or higher-rank
238
+ array).
239
+
240
+ Returns:
241
+ The LaTeX fragment for the constant.
242
+ """
243
+ arr = np.asarray(value)
244
+
245
+ if arr.ndim == 0:
246
+ return _format_scalar(arr.item())
247
+
248
+ if arr.ndim == 1:
249
+ if arr.shape[0] > 6:
250
+ return rf"\mathrm{{const}} \in \mathbb{{R}}^{{{arr.shape[0]}}}"
251
+ body = r" \\ ".join(_format_scalar(v) for v in arr)
252
+ return rf"\begin{{bmatrix}} {body} \end{{bmatrix}}"
253
+
254
+ if arr.ndim == 2:
255
+ m, n = arr.shape
256
+ if m > 6 or n > 6:
257
+ return rf"\mathrm{{const}} \in \mathbb{{R}}^{{{m} \times {n}}}"
258
+ rows = [" & ".join(_format_scalar(v) for v in row) for row in arr]
259
+ body = r" \\ ".join(rows)
260
+ return rf"\begin{{bmatrix}} {body} \end{{bmatrix}}"
261
+
262
+ shape = r" \times ".join(str(d) for d in arr.shape)
263
+ return rf"\mathrm{{const}} \in \mathbb{{R}}^{{{shape}}}"
@@ -0,0 +1,82 @@
1
+ """Visitor registry for the LaTeX lowerer.
2
+
3
+ This module holds the shared visitor dictionary, the ``@visitor`` decorator,
4
+ and the ``dispatch`` function. It is deliberately kept free of heavyweight
5
+ imports (no expression subclasses beyond the base ``Expr``) so that the visitor
6
+ modules can import from it without circular-dependency issues.
7
+
8
+ The registry is independent of the JAX and CVXPy registries. It covers every
9
+ shipped node type; a future node added without a registered LaTeX visitor (or a
10
+ deliberately-unregistered legacy node like the ``stljax`` operators) raises
11
+ ``NotImplementedError`` on dispatch — the designed fallback.
12
+ """
13
+
14
+ from typing import Any, Callable, Dict, Type
15
+
16
+ from openscvx.symbolic.expr import Expr
17
+
18
+ _LATEX_VISITORS: Dict[Type[Expr], Callable] = {}
19
+ """Registry mapping expression types to their visitor functions."""
20
+
21
+
22
+ def visitor(expr_cls: Type[Expr]) -> Callable[[Callable], Callable]:
23
+ """Decorator to register a visitor function for an expression type.
24
+
25
+ This decorator registers a visitor method to handle a specific expression
26
+ type during LaTeX lowering. The decorated function is stored in
27
+ _LATEX_VISITORS and will be called by dispatch() when lowering that
28
+ expression type.
29
+
30
+ Args:
31
+ expr_cls: The Expr subclass this visitor handles (e.g., Add, Mul, Norm)
32
+
33
+ Returns:
34
+ Decorator function that registers the visitor and returns it unchanged
35
+
36
+ Example:
37
+ Register a visitor function for the Add expression::
38
+
39
+ @visitor(Add)
40
+ def _visit_add(lowerer, node: Add):
41
+ ...
42
+
43
+ Note:
44
+ Dispatch is exact-type, so shared behaviour must be registered per
45
+ concrete class. Multiple types can share a visitor by stacking
46
+ decorators::
47
+
48
+ @visitor(Equality)
49
+ @visitor(Inequality)
50
+ def _visit_constraint(lowerer, node: Constraint):
51
+ ...
52
+ """
53
+
54
+ def register(fn: Callable[[Any, Expr], str]):
55
+ _LATEX_VISITORS[expr_cls] = fn
56
+ return fn
57
+
58
+ return register
59
+
60
+
61
+ def dispatch(lowerer: Any, expr: Expr) -> str:
62
+ """Dispatch an expression to its registered visitor function.
63
+
64
+ Looks up the visitor function for the expression's type and calls it.
65
+ This is the core of the visitor pattern implementation.
66
+
67
+ Args:
68
+ lowerer: The LatexLowerer instance (provides context for visitor methods)
69
+ expr: The expression node to lower
70
+
71
+ Returns:
72
+ The result of calling the visitor function (a LaTeX string)
73
+
74
+ Raises:
75
+ NotImplementedError: If no visitor is registered for the expression type
76
+ """
77
+ fn = _LATEX_VISITORS.get(type(expr))
78
+ if fn is None:
79
+ raise NotImplementedError(
80
+ f"{lowerer.__class__.__name__!r} has no visitor for {type(expr).__name__}"
81
+ )
82
+ return fn(lowerer, expr)