openscvx 0.3.2.dev328__tar.gz → 0.3.2.dev339__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 (267) hide show
  1. {openscvx-0.3.2.dev328/openscvx.egg-info → openscvx-0.3.2.dev339}/PKG-INFO +1 -1
  2. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/_version.py +3 -3
  3. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/problem.py +1 -2
  4. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/builder.py +13 -7
  5. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/__init__.py +35 -0
  6. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +111 -0
  7. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/_registry.py +75 -0
  8. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +149 -0
  9. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/array.py +152 -0
  10. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/constraint.py +122 -0
  11. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/control.py +38 -0
  12. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/expr.py +135 -0
  13. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/linalg.py +91 -0
  14. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/logic.py +75 -0
  15. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/math.py +372 -0
  16. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/cvxpy/state.py +40 -0
  17. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/__init__.py +40 -0
  18. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/_lowerer.py +56 -0
  19. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/_registry.py +74 -0
  20. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/arithmetic.py +87 -0
  21. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/array.py +132 -0
  22. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/constraint.py +159 -0
  23. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/control.py +30 -0
  24. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/expr.py +108 -0
  25. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/lie.py +291 -0
  26. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/linalg.py +76 -0
  27. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/logic.py +161 -0
  28. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/math.py +268 -0
  29. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/spatial.py +120 -0
  30. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/state.py +32 -0
  31. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/stl.py +82 -0
  32. openscvx-0.3.2.dev339/openscvx/symbolic/lowerers/jax/vmap.py +152 -0
  33. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/preprocessing.py +136 -0
  34. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339/openscvx.egg-info}/PKG-INFO +1 -1
  35. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx.egg-info/SOURCES.txt +28 -2
  36. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_arithmetic.py +6 -6
  37. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_constraint.py +2 -2
  38. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_parameters.py +3 -3
  39. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_variable.py +4 -4
  40. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/test_lower_jax.py +6 -6
  41. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/test_preprocessing.py +175 -0
  42. openscvx-0.3.2.dev328/openscvx/symbolic/lowerers/cvxpy.py +0 -1431
  43. openscvx-0.3.2.dev328/openscvx/symbolic/lowerers/jax.py +0 -1789
  44. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/assets/logo.svg +0 -0
  45. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/release-drafter.yml +0 -0
  46. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/_docs.yml +0 -0
  47. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/branch-name.yml +0 -0
  48. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/docs.yml +0 -0
  49. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/lint.yml +0 -0
  50. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/nightly.yml +0 -0
  51. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/release-drafter.yml +0 -0
  52. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/release.yml +0 -0
  53. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/tests-integration.yml +0 -0
  54. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.github/workflows/tests-unit.yml +0 -0
  55. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/.gitignore +0 -0
  56. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/CONTRIBUTING.md +0 -0
  57. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/LICENSE +0 -0
  58. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/README.md +0 -0
  59. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/Foundations/constraint_reformulation.md +0 -0
  60. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/Foundations/control_parameterization.md +0 -0
  61. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/Foundations/discretization.md +0 -0
  62. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/Foundations/ocp.md +0 -0
  63. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/Foundations/scvx.md +0 -0
  64. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/Foundations/time_dilation.md +0 -0
  65. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UnderTheHood/lowering_architecture.md +0 -0
  66. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  67. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/00_introduction.md +0 -0
  68. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  69. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  70. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  71. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  72. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/05_visualization.md +0 -0
  73. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/06_logic.md +0 -0
  74. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/UsersGuide/07_lie.md +0 -0
  75. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/favicon.png +0 -0
  76. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/images/ct-scvx_dark.png +0 -0
  77. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/images/ct-scvx_light.png +0 -0
  78. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/images/ctcs_dark.png +0 -0
  79. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/images/ctcs_light.png +0 -0
  80. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/images/problem_class_dark.png +0 -0
  81. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/images/problem_class_light.png +0 -0
  82. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/assets/logo.svg +0 -0
  83. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/citation.md +0 -0
  84. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/examples.md +0 -0
  85. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/getting-started.md +0 -0
  86. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/index.md +0 -0
  87. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/docs/javascripts/mathjax.js +0 -0
  88. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/abstract/brachistochrone.py +0 -0
  89. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/arm/three_link_arm.py +0 -0
  90. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/car/dubins_car.py +0 -0
  91. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/car/dubins_car_conditional.py +0 -0
  92. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/car/dubins_car_disjoint.py +0 -0
  93. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/car/dubins_car_stljax.py +0 -0
  94. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/cinema_vp.py +0 -0
  95. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/cinema_vp_realtime_base.py +0 -0
  96. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/dr_double_integrator.py +0 -0
  97. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/dr_vp.py +0 -0
  98. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/dr_vp_nodal.py +0 -0
  99. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/dr_vp_polytope.py +0 -0
  100. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/drone_racing.py +0 -0
  101. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/obstacle_avoidance.py +0 -0
  102. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  103. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/obstacle_avoidance_realtime_base.py +0 -0
  104. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  105. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/plotting.py +0 -0
  106. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/plotting_viser.py +0 -0
  107. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/realtime/cinema_vp_realtime.py +0 -0
  108. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/realtime/drone_racing_realtime.py +0 -0
  109. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/realtime/dubins_car_realtime.py +0 -0
  110. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  111. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/rocket/3DoF_pdg.py +0 -0
  112. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/examples/spacecraft/proxops_cw.py +0 -0
  113. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/ctlos_cine.gif +0 -0
  114. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/ctlos_dr.gif +0 -0
  115. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/dtlos_cine.gif +0 -0
  116. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/dtlos_dr.gif +0 -0
  117. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/openscvx_logo.svg +0 -0
  118. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/openscvx_logo_square.png +0 -0
  119. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/oscvx_structure_full_dark.svg +0 -0
  120. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/figures/video_preview.png +0 -0
  121. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/__init__.py +0 -0
  122. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/1-background.avif +0 -0
  123. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
  124. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
  125. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
  126. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
  127. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/2-mars.avif +0 -0
  128. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
  129. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
  130. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
  131. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
  132. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/3-moon.avif +0 -0
  133. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
  134. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
  135. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
  136. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
  137. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
  138. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
  139. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
  140. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
  141. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
  142. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/5-space.avif +0 -0
  143. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
  144. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
  145. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
  146. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
  147. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/6-earth.avif +0 -0
  148. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
  149. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
  150. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
  151. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
  152. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/javascripts/parallax.js +0 -0
  153. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/logo.svg +0 -0
  154. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/stylesheets/custom.css +0 -0
  155. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/assets/stylesheets/parallax.css +0 -0
  156. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/home.html +0 -0
  157. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/main.html +0 -0
  158. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/partials/parallax/hero.html +0 -0
  159. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/material/overrides/partials/parallax.html +0 -0
  160. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/mkdocs.yml +0 -0
  161. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/__init__.py +0 -0
  162. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/algorithms/__init__.py +0 -0
  163. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/algorithms/autotuning.py +0 -0
  164. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/algorithms/base.py +0 -0
  165. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/algorithms/optimization_results.py +0 -0
  166. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/algorithms/penalized_trust_region.py +0 -0
  167. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/config.py +0 -0
  168. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/discretization/__init__.py +0 -0
  169. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/discretization/discretization.py +0 -0
  170. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/expert/__init__.py +0 -0
  171. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/expert/byof.py +0 -0
  172. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/expert/lowering.py +0 -0
  173. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/expert/validation.py +0 -0
  174. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/init/__init__.py +0 -0
  175. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/init/interpolation.py +0 -0
  176. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/integrators/__init__.py +0 -0
  177. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/integrators/runge_kutta.py +0 -0
  178. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/__init__.py +0 -0
  179. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/cvxpy_constraints.py +0 -0
  180. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/cvxpy_variables.py +0 -0
  181. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/dynamics.py +0 -0
  182. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/jax_constraints.py +0 -0
  183. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/parameters.py +0 -0
  184. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/problem.py +0 -0
  185. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/lowered/unified.py +0 -0
  186. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/__init__.py +0 -0
  187. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/plotting.py +0 -0
  188. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/scp_iteration.py +0 -0
  189. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/viser/__init__.py +0 -0
  190. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/viser/animated.py +0 -0
  191. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/viser/plotly_integration.py +0 -0
  192. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/viser/primitives.py +0 -0
  193. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/viser/scp.py +0 -0
  194. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/plotting/viser/server.py +0 -0
  195. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/propagation/__init__.py +0 -0
  196. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/propagation/post_processing.py +0 -0
  197. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/propagation/propagation.py +0 -0
  198. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/solvers/__init__.py +0 -0
  199. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/solvers/base.py +0 -0
  200. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/solvers/ptr_solver.py +0 -0
  201. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/__init__.py +0 -0
  202. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/augmentation.py +0 -0
  203. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/constraint_set.py +0 -0
  204. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/__init__.py +0 -0
  205. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/arithmetic.py +0 -0
  206. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/array.py +0 -0
  207. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/constraint.py +0 -0
  208. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/control.py +0 -0
  209. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/expr.py +0 -0
  210. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  211. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  212. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/lie/se3.py +0 -0
  213. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/lie/so3.py +0 -0
  214. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/linalg.py +0 -0
  215. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/logic.py +0 -0
  216. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/math.py +0 -0
  217. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/spatial.py +0 -0
  218. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/state.py +0 -0
  219. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/stl.py +0 -0
  220. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/variable.py +0 -0
  221. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/expr/vmap.py +0 -0
  222. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/hashing.py +0 -0
  223. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/lower.py +0 -0
  224. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/lowerers/__init__.py +0 -0
  225. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/problem.py +0 -0
  226. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/time.py +0 -0
  227. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/symbolic/unified.py +0 -0
  228. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/utils/__init__.py +0 -0
  229. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/utils/cache.py +0 -0
  230. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/utils/caching.py +0 -0
  231. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/utils/printing.py +0 -0
  232. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/utils/profiling.py +0 -0
  233. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx/utils/utils.py +0 -0
  234. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx.egg-info/dependency_links.txt +0 -0
  235. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx.egg-info/requires.txt +0 -0
  236. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/openscvx.egg-info/top_level.txt +0 -0
  237. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/pyproject.toml +0 -0
  238. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/scripts/gen_example_pages.py +0 -0
  239. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/scripts/gen_ref_pages.py +0 -0
  240. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/setup.cfg +0 -0
  241. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/__init__.py +0 -0
  242. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/brachistochrone_analytical.py +0 -0
  243. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/__init__.py +0 -0
  244. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/__init__.py +0 -0
  245. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_array.py +0 -0
  246. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_expr.py +0 -0
  247. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_lie.py +0 -0
  248. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_linalg.py +0 -0
  249. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_logic.py +0 -0
  250. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_math.py +0 -0
  251. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_node_reference.py +0 -0
  252. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_scaling.py +0 -0
  253. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_spatial.py +0 -0
  254. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/expr/test_vmap.py +0 -0
  255. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/test_augmentation.py +0 -0
  256. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/test_hashing.py +0 -0
  257. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/test_lower_cvxpy.py +0 -0
  258. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/symbolic/test_unified.py +0 -0
  259. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_brachistochrone.py +0 -0
  260. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_cvxpygen_optional.py +0 -0
  261. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_discretization.py +0 -0
  262. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_examples.py +0 -0
  263. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_expert.py +0 -0
  264. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_init.py +0 -0
  265. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_integrators.py +0 -0
  266. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_plotting.py +0 -0
  267. {openscvx-0.3.2.dev328 → openscvx-0.3.2.dev339}/tests/test_propagation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.3.2.dev328
3
+ Version: 0.3.2.dev339
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
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.3.2.dev328'
32
- __version_tuple__ = version_tuple = (0, 3, 2, 'dev328')
31
+ __version__ = version = '0.3.2.dev339'
32
+ __version_tuple__ = version_tuple = (0, 3, 2, 'dev339')
33
33
 
34
- __commit_id__ = commit_id = 'g63a14061a'
34
+ __commit_id__ = commit_id = 'g0ad465892'
@@ -50,7 +50,6 @@ from openscvx.lowered.jax_constraints import (
50
50
  from openscvx.propagation import get_propagation_solver, propagate_trajectory_results
51
51
  from openscvx.solvers import PTRSolver
52
52
  from openscvx.symbolic.builder import preprocess_symbolic_problem
53
- from openscvx.symbolic.constraint_set import ConstraintSet
54
53
  from openscvx.symbolic.expr import CTCS, Constraint
55
54
  from openscvx.symbolic.expr.control import Control
56
55
  from openscvx.symbolic.expr.state import State
@@ -128,7 +127,7 @@ class Problem:
128
127
  # Symbolic Preprocessing & Augmentation
129
128
  self.symbolic: SymbolicProblem = preprocess_symbolic_problem(
130
129
  dynamics=dynamics,
131
- constraints=ConstraintSet(unsorted=list(constraints)),
130
+ constraints=constraints,
132
131
  states=states,
133
132
  controls=controls,
134
133
  N=N,
@@ -55,6 +55,8 @@ from openscvx.symbolic.preprocessing import (
55
55
  validate_dynamics_dict_dimensions,
56
56
  validate_dynamics_dimension,
57
57
  validate_guesses,
58
+ validate_input_types,
59
+ validate_propagation_input_types,
58
60
  validate_shapes,
59
61
  validate_variable_names,
60
62
  )
@@ -64,7 +66,7 @@ from openscvx.symbolic.time import Time
64
66
 
65
67
  def preprocess_symbolic_problem(
66
68
  dynamics: dict,
67
- constraints: ConstraintSet,
69
+ constraints: list,
68
70
  states: List[State],
69
71
  controls: List[Control],
70
72
  N: int,
@@ -99,8 +101,8 @@ def preprocess_symbolic_problem(
99
101
  Args:
100
102
  dynamics: Dictionary mapping state names to dynamics expressions.
101
103
  Example: {"x": v, "v": u}
102
- constraints: ConstraintSet with raw constraints in `unsorted` field.
103
- Create with: ConstraintSet(unsorted=[c1, c2, c3])
104
+ constraints: List of constraint objects (Constraint, NodalConstraint,
105
+ CrossNodeConstraint, or CTCS).
104
106
  states: List of user-defined State objects (should NOT include time or CTCS states)
105
107
  controls: List of user-defined Control objects (should NOT include time dilation)
106
108
  N: Number of discretization nodes in the trajectory
@@ -140,16 +142,13 @@ def preprocess_symbolic_problem(
140
142
  Basic usage with CTCS constraint::
141
143
 
142
144
  import openscvx as ox
143
- from openscvx.symbolic.constraint_set import ConstraintSet
144
145
 
145
146
  x = ox.State("x", shape=(2,))
146
147
  v = ox.State("v", shape=(2,))
147
148
  u = ox.Control("u", shape=(2,))
148
149
 
149
150
  dynamics = {"x": v, "v": u}
150
- constraints = ConstraintSet(unsorted=[
151
- (ox.Norm(x) <= 5.0).over((0, 50))
152
- ])
151
+ constraints = [(ox.Norm(x) <= 5.0).over((0, 50))]
153
152
 
154
153
  problem = preprocess_symbolic_problem(
155
154
  dynamics=dynamics,
@@ -187,6 +186,13 @@ def preprocess_symbolic_problem(
187
186
  print([s.name for s in problem.states_prop])
188
187
  """
189
188
 
189
+ # Validate input types before anything else
190
+ validate_input_types(dynamics, states, controls, constraints, N, time)
191
+ validate_propagation_input_types(dynamics_prop_extra, states_prop_extra)
192
+
193
+ # Wrap validated constraints into a ConstraintSet
194
+ constraints = ConstraintSet(unsorted=list(constraints))
195
+
190
196
  # Validate user-provided variables have required attributes
191
197
  validate_boundary_conditions(states)
192
198
  validate_bounds(states + controls)
@@ -0,0 +1,35 @@
1
+ """CVXPy backend for lowering symbolic expressions to disciplined convex programs.
2
+
3
+ This package implements the CVXPy lowering backend that converts symbolic
4
+ expression AST nodes into CVXPy expressions and constraints for convex
5
+ optimization. The lowering uses a visitor pattern where each expression
6
+ type has a 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.
11
+
12
+ Example::
13
+
14
+ from openscvx.symbolic.lowerers.cvxpy import CvxpyLowerer
15
+
16
+ lowerer = CvxpyLowerer(variable_map={"x": cvx_x, "u": cvx_u})
17
+ cvx_expr = lowerer.lower(expr)
18
+ """
19
+
20
+ # Import visitor modules to trigger @visitor registration.
21
+ # Each module populates _CVXPY_VISITORS as a side effect of import.
22
+ from openscvx.symbolic.lowerers.cvxpy import (
23
+ arithmetic, # noqa: F401
24
+ array, # noqa: F401
25
+ constraint, # noqa: F401
26
+ control, # noqa: F401
27
+ expr, # noqa: F401
28
+ linalg, # noqa: F401
29
+ logic, # noqa: F401
30
+ math, # noqa: F401
31
+ state, # noqa: F401
32
+ )
33
+ from openscvx.symbolic.lowerers.cvxpy._lowerer import CvxpyLowerer, lower_to_cvxpy
34
+
35
+ __all__ = ["CvxpyLowerer", "lower_to_cvxpy"]
@@ -0,0 +1,111 @@
1
+ """CvxpyLowerer class definition.
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.
6
+ """
7
+
8
+ from typing import Dict
9
+
10
+ import cvxpy as cp
11
+
12
+ from openscvx.symbolic.expr import Expr
13
+ from openscvx.symbolic.lowerers.cvxpy._registry import dispatch
14
+
15
+
16
+ class CvxpyLowerer:
17
+ """CVXPy backend for lowering symbolic expressions to disciplined convex programs.
18
+
19
+ This class implements the visitor pattern for converting symbolic expression
20
+ AST nodes to CVXPy expressions and constraints. Each expression type has a
21
+ corresponding visitor function decorated with @visitor that handles the
22
+ lowering logic.
23
+
24
+ The lowering process is recursive: each visitor lowers its child expressions
25
+ first, then composes them into a CVXPy operation. CVXPy will validate DCP
26
+ (Disciplined Convex Programming) compliance when the problem is constructed.
27
+
28
+ Attributes:
29
+ variable_map (dict): Dictionary mapping variable names to CVXPy expressions.
30
+ Must include "x" for states and "u" for controls. May include parameter
31
+ names mapped to CVXPy Parameter objects or constants.
32
+
33
+ Example:
34
+ Lower an expression to CVXPy::
35
+
36
+ import cvxpy as cp
37
+ lowerer = CvxpyLowerer(variable_map={
38
+ "x": cp.Variable(3),
39
+ "u": cp.Variable(2),
40
+ })
41
+ expr = ox.Norm(x)**2 + 0.1 * ox.Norm(u)**2
42
+ cvx_expr = lowerer.lower(expr)
43
+
44
+ Note:
45
+ The lowerer is stateful (stores variable_map) unlike JaxLowerer which
46
+ is stateless. Variables must be registered before lowering expressions
47
+ that reference them.
48
+ """
49
+
50
+ def __init__(self, variable_map: Dict[str, cp.Expression] = None):
51
+ """Initialize the CVXPy lowerer.
52
+
53
+ Args:
54
+ variable_map: Dictionary mapping variable names to CVXPy expressions.
55
+ For State/Control objects, keys should be "x" and "u" respectively.
56
+ For Parameter objects, keys should match their names. If None, an
57
+ empty dictionary is created.
58
+ """
59
+ self.variable_map = variable_map or {}
60
+
61
+ def register_variable(self, name: str, value: cp.Expression):
62
+ """Register a variable in the variable map.
63
+
64
+ Args:
65
+ name: Variable name (e.g., "x", "u", or parameter name)
66
+ value: CVXPy expression to associate with the name
67
+ """
68
+ self.variable_map[name] = value
69
+
70
+ def lower(self, expr: Expr) -> cp.Expression:
71
+ """Lower a symbolic expression to a CVXPy expression.
72
+
73
+ Main entry point for lowering. Delegates to dispatch() which looks up
74
+ the appropriate visitor method based on the expression type.
75
+
76
+ Args:
77
+ expr: Symbolic expression to lower (any Expr subclass)
78
+
79
+ Returns:
80
+ CVXPy expression or constraint
81
+
82
+ Raises:
83
+ NotImplementedError: If no visitor exists for the expression type
84
+ ValueError: If required variables are missing from variable_map
85
+ """
86
+ return dispatch(self, expr)
87
+
88
+
89
+ def lower_to_cvxpy(expr: Expr, variable_map: Dict[str, cp.Expression] = None) -> cp.Expression:
90
+ """Lower symbolic expression to CVXPy expression or constraint.
91
+
92
+ Convenience wrapper that creates a CvxpyLowerer and lowers a single
93
+ symbolic expression to a CVXPy expression. The result can be used in
94
+ CVXPy optimization problems.
95
+
96
+ Args:
97
+ expr: Symbolic expression to lower (any Expr subclass)
98
+ variable_map: Dictionary mapping variable names to CVXPy expressions.
99
+ Must include "x" for states and "u" for controls. May include
100
+ parameter names mapped to CVXPy Parameters or constants.
101
+
102
+ Returns:
103
+ CVXPy expression for arithmetic expressions (Add, Mul, Norm, etc.)
104
+ or CVXPy constraint for constraint expressions (Equality, Inequality)
105
+
106
+ Raises:
107
+ NotImplementedError: If the expression type is not supported
108
+ ValueError: If required variables are missing from variable_map
109
+ """
110
+ lowerer = CvxpyLowerer(variable_map)
111
+ return lowerer.lower(expr)
@@ -0,0 +1,75 @@
1
+ """Visitor registry for the CVXPy 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 CVXPy, no expression subclasses beyond the base ``Expr``) so that
6
+ the visitor modules can import from it without circular-dependency issues.
7
+ """
8
+
9
+ from typing import Any, Callable, Dict, Type
10
+
11
+ from openscvx.symbolic.expr import Expr
12
+
13
+ _CVXPY_VISITORS: Dict[Type[Expr], Callable] = {}
14
+ """Registry mapping expression types to their visitor functions."""
15
+
16
+
17
+ def visitor(expr_cls: Type[Expr]) -> Callable[[Callable], Callable]:
18
+ """Decorator to register a visitor function for an expression type.
19
+
20
+ This decorator registers a visitor method to handle a specific expression
21
+ type during CVXPy lowering. The decorated function is stored in
22
+ _CVXPY_VISITORS and will be called by dispatch() when lowering that
23
+ expression type.
24
+
25
+ Args:
26
+ expr_cls: The Expr subclass this visitor handles (e.g., Add, Mul, Norm)
27
+
28
+ Returns:
29
+ Decorator function that registers the visitor and returns it unchanged
30
+
31
+ Example:
32
+ Register a visitor function for the Add expression::
33
+
34
+ @visitor(Add)
35
+ def _visit_add(lowerer, node: Add):
36
+ ...
37
+
38
+ Note:
39
+ Multiple expression types can share a visitor by stacking decorators::
40
+
41
+ @visitor(Equality)
42
+ @visitor(Inequality)
43
+ def _visit_constraint(lowerer, node: Constraint):
44
+ ...
45
+ """
46
+
47
+ def register(fn: Callable[[Any, Expr], Any]):
48
+ _CVXPY_VISITORS[expr_cls] = fn
49
+ return fn
50
+
51
+ return register
52
+
53
+
54
+ def dispatch(lowerer: Any, expr: Expr) -> Any:
55
+ """Dispatch an expression to its registered visitor function.
56
+
57
+ Looks up the visitor function for the expression's type and calls it.
58
+ This is the core of the visitor pattern implementation.
59
+
60
+ Args:
61
+ lowerer: The CvxpyLowerer instance (provides context for visitor methods)
62
+ expr: The expression node to lower
63
+
64
+ Returns:
65
+ The result of calling the visitor function (CVXPy expression or constraint)
66
+
67
+ Raises:
68
+ NotImplementedError: If no visitor is registered for the expression type
69
+ """
70
+ fn = _CVXPY_VISITORS.get(type(expr))
71
+ if fn is None:
72
+ raise NotImplementedError(
73
+ f"{lowerer.__class__.__name__!r} has no visitor for {type(expr).__name__}"
74
+ )
75
+ return fn(lowerer, expr)
@@ -0,0 +1,149 @@
1
+ """CVXPy visitors for arithmetic expressions.
2
+
3
+ Visitors: Add, Sub, Mul, Div, MatMul, Neg, Power
4
+ """
5
+
6
+ import cvxpy as cp
7
+
8
+ # Expression types to handle — uncomment as you paste visitors:
9
+ from openscvx.symbolic.expr.arithmetic import Add, Div, MatMul, Mul, Neg, Power, Sub
10
+ from openscvx.symbolic.lowerers.cvxpy._registry import visitor # noqa: F401
11
+
12
+
13
+ @visitor(Add)
14
+ def _visit_add(lowerer, node: Add) -> cp.Expression:
15
+ """Lower addition to CVXPy expression.
16
+
17
+ Recursively lowers all terms and composes them with element-wise addition.
18
+ Addition is affine and always DCP-compliant.
19
+
20
+ Args:
21
+ node: Add expression node with multiple terms
22
+
23
+ Returns:
24
+ CVXPy expression representing the sum of all terms
25
+ """
26
+ terms = [lowerer.lower(term) for term in node.terms]
27
+ result = terms[0]
28
+ for term in terms[1:]:
29
+ result = result + term
30
+ return result
31
+
32
+
33
+ @visitor(Sub)
34
+ def _visit_sub(lowerer, node: Sub) -> cp.Expression:
35
+ """Lower subtraction to CVXPy expression (element-wise left - right).
36
+
37
+ Subtraction is affine and always DCP-compliant.
38
+
39
+ Args:
40
+ node: Sub expression node
41
+
42
+ Returns:
43
+ CVXPy expression representing left - right
44
+ """
45
+ left = lowerer.lower(node.left)
46
+ right = lowerer.lower(node.right)
47
+ return left - right
48
+
49
+
50
+ @visitor(Mul)
51
+ def _visit_mul(lowerer, node: Mul) -> cp.Expression:
52
+ """Lower element-wise multiplication to CVXPy expression.
53
+
54
+ Element-wise multiplication is DCP-compliant when at least one operand
55
+ is constant. For quadratic forms, use MatMul instead.
56
+
57
+ Args:
58
+ node: Mul expression node with multiple factors
59
+
60
+ Returns:
61
+ CVXPy expression representing element-wise product
62
+
63
+ Note:
64
+ For convex optimization, typically one factor should be constant.
65
+ CVXPy will raise a DCP error if the composition violates DCP rules.
66
+ """
67
+ factors = [lowerer.lower(factor) for factor in node.factors]
68
+ result = factors[0]
69
+ for factor in factors[1:]:
70
+ result = result * factor
71
+ return result
72
+
73
+
74
+ @visitor(Div)
75
+ def _visit_div(lowerer, node: Div) -> cp.Expression:
76
+ """Lower element-wise division to CVXPy expression.
77
+
78
+ Division is DCP-compliant when the denominator is constant or when
79
+ the numerator is constant and the denominator is concave.
80
+
81
+ Args:
82
+ node: Div expression node
83
+
84
+ Returns:
85
+ CVXPy expression representing left / right
86
+
87
+ Note:
88
+ CVXPy will raise a DCP error if the division violates DCP rules.
89
+ """
90
+ left = lowerer.lower(node.left)
91
+ right = lowerer.lower(node.right)
92
+ return left / right
93
+
94
+
95
+ @visitor(MatMul)
96
+ def _visit_matmul(lowerer, node: MatMul) -> cp.Expression:
97
+ """Lower matrix multiplication to CVXPy expression using @ operator.
98
+
99
+ Matrix multiplication is DCP-compliant when at least one operand is
100
+ constant. Used for quadratic forms like x.T @ Q @ x.
101
+
102
+ Args:
103
+ node: MatMul expression node
104
+
105
+ Returns:
106
+ CVXPy expression representing left @ right
107
+ """
108
+ left = lowerer.lower(node.left)
109
+ right = lowerer.lower(node.right)
110
+ return left @ right
111
+
112
+
113
+ @visitor(Neg)
114
+ def _visit_neg(lowerer, node: Neg) -> cp.Expression:
115
+ """Lower negation (unary minus) to CVXPy expression.
116
+
117
+ Negation preserves DCP properties (negating convex gives concave).
118
+
119
+ Args:
120
+ node: Neg expression node
121
+
122
+ Returns:
123
+ CVXPy expression representing -operand
124
+ """
125
+ operand = lowerer.lower(node.operand)
126
+ return -operand
127
+
128
+
129
+ @visitor(Power)
130
+ def _visit_power(lowerer, node: Power) -> cp.Expression:
131
+ """Lower element-wise power (base**exponent) to CVXPy expression.
132
+
133
+ Power is DCP-compliant for specific exponent values:
134
+ - exponent >= 1: convex (when base >= 0)
135
+ - 0 <= exponent <= 1: concave (when base >= 0)
136
+
137
+ Args:
138
+ node: Power expression node
139
+
140
+ Returns:
141
+ CVXPy expression representing base**exponent
142
+
143
+ Note:
144
+ CVXPy will verify DCP compliance at problem construction time.
145
+ Common convex cases: x^2, x^3, x^4 (even powers)
146
+ """
147
+ base = lowerer.lower(node.base)
148
+ exponent = lowerer.lower(node.exponent)
149
+ return cp.power(base, exponent)
@@ -0,0 +1,152 @@
1
+ """CVXPy visitors for array expressions.
2
+
3
+ Visitors: Index, Concat, Stack, Hstack, Vstack, Block
4
+ """
5
+
6
+ import cvxpy as cp
7
+
8
+ # Expression types to handle — uncomment as you paste visitors:
9
+ from openscvx.symbolic.expr.array import Block, Concat, Hstack, Index, Stack, Vstack
10
+ from openscvx.symbolic.lowerers.cvxpy._registry import visitor # noqa: F401
11
+
12
+
13
+ @visitor(Index)
14
+ def _visit_index(lowerer, node: Index) -> cp.Expression:
15
+ """Lower indexing/slicing operation to CVXPy expression.
16
+
17
+ Indexing preserves DCP properties (indexing into convex is convex).
18
+
19
+ Args:
20
+ node: Index expression node
21
+
22
+ Returns:
23
+ CVXPy expression representing base[index]
24
+ """
25
+ base = lowerer.lower(node.base)
26
+ return base[node.index]
27
+
28
+
29
+ @visitor(Concat)
30
+ def _visit_concat(lowerer, node: Concat) -> cp.Expression:
31
+ """Lower concatenation to CVXPy expression.
32
+
33
+ Concatenates expressions horizontally along axis 0. Scalars are
34
+ promoted to 1D arrays before concatenation. Preserves DCP properties.
35
+
36
+ Args:
37
+ node: Concat expression node
38
+
39
+ Returns:
40
+ CVXPy expression representing horizontal concatenation
41
+
42
+ Note:
43
+ Uses cp.hstack for concatenation. Scalars are reshaped to (1,).
44
+ """
45
+ exprs = [lowerer.lower(child) for child in node.exprs]
46
+ # Ensure all expressions are at least 1D for concatenation
47
+ exprs_1d = []
48
+ for expr in exprs:
49
+ if expr.ndim == 0: # scalar
50
+ exprs_1d.append(cp.reshape(expr, (1,), order="C"))
51
+ else:
52
+ exprs_1d.append(expr)
53
+ return cp.hstack(exprs_1d)
54
+
55
+
56
+ @visitor(Stack)
57
+ def _visit_stack(lowerer, node: Stack) -> cp.Expression:
58
+ """Lower vertical stacking to CVXPy expression.
59
+
60
+ Stacks expressions vertically using cp.vstack. Preserves DCP properties.
61
+
62
+ Args:
63
+ node: Stack expression node with multiple rows
64
+
65
+ Returns:
66
+ CVXPy expression representing vertical stack of rows
67
+
68
+ Note:
69
+ Each row is stacked along axis 0 to create a 2D array.
70
+ """
71
+ rows = [lowerer.lower(row) for row in node.rows]
72
+ # Stack rows vertically
73
+ return cp.vstack(rows)
74
+
75
+
76
+ @visitor(Hstack)
77
+ def _visit_hstack(lowerer, node: Hstack) -> cp.Expression:
78
+ """Lower horizontal stacking to CVXPy expression.
79
+
80
+ For 1D arrays, uses cp.hstack (concatenation). For 2D+ arrays, uses
81
+ cp.bmat with a single row to achieve proper horizontal stacking along
82
+ axis 1, matching numpy.hstack semantics.
83
+
84
+ Args:
85
+ node: Hstack expression node with multiple arrays
86
+
87
+ Returns:
88
+ CVXPy expression representing horizontal stack of arrays
89
+ """
90
+ arrays = [lowerer.lower(arr) for arr in node.arrays]
91
+
92
+ # Check dimensionality from the symbolic node's shape
93
+ shape = node.check_shape()
94
+ if len(shape) == 1:
95
+ # 1D: simple concatenation
96
+ return cp.hstack(arrays)
97
+ else:
98
+ # 2D+: use bmat with single row for proper horizontal stacking
99
+ return cp.bmat([arrays])
100
+
101
+
102
+ @visitor(Vstack)
103
+ def _visit_vstack(lowerer, node: Vstack) -> cp.Expression:
104
+ """Lower vertical stacking to CVXPy expression.
105
+
106
+ Stacks expressions vertically using cp.vstack. Preserves DCP properties.
107
+
108
+ Args:
109
+ node: Vstack expression node with multiple arrays
110
+
111
+ Returns:
112
+ CVXPy expression representing vertical stack of arrays
113
+ """
114
+ arrays = [lowerer.lower(arr) for arr in node.arrays]
115
+ return cp.vstack(arrays)
116
+
117
+
118
+ @visitor(Block)
119
+ def _visit_block(lowerer, node: Block) -> cp.Expression:
120
+ """Lower block matrix construction to CVXPy expression.
121
+
122
+ Assembles a block matrix from nested lists of expressions using cp.bmat.
123
+ This is the CVXPy equivalent of numpy.block() for block matrix construction.
124
+
125
+ Args:
126
+ node: Block expression node with 2D nested structure of expressions
127
+
128
+ Returns:
129
+ CVXPy expression representing the assembled block matrix
130
+
131
+ Raises:
132
+ NotImplementedError: If any block has more than 2 dimensions
133
+
134
+ Note:
135
+ cp.bmat preserves DCP properties when all blocks are DCP-compliant.
136
+ Block matrices are commonly used for constraint aggregation.
137
+ For 3D+ tensors, use JAX lowering instead.
138
+ """
139
+ # Check for 3D+ blocks - CVXPy's bmat only supports 2D
140
+ for i, row in enumerate(node.blocks):
141
+ for j, block in enumerate(row):
142
+ block_shape = block.check_shape()
143
+ if len(block_shape) > 2:
144
+ raise NotImplementedError(
145
+ f"CVXPy does not support Block with tensors of dimension > 2. "
146
+ f"Block[{i}][{j}] has shape {block_shape} ({len(block_shape)}D). "
147
+ f"For N-D tensor block assembly, use JAX lowering instead."
148
+ )
149
+
150
+ # Lower each block expression
151
+ block_exprs = [[lowerer.lower(block) for block in row] for row in node.blocks]
152
+ return cp.bmat(block_exprs)