openscvx 0.3.2.dev333__tar.gz → 0.3.2.dev342__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 (265) hide show
  1. {openscvx-0.3.2.dev333/openscvx.egg-info → openscvx-0.3.2.dev342}/PKG-INFO +1 -1
  2. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/_version.py +3 -3
  3. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/problem.py +1 -2
  4. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/builder.py +13 -7
  5. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/constraint.py +14 -35
  6. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/logic.py +4 -2
  7. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/preprocessing.py +136 -0
  8. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342/openscvx.egg-info}/PKG-INFO +1 -1
  9. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/test_preprocessing.py +175 -0
  10. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/assets/logo.svg +0 -0
  11. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/release-drafter.yml +0 -0
  12. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/_docs.yml +0 -0
  13. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/branch-name.yml +0 -0
  14. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/docs.yml +0 -0
  15. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/lint.yml +0 -0
  16. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/nightly.yml +0 -0
  17. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/release-drafter.yml +0 -0
  18. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/release.yml +0 -0
  19. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/tests-integration.yml +0 -0
  20. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.github/workflows/tests-unit.yml +0 -0
  21. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/.gitignore +0 -0
  22. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/CONTRIBUTING.md +0 -0
  23. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/LICENSE +0 -0
  24. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/README.md +0 -0
  25. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/Foundations/constraint_reformulation.md +0 -0
  26. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/Foundations/control_parameterization.md +0 -0
  27. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/Foundations/discretization.md +0 -0
  28. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/Foundations/ocp.md +0 -0
  29. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/Foundations/scvx.md +0 -0
  30. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/Foundations/time_dilation.md +0 -0
  31. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UnderTheHood/lowering_architecture.md +0 -0
  32. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  33. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/00_introduction.md +0 -0
  34. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  35. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  36. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  37. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  38. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/05_visualization.md +0 -0
  39. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/06_logic.md +0 -0
  40. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/UsersGuide/07_lie.md +0 -0
  41. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/favicon.png +0 -0
  42. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/images/ct-scvx_dark.png +0 -0
  43. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/images/ct-scvx_light.png +0 -0
  44. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/images/ctcs_dark.png +0 -0
  45. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/images/ctcs_light.png +0 -0
  46. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/images/problem_class_dark.png +0 -0
  47. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/images/problem_class_light.png +0 -0
  48. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/assets/logo.svg +0 -0
  49. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/citation.md +0 -0
  50. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/examples.md +0 -0
  51. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/getting-started.md +0 -0
  52. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/index.md +0 -0
  53. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/docs/javascripts/mathjax.js +0 -0
  54. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/abstract/brachistochrone.py +0 -0
  55. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/arm/three_link_arm.py +0 -0
  56. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/car/dubins_car.py +0 -0
  57. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/car/dubins_car_conditional.py +0 -0
  58. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/car/dubins_car_disjoint.py +0 -0
  59. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/car/dubins_car_stljax.py +0 -0
  60. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/cinema_vp.py +0 -0
  61. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/cinema_vp_realtime_base.py +0 -0
  62. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/dr_double_integrator.py +0 -0
  63. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/dr_vp.py +0 -0
  64. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/dr_vp_nodal.py +0 -0
  65. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/dr_vp_polytope.py +0 -0
  66. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/drone_racing.py +0 -0
  67. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/obstacle_avoidance.py +0 -0
  68. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  69. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/obstacle_avoidance_realtime_base.py +0 -0
  70. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  71. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/plotting.py +0 -0
  72. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/plotting_viser.py +0 -0
  73. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/realtime/cinema_vp_realtime.py +0 -0
  74. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/realtime/drone_racing_realtime.py +0 -0
  75. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/realtime/dubins_car_realtime.py +0 -0
  76. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  77. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/rocket/3DoF_pdg.py +0 -0
  78. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/examples/spacecraft/proxops_cw.py +0 -0
  79. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/ctlos_cine.gif +0 -0
  80. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/ctlos_dr.gif +0 -0
  81. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/dtlos_cine.gif +0 -0
  82. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/dtlos_dr.gif +0 -0
  83. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/openscvx_logo.svg +0 -0
  84. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/openscvx_logo_square.png +0 -0
  85. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/oscvx_structure_full_dark.svg +0 -0
  86. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/figures/video_preview.png +0 -0
  87. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/__init__.py +0 -0
  88. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/1-background.avif +0 -0
  89. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
  90. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
  91. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
  92. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
  93. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/2-mars.avif +0 -0
  94. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
  95. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
  96. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
  97. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
  98. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/3-moon.avif +0 -0
  99. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
  100. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
  101. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
  102. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
  103. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
  104. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
  105. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
  106. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
  107. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
  108. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/5-space.avif +0 -0
  109. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
  110. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
  111. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
  112. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
  113. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/6-earth.avif +0 -0
  114. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
  115. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
  116. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
  117. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
  118. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/javascripts/parallax.js +0 -0
  119. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/logo.svg +0 -0
  120. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/stylesheets/custom.css +0 -0
  121. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/assets/stylesheets/parallax.css +0 -0
  122. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/home.html +0 -0
  123. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/main.html +0 -0
  124. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/partials/parallax/hero.html +0 -0
  125. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/material/overrides/partials/parallax.html +0 -0
  126. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/mkdocs.yml +0 -0
  127. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/__init__.py +0 -0
  128. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/algorithms/__init__.py +0 -0
  129. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/algorithms/autotuning.py +0 -0
  130. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/algorithms/base.py +0 -0
  131. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/algorithms/optimization_results.py +0 -0
  132. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/algorithms/penalized_trust_region.py +0 -0
  133. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/config.py +0 -0
  134. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/discretization/__init__.py +0 -0
  135. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/discretization/discretization.py +0 -0
  136. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/expert/__init__.py +0 -0
  137. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/expert/byof.py +0 -0
  138. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/expert/lowering.py +0 -0
  139. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/expert/validation.py +0 -0
  140. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/init/__init__.py +0 -0
  141. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/init/interpolation.py +0 -0
  142. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/integrators/__init__.py +0 -0
  143. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/integrators/runge_kutta.py +0 -0
  144. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/__init__.py +0 -0
  145. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/cvxpy_constraints.py +0 -0
  146. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/cvxpy_variables.py +0 -0
  147. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/dynamics.py +0 -0
  148. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/jax_constraints.py +0 -0
  149. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/parameters.py +0 -0
  150. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/problem.py +0 -0
  151. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/lowered/unified.py +0 -0
  152. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/__init__.py +0 -0
  153. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/plotting.py +0 -0
  154. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/scp_iteration.py +0 -0
  155. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/viser/__init__.py +0 -0
  156. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/viser/animated.py +0 -0
  157. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/viser/plotly_integration.py +0 -0
  158. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/viser/primitives.py +0 -0
  159. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/viser/scp.py +0 -0
  160. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/plotting/viser/server.py +0 -0
  161. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/propagation/__init__.py +0 -0
  162. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/propagation/post_processing.py +0 -0
  163. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/propagation/propagation.py +0 -0
  164. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/solvers/__init__.py +0 -0
  165. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/solvers/base.py +0 -0
  166. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/solvers/ptr_solver.py +0 -0
  167. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/__init__.py +0 -0
  168. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/augmentation.py +0 -0
  169. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/constraint_set.py +0 -0
  170. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/__init__.py +0 -0
  171. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/arithmetic.py +0 -0
  172. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/array.py +0 -0
  173. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/constraint.py +0 -0
  174. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/control.py +0 -0
  175. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/expr.py +0 -0
  176. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  177. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  178. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/lie/se3.py +0 -0
  179. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/lie/so3.py +0 -0
  180. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/linalg.py +0 -0
  181. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/logic.py +0 -0
  182. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/math.py +0 -0
  183. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/spatial.py +0 -0
  184. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/state.py +0 -0
  185. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/stl.py +0 -0
  186. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/variable.py +0 -0
  187. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/expr/vmap.py +0 -0
  188. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/hashing.py +0 -0
  189. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lower.py +0 -0
  190. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/__init__.py +0 -0
  191. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  192. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  193. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  194. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  195. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  196. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  197. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  198. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  199. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  200. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  201. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  202. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  203. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  204. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  205. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  206. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  207. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  208. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  209. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  210. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  211. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  212. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  213. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  214. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  215. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  216. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  217. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/problem.py +0 -0
  218. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/time.py +0 -0
  219. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/symbolic/unified.py +0 -0
  220. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/utils/__init__.py +0 -0
  221. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/utils/cache.py +0 -0
  222. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/utils/caching.py +0 -0
  223. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/utils/printing.py +0 -0
  224. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/utils/profiling.py +0 -0
  225. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx/utils/utils.py +0 -0
  226. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx.egg-info/SOURCES.txt +0 -0
  227. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx.egg-info/dependency_links.txt +0 -0
  228. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx.egg-info/requires.txt +0 -0
  229. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/openscvx.egg-info/top_level.txt +0 -0
  230. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/pyproject.toml +0 -0
  231. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/scripts/gen_example_pages.py +0 -0
  232. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/scripts/gen_ref_pages.py +0 -0
  233. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/setup.cfg +0 -0
  234. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/__init__.py +0 -0
  235. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/brachistochrone_analytical.py +0 -0
  236. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/__init__.py +0 -0
  237. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/__init__.py +0 -0
  238. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_arithmetic.py +0 -0
  239. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_array.py +0 -0
  240. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_constraint.py +0 -0
  241. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_expr.py +0 -0
  242. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_lie.py +0 -0
  243. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_linalg.py +0 -0
  244. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_logic.py +0 -0
  245. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_math.py +0 -0
  246. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_node_reference.py +0 -0
  247. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_parameters.py +0 -0
  248. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_scaling.py +0 -0
  249. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_spatial.py +0 -0
  250. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_variable.py +0 -0
  251. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/expr/test_vmap.py +0 -0
  252. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/test_augmentation.py +0 -0
  253. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/test_hashing.py +0 -0
  254. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/test_lower_cvxpy.py +0 -0
  255. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/test_lower_jax.py +0 -0
  256. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/symbolic/test_unified.py +0 -0
  257. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_brachistochrone.py +0 -0
  258. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_cvxpygen_optional.py +0 -0
  259. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_discretization.py +0 -0
  260. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_examples.py +0 -0
  261. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_expert.py +0 -0
  262. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_init.py +0 -0
  263. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_integrators.py +0 -0
  264. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/tests/test_plotting.py +0 -0
  265. {openscvx-0.3.2.dev333 → openscvx-0.3.2.dev342}/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.dev333
3
+ Version: 0.3.2.dev342
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.dev333'
32
- __version_tuple__ = version_tuple = (0, 3, 2, 'dev333')
31
+ __version__ = version = '0.3.2.dev342'
32
+ __version_tuple__ = version_tuple = (0, 3, 2, 'dev342')
33
33
 
34
- __commit_id__ = commit_id = 'g298f4bc4f'
34
+ __commit_id__ = commit_id = 'gd4121da9a'
@@ -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)
@@ -3,9 +3,6 @@
3
3
  Visitors: Equality, Inequality, NodalConstraint, CrossNodeConstraint, CTCS
4
4
  """
5
5
 
6
- import jax.numpy as jnp
7
- from jax.lax import cond
8
-
9
6
  # Expression types to handle — uncomment as you paste visitors:
10
7
  from openscvx.symbolic.expr.constraint import (
11
8
  CTCS,
@@ -15,6 +12,7 @@ from openscvx.symbolic.expr.constraint import (
15
12
  Inequality,
16
13
  NodalConstraint,
17
14
  )
15
+ from openscvx.symbolic.expr.logic import Cond
18
16
  from openscvx.symbolic.lowerers.jax._registry import visitor # noqa: F401
19
17
 
20
18
 
@@ -106,17 +104,13 @@ def _visit_cross_node_constraint(lowerer, node: CrossNodeConstraint):
106
104
  return trajectory_constraint
107
105
 
108
106
 
109
- # TODO: (norrisg) CTCS is playing 2 roles here: both as a constraint wrapper and as the penalty
110
- # expression w/ conditional logic. Consider adding conditional logic as separate AST nodes.
111
- # Then, CTCS remains a wrapper and we just wrap the penalty expression with the conditional
112
- # logic when we lower it.
113
107
  @visitor(CTCS)
114
108
  def _visit_ctcs(lowerer, node: CTCS):
115
109
  """Lower CTCS (Continuous-Time Constraint Satisfaction) to JAX function.
116
110
 
117
111
  CTCS constraints use penalty methods to enforce constraints over continuous
118
- time intervals. The lowered function includes conditional logic to activate
119
- the penalty only within the specified node interval.
112
+ time intervals. When a node range is specified, the penalty expression is
113
+ wrapped in a Cond node to activate it only within that interval.
120
114
 
121
115
  Args:
122
116
  node: CTCS constraint node with penalty expression and optional node range
@@ -125,35 +119,20 @@ def _visit_ctcs(lowerer, node: CTCS):
125
119
  Function (x, u, current_node, params) -> penalty value or 0
126
120
 
127
121
  Note:
128
- Uses jax.lax.cond for JAX-traceable conditional evaluation. The penalty
129
- is active only when current_node is in [start_node, end_node).
122
+ The penalty is active only when current_node is in [start_node, end_node).
130
123
  If no node range is specified, the penalty is always active.
124
+ Conditional node-range logic is delegated to the Cond AST node.
131
125
 
132
126
  See Also:
133
127
  - CTCS: The symbolic CTCS constraint class
128
+ - Cond: Conditional AST node used for node-range gating
134
129
  - penalty functions: PositivePart, Huber, SmoothReLU
135
130
  """
136
- # Lower the penalty expression (which includes the constraint residual)
137
- penalty_expr_fn = lowerer.lower(node.penalty_expr())
138
-
139
- def ctcs_fn(x, u, current_node, params):
140
- # Check if constraint is active at this node
141
- if node.nodes is not None:
142
- start_node, end_node = node.nodes
143
- # Extract scalar value from current_node (which may be array or scalar)
144
- # Keep as JAX array for tracing compatibility
145
- node_scalar = jnp.atleast_1d(current_node)[0]
146
- is_active = (start_node <= node_scalar) & (node_scalar < end_node)
147
-
148
- # Use jax.lax.cond for conditional evaluation
149
- return cond(
150
- is_active,
151
- lambda _: penalty_expr_fn(x, u, current_node, params),
152
- lambda _: 0.0,
153
- operand=None,
154
- )
155
- else:
156
- # Always active if no node range specified
157
- return penalty_expr_fn(x, u, current_node, params)
158
-
159
- return ctcs_fn
131
+ penalty_expr = node.penalty_expr()
132
+
133
+ if node.nodes is not None:
134
+ # Wrap penalty in a Cond node that gates on the node range
135
+ gated_expr = Cond(None, penalty_expr, 0.0, node_ranges=[node.nodes])
136
+ return lowerer.lower(gated_expr)
137
+ else:
138
+ return lowerer.lower(penalty_expr)
@@ -143,10 +143,12 @@ def _visit_cond(lowerer, node: Cond):
143
143
 
144
144
  # If node_ranges is specified, check if current node is in range
145
145
  if node_ranges is not None:
146
- # Check if node_arg is within any of the specified ranges [start, end)
146
+ # Extract scalar from node_arg (which may be array or scalar from vmap)
147
+ node_scalar = jnp.atleast_1d(node_arg)[0]
148
+ # Check if node_scalar is within any of the specified ranges [start, end)
147
149
  in_range = jnp.array(False)
148
150
  for start, end in node_ranges:
149
- in_range = in_range | ((node_arg >= start) & (node_arg < end))
151
+ in_range = in_range | ((node_scalar >= start) & (node_scalar < end))
150
152
  # Combined predicate: must be in range AND predicate satisfied
151
153
  pred_bool = in_range & pred_bool
152
154
 
@@ -748,6 +748,142 @@ def validate_bounds(variables: List[Variable]) -> None:
748
748
  )
749
749
 
750
750
 
751
+ def validate_input_types(
752
+ dynamics: any,
753
+ states: any,
754
+ controls: any,
755
+ constraints: any,
756
+ N: any,
757
+ time: any,
758
+ ) -> None:
759
+ """Validate that all user-facing inputs have correct types.
760
+
761
+ This catches common user errors like passing a single State or Control
762
+ instead of a list, or passing wrong types for dynamics, N, or time.
763
+ Should be called before any other validation in the preprocessing pipeline.
764
+
765
+ Raises:
766
+ TypeError: If any input has the wrong type
767
+ ValueError: If N is not positive
768
+ """
769
+ from openscvx.symbolic.expr import CTCS, Constraint, CrossNodeConstraint, NodalConstraint
770
+ from openscvx.symbolic.time import Time
771
+
772
+ if not isinstance(dynamics, dict):
773
+ raise TypeError(
774
+ f"'dynamics' must be a dict mapping state names to expressions, "
775
+ f"got {type(dynamics).__name__}"
776
+ )
777
+
778
+ if not isinstance(states, list):
779
+ hint = ""
780
+ if isinstance(states, State):
781
+ hint = f" Hint: use states=[{states.name}] instead of states={states.name}"
782
+ raise TypeError(
783
+ f"'states' must be a list of State objects, got {type(states).__name__}.{hint}"
784
+ )
785
+
786
+ for i, s in enumerate(states):
787
+ if not isinstance(s, State):
788
+ raise TypeError(f"states[{i}] must be a State, got {type(s).__name__}")
789
+
790
+ if not isinstance(controls, list):
791
+ hint = ""
792
+ if isinstance(controls, Control):
793
+ hint = f" Hint: use controls=[{controls.name}] instead of controls={controls.name}"
794
+ raise TypeError(
795
+ f"'controls' must be a list of Control objects, got {type(controls).__name__}.{hint}"
796
+ )
797
+
798
+ for i, c in enumerate(controls):
799
+ if not isinstance(c, Control):
800
+ raise TypeError(f"controls[{i}] must be a Control, got {type(c).__name__}")
801
+
802
+ if not isinstance(constraints, list):
803
+ raise TypeError(
804
+ f"'constraints' must be a list of Constraint objects, got {type(constraints).__name__}"
805
+ )
806
+
807
+ valid_constraint_types = (Constraint, NodalConstraint, CrossNodeConstraint, CTCS)
808
+ for i, c in enumerate(constraints):
809
+ if not isinstance(c, valid_constraint_types):
810
+ raise TypeError(
811
+ f"constraints[{i}] must be a Constraint, NodalConstraint, "
812
+ f"CrossNodeConstraint, or CTCS, got {type(c).__name__}"
813
+ )
814
+
815
+ if not isinstance(N, int):
816
+ raise TypeError(f"'N' must be an integer, got {type(N).__name__}")
817
+
818
+ if N < 1:
819
+ raise ValueError(f"'N' must be positive, got {N}")
820
+
821
+ if not isinstance(time, Time):
822
+ raise TypeError(f"'time' must be a Time object, got {type(time).__name__}")
823
+
824
+
825
+ def validate_propagation_input_types(
826
+ dynamics_prop_extra: any,
827
+ states_prop_extra: any,
828
+ ) -> None:
829
+ """Validate types for optional propagation inputs.
830
+
831
+ These parameters must either both be None or both be provided.
832
+ When provided, dynamics_prop_extra must be a dict and states_prop_extra
833
+ must be a list of State objects.
834
+
835
+ Args:
836
+ dynamics_prop_extra: Should be None or a dict mapping state names to expressions
837
+ states_prop_extra: Should be None or a list of State objects
838
+
839
+ Raises:
840
+ TypeError: If either input has the wrong type
841
+ ValueError: If only one of the two is provided
842
+
843
+ Example:
844
+ distance = ox.State("distance", shape=(1,))
845
+
846
+ # Wrong: passing bare State instead of list
847
+ validate_propagation_input_types({"distance": expr}, distance)
848
+ # Raises TypeError: 'states_prop_extra' must be a list ...
849
+ """
850
+ both_none = dynamics_prop_extra is None and states_prop_extra is None
851
+ both_set = dynamics_prop_extra is not None and states_prop_extra is not None
852
+
853
+ if not both_none and not both_set:
854
+ provided = "dynamics_prop" if dynamics_prop_extra is not None else "states_prop"
855
+ missing = "states_prop" if dynamics_prop_extra is not None else "dynamics_prop"
856
+ raise ValueError(
857
+ f"'{provided}' was provided but '{missing}' was not. "
858
+ f"Both must be provided together, or both omitted."
859
+ )
860
+
861
+ if both_none:
862
+ return
863
+
864
+ if not isinstance(dynamics_prop_extra, dict):
865
+ raise TypeError(
866
+ f"'dynamics_prop' must be a dict mapping state names to expressions, "
867
+ f"got {type(dynamics_prop_extra).__name__}"
868
+ )
869
+
870
+ if not isinstance(states_prop_extra, list):
871
+ hint = ""
872
+ if isinstance(states_prop_extra, State):
873
+ hint = (
874
+ f" Hint: use states_prop=[{states_prop_extra.name}]"
875
+ f" instead of states_prop={states_prop_extra.name}"
876
+ )
877
+ raise TypeError(
878
+ f"'states_prop' must be a list of State objects, "
879
+ f"got {type(states_prop_extra).__name__}.{hint}"
880
+ )
881
+
882
+ for i, s in enumerate(states_prop_extra):
883
+ if not isinstance(s, State):
884
+ raise TypeError(f"states_prop[{i}] must be a State, got {type(s).__name__}")
885
+
886
+
751
887
  def validate_guesses(variables: List[Variable]) -> None:
752
888
  """Validate that all variables have initial guesses set.
753
889
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.3.2.dev333
3
+ Version: 0.3.2.dev342
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
@@ -15,6 +15,8 @@ from openscvx.symbolic.preprocessing import (
15
15
  validate_dynamics_dict_dimensions,
16
16
  validate_dynamics_dimension,
17
17
  validate_guesses,
18
+ validate_input_types,
19
+ validate_propagation_input_types,
18
20
  validate_variable_names,
19
21
  )
20
22
 
@@ -976,3 +978,176 @@ def test_validate_guesses_raises_missing():
976
978
  match="Control 'thrust' is missing initial guess.*controls require explicit guesses",
977
979
  ):
978
980
  validate_guesses([u])
981
+
982
+
983
+ # =============================================================================
984
+ # validate_input_types Tests
985
+ # =============================================================================
986
+
987
+
988
+ @pytest.fixture
989
+ def valid_inputs():
990
+ """Provide a minimal set of valid inputs for validate_input_types."""
991
+ from openscvx.symbolic.time import Time
992
+
993
+ x = State("x", shape=(3,))
994
+ u = Control("u", shape=(2,))
995
+ dynamics = {"x": x}
996
+ constraints = [x <= 5]
997
+ time = Time(initial=0.0, final=10.0, min=0.0, max=20.0)
998
+ return dynamics, [x], [u], constraints, 50, time
999
+
1000
+
1001
+ def test_validate_input_types_passes(valid_inputs):
1002
+ """Test that valid inputs pass validation."""
1003
+ dynamics, states, controls, constraints, N, time = valid_inputs
1004
+ validate_input_types(dynamics, states, controls, constraints, N, time)
1005
+
1006
+
1007
+ def test_validate_input_types_bare_control(valid_inputs):
1008
+ """Test that passing a bare Control instead of a list raises TypeError with hint."""
1009
+ dynamics, states, _, constraints, N, time = valid_inputs
1010
+ u = Control("thrust", shape=(2,))
1011
+
1012
+ with pytest.raises(
1013
+ TypeError, match=r"'controls' must be a list.*Control.*Hint.*controls=\[thrust\]"
1014
+ ):
1015
+ validate_input_types(dynamics, states, u, constraints, N, time)
1016
+
1017
+
1018
+ def test_validate_input_types_bare_state(valid_inputs):
1019
+ """Test that passing a bare State instead of a list raises TypeError with hint."""
1020
+ dynamics, _, controls, constraints, N, time = valid_inputs
1021
+ x = State("position", shape=(3,))
1022
+
1023
+ with pytest.raises(
1024
+ TypeError, match=r"'states' must be a list.*State.*Hint.*states=\[position\]"
1025
+ ):
1026
+ validate_input_types(dynamics, x, controls, constraints, N, time)
1027
+
1028
+
1029
+ def test_validate_input_types_wrong_element_in_states(valid_inputs):
1030
+ """Test that a non-State element in the states list raises TypeError."""
1031
+ dynamics, _, controls, constraints, N, time = valid_inputs
1032
+
1033
+ with pytest.raises(TypeError, match=r"states\[0\] must be a State, got str"):
1034
+ validate_input_types(dynamics, ["not_a_state"], controls, constraints, N, time)
1035
+
1036
+
1037
+ def test_validate_input_types_wrong_element_in_controls(valid_inputs):
1038
+ """Test that a non-Control element in the controls list raises TypeError."""
1039
+ dynamics, states, _, constraints, N, time = valid_inputs
1040
+
1041
+ with pytest.raises(TypeError, match=r"controls\[0\] must be a Control, got int"):
1042
+ validate_input_types(dynamics, states, [42], constraints, N, time)
1043
+
1044
+
1045
+ def test_validate_input_types_dynamics_not_dict(valid_inputs):
1046
+ """Test that passing non-dict dynamics raises TypeError."""
1047
+ _, states, controls, constraints, N, time = valid_inputs
1048
+
1049
+ with pytest.raises(TypeError, match="'dynamics' must be a dict"):
1050
+ validate_input_types([1, 2, 3], states, controls, constraints, N, time)
1051
+
1052
+
1053
+ def test_validate_input_types_constraints_not_list(valid_inputs):
1054
+ """Test that passing a non-list constraints raises TypeError."""
1055
+ dynamics, states, controls, _, N, time = valid_inputs
1056
+
1057
+ with pytest.raises(TypeError, match="'constraints' must be a list"):
1058
+ validate_input_types(dynamics, states, controls, "not_a_list", N, time)
1059
+
1060
+
1061
+ def test_validate_input_types_constraints_invalid_element(valid_inputs):
1062
+ """Test that non-constraint elements raise TypeError."""
1063
+ dynamics, states, controls, _, N, time = valid_inputs
1064
+
1065
+ with pytest.raises(TypeError, match=r"constraints\[0\] must be a Constraint.*got int"):
1066
+ validate_input_types(dynamics, states, controls, [42], N, time)
1067
+
1068
+
1069
+ def test_validate_input_types_constraints_mixed_valid_and_invalid(valid_inputs):
1070
+ """Test that invalid element is caught even after valid ones."""
1071
+ dynamics, states, controls, _, N, time = valid_inputs
1072
+ x = State("x", shape=(3,))
1073
+
1074
+ with pytest.raises(TypeError, match=r"constraints\[1\] must be a Constraint.*got str"):
1075
+ validate_input_types(dynamics, states, controls, [x <= 5, "bad"], N, time)
1076
+
1077
+
1078
+ def test_validate_input_types_N_not_int(valid_inputs):
1079
+ """Test that passing non-int N raises TypeError."""
1080
+ dynamics, states, controls, constraints, _, time = valid_inputs
1081
+
1082
+ with pytest.raises(TypeError, match="'N' must be an integer, got float"):
1083
+ validate_input_types(dynamics, states, controls, constraints, 50.0, time)
1084
+
1085
+
1086
+ def test_validate_input_types_N_not_positive(valid_inputs):
1087
+ """Test that passing non-positive N raises ValueError."""
1088
+ dynamics, states, controls, constraints, _, time = valid_inputs
1089
+
1090
+ with pytest.raises(ValueError, match="'N' must be positive, got 0"):
1091
+ validate_input_types(dynamics, states, controls, constraints, 0, time)
1092
+
1093
+ with pytest.raises(ValueError, match="'N' must be positive, got -5"):
1094
+ validate_input_types(dynamics, states, controls, constraints, -5, time)
1095
+
1096
+
1097
+ def test_validate_input_types_time_not_time(valid_inputs):
1098
+ """Test that passing non-Time object raises TypeError."""
1099
+ dynamics, states, controls, constraints, N, _ = valid_inputs
1100
+
1101
+ with pytest.raises(TypeError, match="'time' must be a Time object, got float"):
1102
+ validate_input_types(dynamics, states, controls, constraints, N, 10.0)
1103
+
1104
+
1105
+ # =============================================================================
1106
+ # validate_propagation_input_types Tests
1107
+ # =============================================================================
1108
+
1109
+
1110
+ def test_validate_propagation_input_types_both_none():
1111
+ """Test that both None passes validation."""
1112
+ validate_propagation_input_types(None, None)
1113
+
1114
+
1115
+ def test_validate_propagation_input_types_both_valid():
1116
+ """Test that valid dict + list passes validation."""
1117
+ distance = State("distance", shape=(1,))
1118
+ validate_propagation_input_types({"distance": distance}, [distance])
1119
+
1120
+
1121
+ def test_validate_propagation_input_types_only_dynamics():
1122
+ """Test that providing dynamics_prop without states_prop raises ValueError."""
1123
+ with pytest.raises(ValueError, match="'dynamics_prop' was provided but 'states_prop' was not"):
1124
+ validate_propagation_input_types({"distance": 1.0}, None)
1125
+
1126
+
1127
+ def test_validate_propagation_input_types_only_states():
1128
+ """Test that providing states_prop without dynamics_prop raises ValueError."""
1129
+ distance = State("distance", shape=(1,))
1130
+ with pytest.raises(ValueError, match="'states_prop' was provided but 'dynamics_prop' was not"):
1131
+ validate_propagation_input_types(None, [distance])
1132
+
1133
+
1134
+ def test_validate_propagation_input_types_bare_state():
1135
+ """Test that passing a bare State instead of a list raises TypeError with hint."""
1136
+ distance = State("distance", shape=(1,))
1137
+ with pytest.raises(
1138
+ TypeError, match=r"'states_prop' must be a list.*Hint.*states_prop=\[distance\]"
1139
+ ):
1140
+ validate_propagation_input_types({"distance": distance}, distance)
1141
+
1142
+
1143
+ def test_validate_propagation_input_types_dynamics_not_dict():
1144
+ """Test that passing non-dict dynamics_prop raises TypeError."""
1145
+ distance = State("distance", shape=(1,))
1146
+ with pytest.raises(TypeError, match="'dynamics_prop' must be a dict"):
1147
+ validate_propagation_input_types([distance], [distance])
1148
+
1149
+
1150
+ def test_validate_propagation_input_types_wrong_element_in_states():
1151
+ """Test that a non-State element in states_prop raises TypeError."""
1152
+ with pytest.raises(TypeError, match=r"states_prop\[0\] must be a State, got str"):
1153
+ validate_propagation_input_types({"x": 1.0}, ["not_a_state"])
File without changes