openscvx 0.3.2.dev215__tar.gz → 0.3.2.dev228__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 (235) hide show
  1. {openscvx-0.3.2.dev215/openscvx.egg-info → openscvx-0.3.2.dev228}/PKG-INFO +1 -1
  2. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/abstract/3DoF_pdg.py +0 -3
  3. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/abstract/brachistochrone.py +0 -2
  4. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/car/dubins_car.py +0 -2
  5. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/car/dubins_car_disjoint.py +0 -1
  6. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/car/dubins_car_stljax.py +0 -3
  7. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/cinema_vp.py +0 -1
  8. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/dr_double_integrator.py +0 -2
  9. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/drone_racing.py +0 -4
  10. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/obstacle_avoidance.py +0 -6
  11. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/obstacle_avoidance_nodal.py +0 -6
  12. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/obstacle_avoidance_realtime_base.py +0 -7
  13. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/spacecraft/proxops_cw.py +0 -1
  14. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/_version.py +3 -3
  15. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/problem.py +4 -0
  16. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/builder.py +15 -0
  17. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/preprocessing.py +82 -0
  18. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/utils/caching.py +37 -0
  19. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228/openscvx.egg-info}/PKG-INFO +1 -1
  20. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/test_hashing.py +39 -0
  21. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/test_preprocessing.py +175 -0
  22. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/test_brachistochrone.py +2 -11
  23. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/test_expert.py +2 -0
  24. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/assets/logo.svg +0 -0
  25. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/release-drafter.yml +0 -0
  26. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/_docs.yml +0 -0
  27. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/docs.yml +0 -0
  28. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/lint.yml +0 -0
  29. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/nightly.yml +0 -0
  30. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/release-drafter.yml +0 -0
  31. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/release.yml +0 -0
  32. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/tests-integration.yml +0 -0
  33. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.github/workflows/tests-unit.yml +0 -0
  34. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/.gitignore +0 -0
  35. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/CONTRIBUTING.md +0 -0
  36. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/LICENSE +0 -0
  37. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/README.md +0 -0
  38. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Overview/constraint_reformulation.md +0 -0
  39. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Overview/control_parameterization.md +0 -0
  40. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Overview/discretization.md +0 -0
  41. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Overview/ocp.md +0 -0
  42. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Overview/scvx.md +0 -0
  43. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Overview/time_dilation.md +0 -0
  44. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/UnderTheHood/lowering_architecture.md +0 -0
  45. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  46. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/advanced_problem_setup.md +0 -0
  47. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/api.md +0 -0
  48. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/api_constraints.md +0 -0
  49. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/api_control.md +0 -0
  50. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/api_integrators.md +0 -0
  51. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/api_state.md +0 -0
  52. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/api_trajoptproblem.md +0 -0
  53. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/api_variable.md +0 -0
  54. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/basic_problem_setup.md +0 -0
  55. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/tutorial_6dof_los_guidance.md +0 -0
  56. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/tutorial_6dof_obstacle_avoidance.md +0 -0
  57. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/Usage/tutorials.md +0 -0
  58. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/favicon.png +0 -0
  59. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/images/ct-scvx_dark.png +0 -0
  60. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/images/ct-scvx_light.png +0 -0
  61. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/images/ctcs_dark.png +0 -0
  62. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/images/ctcs_light.png +0 -0
  63. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/images/problem_class_dark.png +0 -0
  64. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/images/problem_class_light.png +0 -0
  65. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/assets/logo.svg +0 -0
  66. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/citation.md +0 -0
  67. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/examples.md +0 -0
  68. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/getting-started.md +0 -0
  69. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/index.md +0 -0
  70. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/docs/javascripts/mathjax.js +0 -0
  71. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/arm/three_link_arm.py +0 -0
  72. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/cinema_vp_realtime_base.py +0 -0
  73. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/dr_vp.py +0 -0
  74. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/dr_vp_nodal.py +0 -0
  75. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/dr_vp_polytope.py +0 -0
  76. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  77. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/plotting.py +0 -0
  78. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/plotting_viser.py +0 -0
  79. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/realtime/cinema_vp_realtime.py +0 -0
  80. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/realtime/drone_racing_realtime.py +0 -0
  81. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/realtime/dubins_car_realtime.py +0 -0
  82. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  83. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/ctlos_cine.gif +0 -0
  84. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/ctlos_dr.gif +0 -0
  85. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/dtlos_cine.gif +0 -0
  86. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/dtlos_dr.gif +0 -0
  87. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/openscvx_logo.svg +0 -0
  88. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/openscvx_logo_square.png +0 -0
  89. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/oscvx_structure_full_dark.svg +0 -0
  90. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/figures/video_preview.png +0 -0
  91. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/__init__.py +0 -0
  92. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/1-background.avif +0 -0
  93. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
  94. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
  95. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
  96. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
  97. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/2-mars.avif +0 -0
  98. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
  99. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
  100. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
  101. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
  102. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/3-moon.avif +0 -0
  103. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
  104. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
  105. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
  106. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
  107. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
  108. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
  109. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
  110. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
  111. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
  112. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/5-space.avif +0 -0
  113. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
  114. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
  115. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
  116. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
  117. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/6-earth.avif +0 -0
  118. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
  119. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
  120. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
  121. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
  122. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/javascripts/parallax.js +0 -0
  123. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/logo.svg +0 -0
  124. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/stylesheets/custom.css +0 -0
  125. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/assets/stylesheets/parallax.css +0 -0
  126. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/home.html +0 -0
  127. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/main.html +0 -0
  128. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/partials/parallax/hero.html +0 -0
  129. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/material/overrides/partials/parallax.html +0 -0
  130. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/mkdocs.yml +0 -0
  131. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/__init__.py +0 -0
  132. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/algorithms/__init__.py +0 -0
  133. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/algorithms/autotuning.py +0 -0
  134. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/algorithms/base.py +0 -0
  135. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/algorithms/optimization_results.py +0 -0
  136. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/algorithms/penalized_trust_region.py +0 -0
  137. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/config.py +0 -0
  138. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/discretization/__init__.py +0 -0
  139. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/discretization/discretization.py +0 -0
  140. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/expert/__init__.py +0 -0
  141. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/expert/byof.py +0 -0
  142. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/expert/lowering.py +0 -0
  143. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/expert/validation.py +0 -0
  144. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/integrators/__init__.py +0 -0
  145. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/integrators/runge_kutta.py +0 -0
  146. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/__init__.py +0 -0
  147. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/cvxpy_constraints.py +0 -0
  148. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/cvxpy_variables.py +0 -0
  149. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/dynamics.py +0 -0
  150. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/jax_constraints.py +0 -0
  151. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/parameters.py +0 -0
  152. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/problem.py +0 -0
  153. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/lowered/unified.py +0 -0
  154. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/__init__.py +0 -0
  155. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/plotting.py +0 -0
  156. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/scp_iteration.py +0 -0
  157. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/viser/__init__.py +0 -0
  158. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/viser/animated.py +0 -0
  159. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/viser/plotly_integration.py +0 -0
  160. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/viser/primitives.py +0 -0
  161. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/viser/scp.py +0 -0
  162. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/plotting/viser/server.py +0 -0
  163. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/propagation/__init__.py +0 -0
  164. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/propagation/post_processing.py +0 -0
  165. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/propagation/propagation.py +0 -0
  166. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/solvers/__init__.py +0 -0
  167. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/solvers/cvxpy.py +0 -0
  168. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/__init__.py +0 -0
  169. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/augmentation.py +0 -0
  170. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/constraint_set.py +0 -0
  171. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/__init__.py +0 -0
  172. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/arithmetic.py +0 -0
  173. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/array.py +0 -0
  174. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/constraint.py +0 -0
  175. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/control.py +0 -0
  176. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/expr.py +0 -0
  177. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  178. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  179. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/lie/se3.py +0 -0
  180. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/lie/so3.py +0 -0
  181. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/linalg.py +0 -0
  182. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/math.py +0 -0
  183. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/spatial.py +0 -0
  184. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/state.py +0 -0
  185. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/stl.py +0 -0
  186. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/variable.py +0 -0
  187. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/expr/vmap.py +0 -0
  188. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/hashing.py +0 -0
  189. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/lower.py +0 -0
  190. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/lowerers/__init__.py +0 -0
  191. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/lowerers/cvxpy.py +0 -0
  192. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/lowerers/jax.py +0 -0
  193. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/problem.py +0 -0
  194. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/time.py +0 -0
  195. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/symbolic/unified.py +0 -0
  196. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/utils/__init__.py +0 -0
  197. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/utils/cache.py +0 -0
  198. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/utils/printing.py +0 -0
  199. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/utils/profiling.py +0 -0
  200. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx/utils/utils.py +0 -0
  201. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx.egg-info/SOURCES.txt +0 -0
  202. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx.egg-info/dependency_links.txt +0 -0
  203. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx.egg-info/requires.txt +0 -0
  204. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/openscvx.egg-info/top_level.txt +0 -0
  205. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/pyproject.toml +0 -0
  206. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/scripts/gen_example_pages.py +0 -0
  207. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/scripts/gen_ref_pages.py +0 -0
  208. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/setup.cfg +0 -0
  209. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/__init__.py +0 -0
  210. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/brachistochrone_analytical.py +0 -0
  211. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/__init__.py +0 -0
  212. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/__init__.py +0 -0
  213. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_arithmetic.py +0 -0
  214. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_array.py +0 -0
  215. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_constraint.py +0 -0
  216. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_expr.py +0 -0
  217. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_lie.py +0 -0
  218. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_linalg.py +0 -0
  219. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_math.py +0 -0
  220. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_node_reference.py +0 -0
  221. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_parameters.py +0 -0
  222. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_scaling.py +0 -0
  223. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_spatial.py +0 -0
  224. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_variable.py +0 -0
  225. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/expr/test_vmap.py +0 -0
  226. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/test_augmentation.py +0 -0
  227. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/test_lower_cvxpy.py +0 -0
  228. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/test_lower_jax.py +0 -0
  229. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/symbolic/test_unified.py +0 -0
  230. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/test_cvxpygen_optional.py +0 -0
  231. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/test_discretization.py +0 -0
  232. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/test_examples.py +0 -0
  233. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/test_integrators.py +0 -0
  234. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/tests/test_plotting.py +0 -0
  235. {openscvx-0.3.2.dev215 → openscvx-0.3.2.dev228}/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.dev215
3
+ Version: 0.3.2.dev228
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
@@ -38,14 +38,12 @@ position.max = np.array([3000, 3000, 3000])
38
38
  position.min = np.array([-3000, -3000, 0])
39
39
  position.initial = np.array([2000, 0, 1500])
40
40
  position.final = np.array([0, 0, 0])
41
- position.guess = np.linspace(position.initial, position.final, n)
42
41
 
43
42
  velocity = ox.State("velocity", shape=(3,)) # 3D velocity [vx, vy, vz]
44
43
  velocity.max = np.array([v_max, v_max, v_max])
45
44
  velocity.min = np.array([-v_max, -v_max, -v_max])
46
45
  velocity.initial = np.array([80, 30, -75])
47
46
  velocity.final = np.array([0, 0, 0])
48
- velocity.guess = np.linspace(velocity.initial, velocity.final, n)
49
47
 
50
48
  mass = ox.State("mass", shape=(1,)) # Vehicle mass
51
49
  mass.max = np.array([1905])
@@ -54,7 +52,6 @@ mass.initial = np.array([1905])
54
52
  mass.final = [("maximize", 1690)]
55
53
  mass.scaling_min = np.array([1690])
56
54
  # mass.scaling_max = np.array([1700])
57
- mass.guess = np.linspace(mass.initial, 1690, n).reshape(-1, 1)
58
55
 
59
56
  # Define control
60
57
  thrust = ox.Control("thrust", shape=(3,)) # Thrust force vector [Tx, Ty, Tz]
@@ -38,14 +38,12 @@ position.max = np.array([10.0, 10.0])
38
38
  position.min = np.array([0.0, 0.0])
39
39
  position.initial = np.array([0.0, 10.0])
40
40
  position.final = [10.0, 5.0]
41
- position.guess = np.linspace(position.initial, position.final, n)
42
41
 
43
42
  velocity = ox.State("velocity", shape=(1,)) # Scalar speed
44
43
  velocity.max = np.array([10.0])
45
44
  velocity.min = np.array([0.0])
46
45
  velocity.initial = np.array([0.0])
47
46
  velocity.final = [("free", 10.0)]
48
- velocity.guess = np.linspace(0.0, 10.0, n).reshape(-1, 1)
49
47
 
50
48
  # Define control
51
49
  theta = ox.Control("theta", shape=(1,)) # Angle from vertical
@@ -35,14 +35,12 @@ position.min = np.array([-5.0, -5.0])
35
35
  position.max = np.array([5.0, 5.0])
36
36
  position.initial = np.array([0, -2])
37
37
  position.final = np.array([0, 2])
38
- position.guess = np.linspace(position.initial, position.final, n)
39
38
 
40
39
  theta = ox.State("theta", shape=(1,)) # Heading angle
41
40
  theta.min = np.array([-2 * jnp.pi])
42
41
  theta.max = np.array([2 * jnp.pi])
43
42
  theta.initial = np.array([0])
44
43
  theta.final = [ox.Free(0)]
45
- theta.guess = np.zeros((n, 1))
46
44
 
47
45
  # Define control components
48
46
  speed = ox.Control("speed", shape=(1,)) # Forward speed
@@ -41,7 +41,6 @@ theta.min = np.array([-2 * jnp.pi])
41
41
  theta.max = np.array([2 * jnp.pi])
42
42
  theta.initial = np.array([0])
43
43
  theta.final = [("free", 0)]
44
- theta.guess = np.zeros((n, 1))
45
44
 
46
45
  # Define control components
47
46
  speed = ox.Control("speed", shape=(1,)) # Forward speed
@@ -37,14 +37,12 @@ position.min = np.array([-5.0, -5.0])
37
37
  position.max = np.array([5.0, 5.0])
38
38
  position.initial = np.array([0, -2])
39
39
  position.final = np.array([0, 2])
40
- position.guess = np.linspace(position.initial, position.final, n)
41
40
 
42
41
  theta = ox.State("theta", shape=(1,)) # Heading angle
43
42
  theta.min = np.array([-2 * jnp.pi])
44
43
  theta.max = np.array([2 * jnp.pi])
45
44
  theta.initial = np.array([0])
46
45
  theta.final = [("free", 0)]
47
- theta.guess = np.zeros((n, 1))
48
46
 
49
47
  # Define control components
50
48
  speed = ox.Control("speed", shape=(1,)) # Forward speed
@@ -63,7 +61,6 @@ time.max = np.array([10])
63
61
  time.min = np.array([0.0])
64
62
  time.initial = np.array([0.0])
65
63
  time.final = [ox.Minimize(total_time)]
66
- time.guess = np.linspace(0.0, total_time, n).reshape(-1, 1)
67
64
 
68
65
 
69
66
  # Define list of all states and controls
@@ -70,7 +70,6 @@ time.max = np.array([total_time])
70
70
  time.min = np.array([0.0])
71
71
  time.initial = np.array([0.0])
72
72
  time.final = np.array([total_time])
73
- time.guess = np.linspace(0.0, total_time, n).reshape(-1, 1)
74
73
 
75
74
  # Define control components
76
75
  thrust_force = ox.Control("thrust_force", shape=(3,)) # Thrust forces [fx, fy, fz]
@@ -38,14 +38,12 @@ position.max = np.array([200.0, 100, 50])
38
38
  position.min = np.array([-200.0, -100, 15])
39
39
  position.initial = np.array([10.0, 0, 20])
40
40
  position.final = [10.0, 0, 20]
41
- position.guess = np.linspace(position.initial, position.final, n)
42
41
 
43
42
  velocity = ox.State("velocity", shape=(3,)) # 3D velocity [vx, vy, vz]
44
43
  velocity.max = np.array([100, 100, 100])
45
44
  velocity.min = np.array([-100, -100, -100])
46
45
  velocity.initial = np.array([0, 0, 0])
47
46
  velocity.final = [("free", 0), ("free", 0), ("free", 0)]
48
- velocity.guess = np.linspace(velocity.initial, [0, 0, 0], n)
49
47
 
50
48
  # Define control
51
49
  force = ox.Control("force", shape=(3,)) # Control forces [fx, fy, fz]
@@ -37,28 +37,24 @@ position.max = np.array([200.0, 100, 200])
37
37
  position.min = np.array([-200.0, -100, 15])
38
38
  position.initial = np.array([10.0, 0, 20])
39
39
  position.final = [10.0, 0, 20]
40
- position.guess = np.linspace(position.initial, position.final, n)
41
40
 
42
41
  velocity = ox.State("velocity", shape=(3,)) # 3D velocity [vx, vy, vz]
43
42
  velocity.max = np.array([100, 100, 100])
44
43
  velocity.min = np.array([-100, -100, -100])
45
44
  velocity.initial = np.array([0, 0, 0])
46
45
  velocity.final = [("free", 0), ("free", 0), ("free", 0)]
47
- velocity.guess = np.linspace(velocity.initial, [0, 0, 0], n)
48
46
 
49
47
  attitude = ox.State("attitude", shape=(4,)) # Quaternion [qw, qx, qy, qz]
50
48
  attitude.max = np.array([1, 1, 1, 1])
51
49
  attitude.min = np.array([-1, -1, -1, -1])
52
50
  attitude.initial = [("free", 1.0), ("free", 0), ("free", 0), ("free", 0)]
53
51
  attitude.final = [("free", 1), ("free", 0), ("free", 0), ("free", 0)]
54
- attitude.guess = np.tile([1.0, 0.0, 0.0, 0.0], (n, 1))
55
52
 
56
53
  angular_velocity = ox.State("angular_velocity", shape=(3,)) # Angular velocity [wx, wy, wz]
57
54
  angular_velocity.max = np.array([10, 10, 10])
58
55
  angular_velocity.min = np.array([-10, -10, -10])
59
56
  angular_velocity.initial = [("free", 0), ("free", 0), ("free", 0)]
60
57
  angular_velocity.final = [("free", 0), ("free", 0), ("free", 0)]
61
- angular_velocity.guess = np.zeros((n, 3))
62
58
 
63
59
  # Define control components
64
60
  thrust_force = ox.Control("thrust_force", shape=(3,)) # Thrust forces [fx, fy, fz]
@@ -137,12 +137,6 @@ for center, A in zip(obstacle_centers, A_obs):
137
137
  obstacle_constraint = ox.ctcs(1.0 <= diff.T @ A_const @ diff)
138
138
  constraints.append(obstacle_constraint)
139
139
 
140
- # Set initial guesses
141
- position.guess = np.linspace(position.initial, position.final, n)
142
- velocity.guess = np.linspace(velocity.initial, [0, 0, 0], n)
143
- attitude.guess = np.tile([1.0, 0.0, 0.0, 0.0], (n, 1))
144
- angular_velocity.guess = np.zeros((n, 3))
145
-
146
140
 
147
141
  time = ox.Time(
148
142
  initial=0.0,
@@ -136,12 +136,6 @@ for center, A in zip(obstacle_centers, A_obs):
136
136
  obstacle_constraint = 1.0 <= diff.T @ A_const @ diff
137
137
  constraints.append(obstacle_constraint)
138
138
 
139
- # Set initial guesses
140
- position.guess = np.linspace(position.initial, position.final, n)
141
- velocity.guess = np.linspace(velocity.initial, [0, 0, 0], n)
142
- attitude.guess = np.tile([1.0, 0.0, 0.0, 0.0], (n, 1))
143
- angular_velocity.guess = np.zeros((n, 3))
144
-
145
139
  time = ox.Time(
146
140
  initial=0.0,
147
141
  final=("minimize", total_time),
@@ -137,13 +137,6 @@ for center, A in zip(obstacle_centers, A_obs):
137
137
  obstacle_constraint = ox.ctcs(1.0 <= diff.T @ A_const @ diff)
138
138
  constraints.append(obstacle_constraint)
139
139
 
140
- # Set initial guesses
141
- position.guess = np.linspace(position.initial, position.final, n)
142
- velocity.guess = np.linspace(velocity.initial, [0, 0, 0], n)
143
- attitude.guess = np.tile([1.0, 0.0, 0.0, 0.0], (n, 1))
144
- angular_velocity.guess = np.zeros((n, 3))
145
-
146
-
147
140
  time = ox.Time(
148
141
  initial=0.0,
149
142
  final=("minimize", total_time),
@@ -54,7 +54,6 @@ position.max = np.array([100.0, 100.0, 100.0])
54
54
  position.min = np.array([-100.0, -100.0, -100.0])
55
55
  position.initial = np.array([0.0, -100.0, 0.0]) # Start 100m behind target (V-bar position)
56
56
  position.final = np.array([0.0, 0.0, 0.0]) # Dock at origin
57
- position.guess = np.linspace(position.initial, position.final, n_nodes)
58
57
 
59
58
  # Velocity in CW frame [vx, vy, vz] in m/s
60
59
  velocity = ox.State("velocity", shape=(3,))
@@ -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.dev215'
32
- __version_tuple__ = version_tuple = (0, 3, 2, 'dev215')
31
+ __version__ = version = '0.3.2.dev228'
32
+ __version_tuple__ = version_tuple = (0, 3, 2, 'dev228')
33
33
 
34
- __commit_id__ = commit_id = 'g708268a3b'
34
+ __commit_id__ = commit_id = 'g44572a46d'
@@ -164,6 +164,9 @@ class Problem:
164
164
 
165
165
  validate_byof(byof, self.symbolic.states, n_x, n_u, N)
166
166
 
167
+ # Store byof for cache hashing
168
+ self._byof = byof
169
+
167
170
  # Lower to JAX and CVXPy (byof handling happens inside lower_symbolic_problem)
168
171
  self._lowered: LoweredProblem = lower_symbolic_problem(self.symbolic, byof=byof)
169
172
 
@@ -458,6 +461,7 @@ class Problem:
458
461
  self.symbolic,
459
462
  dt=self.settings.prp.dt,
460
463
  total_time=self.settings.sim.total_time,
464
+ byof=self._byof,
461
465
  )
462
466
 
463
467
  # Compile the discretization solver
@@ -47,11 +47,15 @@ from openscvx.symbolic.expr.state import State
47
47
  from openscvx.symbolic.preprocessing import (
48
48
  collect_and_assign_slices,
49
49
  convert_dynamics_dict_to_expr,
50
+ fill_default_guesses,
50
51
  validate_and_normalize_constraint_nodes,
52
+ validate_boundary_conditions,
53
+ validate_bounds,
51
54
  validate_constraints_at_root,
52
55
  validate_dynamics_dict,
53
56
  validate_dynamics_dict_dimensions,
54
57
  validate_dynamics_dimension,
58
+ validate_guesses,
55
59
  validate_shapes,
56
60
  validate_time_parameters,
57
61
  validate_variable_names,
@@ -185,6 +189,17 @@ def preprocess_symbolic_problem(
185
189
  print([s.name for s in problem.states_prop])
186
190
  """
187
191
 
192
+ # Validate user-provided variables have required attributes
193
+ validate_boundary_conditions(states)
194
+ validate_bounds(states + controls)
195
+
196
+ # Fill in default guesses for user-provided states
197
+ # (augmented states get their guesses set by augmentation code)
198
+ fill_default_guesses(states, N)
199
+
200
+ # Validate that all user-provided variables have guesses
201
+ validate_guesses(states + controls)
202
+
188
203
  # ==================== PHASE 1: Time Handling & Validation ====================
189
204
 
190
205
  # Validate time handling approach and get processed parameters
@@ -54,6 +54,7 @@ from openscvx.symbolic.expr import (
54
54
  Expr,
55
55
  NodalConstraint,
56
56
  State,
57
+ Variable,
57
58
  traverse,
58
59
  )
59
60
 
@@ -755,3 +756,84 @@ def convert_dynamics_dict_to_expr(
755
756
  dynamics_concat = Concat(*dynamics_exprs)
756
757
 
757
758
  return dynamics_converted, dynamics_concat
759
+
760
+
761
+ def fill_default_guesses(states: List[State], N: int) -> None:
762
+ """Fill in default linspace guesses for states with guess=None.
763
+
764
+ For states with both initial and final conditions set, generates a linear
765
+ interpolation from initial to final values.
766
+
767
+ This function modifies states in-place.
768
+
769
+ Args:
770
+ states: List of State objects to fill guesses for
771
+ N: Number of discretization nodes
772
+ """
773
+ for state in states:
774
+ if state.guess is None and state.initial is not None and state.final is not None:
775
+ # state.initial and state.final are already numpy arrays of values
776
+ # (the setter handles parsing tuples like ("free", value))
777
+ state.guess = np.linspace(state.initial, state.final, N)
778
+
779
+
780
+ def validate_boundary_conditions(states: List[State]) -> None:
781
+ """Validate that all states have initial and final boundary conditions set.
782
+
783
+ Args:
784
+ states: List of State objects to validate
785
+
786
+ Raises:
787
+ ValueError: If any state is missing initial or final conditions
788
+ """
789
+ for state in states:
790
+ if state.initial is None:
791
+ raise ValueError(
792
+ f"State '{state.name}' is missing initial condition. "
793
+ f"Please set {state.name}.initial"
794
+ )
795
+ if state.final is None:
796
+ raise ValueError(
797
+ f"State '{state.name}' is missing final condition. Please set {state.name}.final"
798
+ )
799
+
800
+
801
+ def validate_bounds(variables: List[Variable]) -> None:
802
+ """Validate that all variables have min and max bounds set.
803
+
804
+ Args:
805
+ variables: List of Variable objects (State or Control) to validate
806
+
807
+ Raises:
808
+ ValueError: If any variable is missing min or max bounds
809
+ """
810
+ for var in variables:
811
+ if var.min is None:
812
+ raise ValueError(
813
+ f"Variable '{var.name}' is missing min bound. Please set {var.name}.min"
814
+ )
815
+ if var.max is None:
816
+ raise ValueError(
817
+ f"Variable '{var.name}' is missing max bound. Please set {var.name}.max"
818
+ )
819
+
820
+
821
+ def validate_guesses(variables: List[Variable]) -> None:
822
+ """Validate that all variables have initial guesses set.
823
+
824
+ Args:
825
+ variables: List of Variable objects (State or Control) to validate
826
+
827
+ Raises:
828
+ ValueError: If any variable is missing a guess
829
+ """
830
+ for var in variables:
831
+ if var.guess is None:
832
+ if isinstance(var, Control):
833
+ raise ValueError(
834
+ f"Control '{var.name}' is missing initial guess. "
835
+ f"Please set {var.name}.guess (controls require explicit guesses)"
836
+ )
837
+ raise ValueError(
838
+ f"State '{var.name}' is missing initial guess. Please set {var.name}.guess"
839
+ )
@@ -9,14 +9,45 @@ from jax import export
9
9
  from openscvx.utils.cache import get_cache_dir
10
10
 
11
11
  if TYPE_CHECKING:
12
+ from openscvx.expert.byof import ByofSpec
12
13
  from openscvx.symbolic.problem import SymbolicProblem
13
14
 
14
15
 
16
+ def _hash_byof(byof: Optional["ByofSpec"]) -> bytes:
17
+ """Hash BYOF functions by their bytecode and constants.
18
+
19
+ Args:
20
+ byof: Optional ByofSpec containing raw JAX functions
21
+
22
+ Returns:
23
+ Concatenated bytecode and constants of all functions, or empty bytes if no byof
24
+ """
25
+ if not byof:
26
+ return b""
27
+
28
+ codes = []
29
+ for f in byof.get("dynamics", {}).values():
30
+ codes.append(f.__code__.co_code)
31
+ codes.append(repr(f.__code__.co_consts).encode())
32
+ for c in byof.get("nodal_constraints", []):
33
+ codes.append(c["constraint_fn"].__code__.co_code)
34
+ codes.append(repr(c["constraint_fn"].__code__.co_consts).encode())
35
+ for f in byof.get("cross_nodal_constraints", []):
36
+ codes.append(f.__code__.co_code)
37
+ codes.append(repr(f.__code__.co_consts).encode())
38
+ for c in byof.get("ctcs_constraints", []):
39
+ codes.append(c["constraint_fn"].__code__.co_code)
40
+ codes.append(repr(c["constraint_fn"].__code__.co_consts).encode())
41
+
42
+ return b"".join(codes)
43
+
44
+
15
45
  def get_solver_cache_paths(
16
46
  symbolic_problem: "SymbolicProblem",
17
47
  dt: float,
18
48
  total_time: float,
19
49
  cache_dir: Optional[Path] = None,
50
+ byof: Optional["ByofSpec"] = None,
20
51
  ) -> Tuple[Path, Path]:
21
52
  """Generate cache file paths using symbolic AST hashing.
22
53
 
@@ -30,6 +61,8 @@ def get_solver_cache_paths(
30
61
  total_time: Total simulation time
31
62
  cache_dir: Directory to store cached solvers. If None, uses the default
32
63
  cache directory (see :func:`openscvx.get_cache_dir`).
64
+ byof: Optional ByofSpec containing raw JAX functions. If provided,
65
+ function bytecode is included in the hash.
33
66
 
34
67
  Returns:
35
68
  Tuple of (discretization_solver_path, propagation_solver_path)
@@ -44,6 +77,10 @@ def get_solver_cache_paths(
44
77
  final_hasher.update(problem_hash.encode())
45
78
  final_hasher.update(f"dt:{dt}".encode())
46
79
  final_hasher.update(f"total_time:{total_time}".encode())
80
+
81
+ # Include BYOF function bytecode in the hash
82
+ final_hasher.update(_hash_byof(byof))
83
+
47
84
  final_hash = final_hasher.hexdigest()[:32]
48
85
 
49
86
  solver_dir = cache_dir if cache_dir is not None else get_cache_dir()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.3.2.dev215
3
+ Version: 0.3.2.dev228
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
@@ -1047,3 +1047,42 @@ def test_duplicate_constraints_same_hash():
1047
1047
  # Different number of duplicates should hash differently
1048
1048
  p3 = make_problem(2)
1049
1049
  assert hash_symbolic_problem(p1) != hash_symbolic_problem(p3)
1050
+
1051
+
1052
+ # =============================================================================
1053
+ # BYOF Hashing Tests
1054
+ # =============================================================================
1055
+
1056
+
1057
+ def test_byof_hash_none():
1058
+ """None byof should return empty bytes."""
1059
+ from openscvx.utils.caching import _hash_byof
1060
+
1061
+ assert _hash_byof(None) == b""
1062
+
1063
+
1064
+ def test_byof_hash_empty():
1065
+ """Empty byof dict should return empty bytes."""
1066
+ from openscvx.utils.caching import _hash_byof
1067
+
1068
+ assert _hash_byof({}) == b""
1069
+
1070
+
1071
+ def test_byof_hash_changes_with_function():
1072
+ """Different lambda implementations should produce different hashes."""
1073
+ from openscvx.utils.caching import _hash_byof
1074
+
1075
+ byof1 = {"nodal_constraints": [{"constraint_fn": lambda x, u, n, p: x[0] - 10.0}]}
1076
+ byof2 = {"nodal_constraints": [{"constraint_fn": lambda x, u, n, p: x[0] - 20.0}]}
1077
+
1078
+ assert _hash_byof(byof1) != _hash_byof(byof2)
1079
+
1080
+
1081
+ def test_byof_hash_same_function_same_hash():
1082
+ """Identical lambda implementations should produce same hash."""
1083
+ from openscvx.utils.caching import _hash_byof
1084
+
1085
+ byof1 = {"nodal_constraints": [{"constraint_fn": lambda x, u, n, p: x[0] - 10.0}]}
1086
+ byof2 = {"nodal_constraints": [{"constraint_fn": lambda x, u, n, p: x[0] - 10.0}]}
1087
+
1088
+ assert _hash_byof(byof1) == _hash_byof(byof2)
@@ -6,11 +6,15 @@ from openscvx.symbolic.expr import Add, Concat, Constant, Control, CrossNodeCons
6
6
  from openscvx.symbolic.preprocessing import (
7
7
  collect_and_assign_slices,
8
8
  convert_dynamics_dict_to_expr,
9
+ fill_default_guesses,
10
+ validate_boundary_conditions,
11
+ validate_bounds,
9
12
  validate_constraints_at_root,
10
13
  validate_cross_node_constraint,
11
14
  validate_dynamics_dict,
12
15
  validate_dynamics_dict_dimensions,
13
16
  validate_dynamics_dimension,
17
+ validate_guesses,
14
18
  validate_variable_names,
15
19
  )
16
20
 
@@ -801,3 +805,174 @@ def test_cross_node_constraint_both_sides():
801
805
  constraint2 = CrossNodeConstraint(velocity <= position.at(5))
802
806
  with pytest.raises(ValueError, match="without .at\\(\\).*vel"):
803
807
  validate_cross_node_constraint(constraint2, N)
808
+
809
+
810
+ # =============================================================================
811
+ # fill_default_guesses Tests
812
+ # =============================================================================
813
+
814
+
815
+ def test_fill_default_guesses_state_linspace():
816
+ """Test that state guesses are filled with linspace from initial to final."""
817
+ N = 11 # Use 11 so middle index (5) is exactly at midpoint
818
+ x = State("x", shape=(3,))
819
+ x.initial = np.array([0.0, 1.0, 2.0])
820
+ x.final = np.array([10.0, 11.0, 12.0])
821
+
822
+ fill_default_guesses([x], N)
823
+
824
+ assert x.guess is not None
825
+ assert x.guess.shape == (N, 3)
826
+ # Check first and last rows match initial and final
827
+ np.testing.assert_array_almost_equal(x.guess[0], [0.0, 1.0, 2.0])
828
+ np.testing.assert_array_almost_equal(x.guess[-1], [10.0, 11.0, 12.0])
829
+ # Check it's actually a linspace (middle index should be exactly at midpoint)
830
+ np.testing.assert_array_almost_equal(x.guess[N // 2], [5.0, 6.0, 7.0])
831
+
832
+
833
+ def test_fill_default_guesses_preserves_existing():
834
+ """Test that existing guesses are not overwritten."""
835
+ N = 10
836
+ x = State("x", shape=(2,))
837
+ x.initial = np.array([0.0, 0.0])
838
+ x.final = np.array([10.0, 10.0])
839
+ custom_guess = np.ones((N, 2)) * 99.0
840
+ x.guess = custom_guess
841
+
842
+ fill_default_guesses([x], N)
843
+
844
+ # Guess should be unchanged
845
+ np.testing.assert_array_equal(x.guess, custom_guess)
846
+
847
+
848
+ def test_fill_default_guesses_with_free_boundary_conditions():
849
+ """Test that states with free boundary conditions use the guess values."""
850
+ N = 10
851
+ x = State("x", shape=(3,))
852
+ # Mixed: first fixed, second free, third fixed
853
+ x.initial = [0.0, ("free", 5.0), 2.0]
854
+ x.final = [10.0, ("free", 15.0), 12.0]
855
+
856
+ fill_default_guesses([x], N)
857
+
858
+ assert x.guess is not None
859
+ assert x.guess.shape == (N, 3)
860
+ # The setter extracts values from tuples, so initial=[0, 5, 2], final=[10, 15, 12]
861
+ np.testing.assert_array_almost_equal(x.guess[0], [0.0, 5.0, 2.0])
862
+ np.testing.assert_array_almost_equal(x.guess[-1], [10.0, 15.0, 12.0])
863
+
864
+
865
+ def test_fill_default_guesses_multiple_states():
866
+ """Test filling guesses for multiple states at once."""
867
+ N = 5
868
+ x1 = State("x1", shape=(2,))
869
+ x1.initial = np.array([0.0, 0.0])
870
+ x1.final = np.array([4.0, 8.0])
871
+
872
+ x2 = State("x2", shape=(1,))
873
+ x2.initial = np.array([10.0])
874
+ x2.final = np.array([20.0])
875
+
876
+ fill_default_guesses([x1, x2], N)
877
+
878
+ # Check x1
879
+ assert x1.guess.shape == (N, 2)
880
+ np.testing.assert_array_almost_equal(x1.guess[0], [0.0, 0.0])
881
+ np.testing.assert_array_almost_equal(x1.guess[-1], [4.0, 8.0])
882
+
883
+ # Check x2
884
+ assert x2.guess.shape == (N, 1)
885
+ np.testing.assert_array_almost_equal(x2.guess[0], [10.0])
886
+ np.testing.assert_array_almost_equal(x2.guess[-1], [20.0])
887
+
888
+
889
+ # =============================================================================
890
+ # validate_boundary_conditions Tests
891
+ # =============================================================================
892
+
893
+
894
+ def test_validate_boundary_conditions_passes():
895
+ """Test that validation passes when all states have initial and final."""
896
+ x = State("position", shape=(3,))
897
+ x.initial = np.zeros(3)
898
+ x.final = np.ones(3)
899
+
900
+ validate_boundary_conditions([x])
901
+ validate_boundary_conditions([])
902
+
903
+
904
+ def test_validate_boundary_conditions_raises_missing():
905
+ """Test that validation fails fast on first missing attribute."""
906
+ x = State("position", shape=(2,))
907
+ # No initial or final set
908
+
909
+ with pytest.raises(ValueError, match="'position' is missing initial"):
910
+ validate_boundary_conditions([x])
911
+
912
+ # With initial but no final
913
+ x.initial = np.zeros(2)
914
+ with pytest.raises(ValueError, match="'position' is missing final"):
915
+ validate_boundary_conditions([x])
916
+
917
+
918
+ # =============================================================================
919
+ # validate_bounds Tests
920
+ # =============================================================================
921
+
922
+
923
+ def test_validate_bounds_passes():
924
+ """Test that validation passes when all variables have min and max."""
925
+ x = State("position", shape=(3,))
926
+ x.min = np.array([-10, -10, -10])
927
+ x.max = np.array([10, 10, 10])
928
+
929
+ u = Control("thrust", shape=(2,))
930
+ u.min = np.zeros(2)
931
+ u.max = np.array([100, 100])
932
+
933
+ validate_bounds([x, u])
934
+ validate_bounds([])
935
+
936
+
937
+ def test_validate_bounds_raises_missing():
938
+ """Test that validation fails fast on first missing attribute."""
939
+ u = Control("thrust", shape=(2,))
940
+ # No min or max set
941
+
942
+ with pytest.raises(ValueError, match="'thrust' is missing min"):
943
+ validate_bounds([u])
944
+
945
+ # With min but no max
946
+ u.min = np.zeros(2)
947
+ with pytest.raises(ValueError, match="'thrust' is missing max"):
948
+ validate_bounds([u])
949
+
950
+
951
+ # =============================================================================
952
+ # validate_guesses Tests
953
+ # =============================================================================
954
+
955
+
956
+ def test_validate_guesses_passes():
957
+ """Test that validation passes when all variables have guesses."""
958
+ N = 10
959
+ x = State("position", shape=(3,))
960
+ x.guess = np.zeros((N, 3))
961
+
962
+ u = Control("thrust", shape=(2,))
963
+ u.guess = np.ones((N, 2))
964
+
965
+ validate_guesses([x, u])
966
+ validate_guesses([])
967
+
968
+
969
+ def test_validate_guesses_raises_missing():
970
+ """Test that validation fails fast on first missing guess."""
971
+ u = Control("thrust", shape=(2,))
972
+ # No guess set
973
+
974
+ with pytest.raises(
975
+ ValueError,
976
+ match="Control 'thrust' is missing initial guess.*controls require explicit guesses",
977
+ ):
978
+ validate_guesses([u])