openscvx 0.3.2.dev315__tar.gz → 0.3.2.dev317__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 (251) hide show
  1. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/_docs.yml +1 -1
  2. {openscvx-0.3.2.dev315/openscvx.egg-info → openscvx-0.3.2.dev317}/PKG-INFO +1 -1
  3. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/UnderTheHood/vectorization_and_vmapping.md +2 -5
  4. openscvx-0.3.2.dev317/docs/UsersGuide/00_introduction.md +44 -0
  5. openscvx-0.3.2.dev317/docs/UsersGuide/01_hello_world_brachistochrone.md +320 -0
  6. openscvx-0.3.2.dev317/docs/UsersGuide/02_drone_racing_constraints.md +289 -0
  7. openscvx-0.3.2.dev317/docs/UsersGuide/03_obstacle_avoidance_vmap.md +424 -0
  8. openscvx-0.3.2.dev317/docs/UsersGuide/04_viewpoint_constraints.md +356 -0
  9. openscvx-0.3.2.dev317/docs/UsersGuide/05_visualization.md +447 -0
  10. openscvx-0.3.2.dev317/docs/UsersGuide/06_logic.md +12 -0
  11. openscvx-0.3.2.dev317/docs/UsersGuide/07_lie.md +9 -0
  12. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/getting-started.md +9 -5
  13. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/mkdocs.yml +17 -23
  14. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/_version.py +3 -3
  15. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/algorithms/optimization_results.py +22 -25
  16. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/config.py +0 -8
  17. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/discretization/discretization.py +16 -15
  18. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/integrators/runge_kutta.py +9 -9
  19. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/unified.py +2 -2
  20. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/plotting.py +2 -2
  21. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/scp_iteration.py +1 -1
  22. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/problem.py +18 -16
  23. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/propagation/propagation.py +19 -9
  24. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/solvers/base.py +2 -2
  25. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/arithmetic.py +31 -15
  26. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/array.py +11 -11
  27. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/constraint.py +13 -13
  28. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/control.py +6 -4
  29. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/expr.py +23 -15
  30. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/lie/adjoint.py +19 -9
  31. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/lie/se3.py +6 -5
  32. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/lie/so3.py +6 -5
  33. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/linalg.py +15 -11
  34. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/logic.py +5 -5
  35. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/math.py +43 -31
  36. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/spatial.py +9 -7
  37. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/state.py +13 -12
  38. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/stl.py +10 -7
  39. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/variable.py +15 -7
  40. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/vmap.py +3 -3
  41. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/lower.py +2 -2
  42. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/lowerers/cvxpy.py +2 -2
  43. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/lowerers/jax.py +5 -5
  44. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/preprocessing.py +3 -1
  45. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/utils/caching.py +1 -1
  46. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/utils/printing.py +2 -1
  47. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317/openscvx.egg-info}/PKG-INFO +1 -1
  48. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx.egg-info/SOURCES.txt +14 -18
  49. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/scripts/gen_example_pages.py +2 -2
  50. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/scripts/gen_ref_pages.py +2 -2
  51. openscvx-0.3.2.dev315/docs/Usage/advanced_problem_setup.md +0 -249
  52. openscvx-0.3.2.dev315/docs/Usage/api.md +0 -121
  53. openscvx-0.3.2.dev315/docs/Usage/api_constraints.md +0 -41
  54. openscvx-0.3.2.dev315/docs/Usage/api_control.md +0 -10
  55. openscvx-0.3.2.dev315/docs/Usage/api_integrators.md +0 -25
  56. openscvx-0.3.2.dev315/docs/Usage/api_state.md +0 -26
  57. openscvx-0.3.2.dev315/docs/Usage/api_trajoptproblem.md +0 -48
  58. openscvx-0.3.2.dev315/docs/Usage/api_variable.md +0 -6
  59. openscvx-0.3.2.dev315/docs/Usage/basic_problem_setup.md +0 -273
  60. openscvx-0.3.2.dev315/docs/Usage/tutorial_6dof_los_guidance.md +0 -436
  61. openscvx-0.3.2.dev315/docs/Usage/tutorial_6dof_obstacle_avoidance.md +0 -294
  62. openscvx-0.3.2.dev315/docs/Usage/tutorials.md +0 -46
  63. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/assets/logo.svg +0 -0
  64. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/release-drafter.yml +0 -0
  65. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/branch-name.yml +0 -0
  66. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/docs.yml +0 -0
  67. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/lint.yml +0 -0
  68. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/nightly.yml +0 -0
  69. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/release-drafter.yml +0 -0
  70. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/release.yml +0 -0
  71. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/tests-integration.yml +0 -0
  72. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.github/workflows/tests-unit.yml +0 -0
  73. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/.gitignore +0 -0
  74. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/CONTRIBUTING.md +0 -0
  75. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/LICENSE +0 -0
  76. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/README.md +0 -0
  77. {openscvx-0.3.2.dev315/docs/Overview → openscvx-0.3.2.dev317/docs/Foundations}/constraint_reformulation.md +0 -0
  78. {openscvx-0.3.2.dev315/docs/Overview → openscvx-0.3.2.dev317/docs/Foundations}/control_parameterization.md +0 -0
  79. {openscvx-0.3.2.dev315/docs/Overview → openscvx-0.3.2.dev317/docs/Foundations}/discretization.md +0 -0
  80. {openscvx-0.3.2.dev315/docs/Overview → openscvx-0.3.2.dev317/docs/Foundations}/ocp.md +0 -0
  81. {openscvx-0.3.2.dev315/docs/Overview → openscvx-0.3.2.dev317/docs/Foundations}/scvx.md +0 -0
  82. {openscvx-0.3.2.dev315/docs/Overview → openscvx-0.3.2.dev317/docs/Foundations}/time_dilation.md +0 -0
  83. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/UnderTheHood/lowering_architecture.md +0 -0
  84. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/favicon.png +0 -0
  85. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/images/ct-scvx_dark.png +0 -0
  86. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/images/ct-scvx_light.png +0 -0
  87. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/images/ctcs_dark.png +0 -0
  88. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/images/ctcs_light.png +0 -0
  89. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/images/problem_class_dark.png +0 -0
  90. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/images/problem_class_light.png +0 -0
  91. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/assets/logo.svg +0 -0
  92. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/citation.md +0 -0
  93. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/examples.md +0 -0
  94. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/index.md +0 -0
  95. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/docs/javascripts/mathjax.js +0 -0
  96. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/abstract/brachistochrone.py +0 -0
  97. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/arm/three_link_arm.py +0 -0
  98. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/car/dubins_car.py +0 -0
  99. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/car/dubins_car_conditional.py +0 -0
  100. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/car/dubins_car_disjoint.py +0 -0
  101. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/car/dubins_car_stljax.py +0 -0
  102. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/cinema_vp.py +0 -0
  103. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/cinema_vp_realtime_base.py +0 -0
  104. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/dr_double_integrator.py +0 -0
  105. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/dr_vp.py +0 -0
  106. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/dr_vp_nodal.py +0 -0
  107. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/dr_vp_polytope.py +0 -0
  108. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/drone_racing.py +0 -0
  109. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/obstacle_avoidance.py +0 -0
  110. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  111. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/obstacle_avoidance_realtime_base.py +0 -0
  112. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  113. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/plotting.py +0 -0
  114. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/plotting_viser.py +0 -0
  115. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/realtime/cinema_vp_realtime.py +0 -0
  116. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/realtime/drone_racing_realtime.py +0 -0
  117. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/realtime/dubins_car_realtime.py +0 -0
  118. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  119. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/rocket/3DoF_pdg.py +0 -0
  120. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/examples/spacecraft/proxops_cw.py +0 -0
  121. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/ctlos_cine.gif +0 -0
  122. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/ctlos_dr.gif +0 -0
  123. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/dtlos_cine.gif +0 -0
  124. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/dtlos_dr.gif +0 -0
  125. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/openscvx_logo.svg +0 -0
  126. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/openscvx_logo_square.png +0 -0
  127. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/oscvx_structure_full_dark.svg +0 -0
  128. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/figures/video_preview.png +0 -0
  129. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/__init__.py +0 -0
  130. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/1-background.avif +0 -0
  131. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
  132. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
  133. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
  134. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
  135. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/2-mars.avif +0 -0
  136. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
  137. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
  138. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
  139. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
  140. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/3-moon.avif +0 -0
  141. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
  142. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
  143. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
  144. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
  145. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
  146. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
  147. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
  148. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
  149. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
  150. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/5-space.avif +0 -0
  151. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
  152. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
  153. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
  154. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
  155. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/6-earth.avif +0 -0
  156. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
  157. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
  158. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
  159. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
  160. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/javascripts/parallax.js +0 -0
  161. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/logo.svg +0 -0
  162. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/stylesheets/custom.css +0 -0
  163. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/assets/stylesheets/parallax.css +0 -0
  164. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/home.html +0 -0
  165. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/main.html +0 -0
  166. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/partials/parallax/hero.html +0 -0
  167. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/material/overrides/partials/parallax.html +0 -0
  168. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/__init__.py +0 -0
  169. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/algorithms/__init__.py +0 -0
  170. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/algorithms/autotuning.py +0 -0
  171. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/algorithms/base.py +0 -0
  172. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/algorithms/penalized_trust_region.py +0 -0
  173. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/discretization/__init__.py +0 -0
  174. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/expert/__init__.py +0 -0
  175. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/expert/byof.py +0 -0
  176. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/expert/lowering.py +0 -0
  177. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/expert/validation.py +0 -0
  178. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/init/__init__.py +0 -0
  179. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/init/interpolation.py +0 -0
  180. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/integrators/__init__.py +0 -0
  181. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/__init__.py +0 -0
  182. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/cvxpy_constraints.py +0 -0
  183. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/cvxpy_variables.py +0 -0
  184. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/dynamics.py +0 -0
  185. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/jax_constraints.py +0 -0
  186. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/parameters.py +0 -0
  187. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/lowered/problem.py +0 -0
  188. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/__init__.py +0 -0
  189. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/viser/__init__.py +0 -0
  190. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/viser/animated.py +0 -0
  191. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/viser/plotly_integration.py +0 -0
  192. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/viser/primitives.py +0 -0
  193. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/viser/scp.py +0 -0
  194. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/plotting/viser/server.py +0 -0
  195. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/propagation/__init__.py +0 -0
  196. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/propagation/post_processing.py +0 -0
  197. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/solvers/__init__.py +0 -0
  198. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/solvers/ptr_solver.py +0 -0
  199. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/__init__.py +0 -0
  200. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/augmentation.py +0 -0
  201. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/builder.py +0 -0
  202. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/constraint_set.py +0 -0
  203. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/__init__.py +0 -0
  204. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  205. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/hashing.py +0 -0
  206. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/lowerers/__init__.py +0 -0
  207. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/problem.py +0 -0
  208. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/time.py +0 -0
  209. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/symbolic/unified.py +0 -0
  210. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/utils/__init__.py +0 -0
  211. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/utils/cache.py +0 -0
  212. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/utils/profiling.py +0 -0
  213. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx/utils/utils.py +0 -0
  214. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx.egg-info/dependency_links.txt +0 -0
  215. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx.egg-info/requires.txt +0 -0
  216. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/openscvx.egg-info/top_level.txt +0 -0
  217. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/pyproject.toml +0 -0
  218. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/setup.cfg +0 -0
  219. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/__init__.py +0 -0
  220. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/brachistochrone_analytical.py +0 -0
  221. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/__init__.py +0 -0
  222. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/__init__.py +0 -0
  223. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_arithmetic.py +0 -0
  224. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_array.py +0 -0
  225. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_constraint.py +0 -0
  226. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_expr.py +0 -0
  227. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_lie.py +0 -0
  228. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_linalg.py +0 -0
  229. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_logic.py +0 -0
  230. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_math.py +0 -0
  231. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_node_reference.py +0 -0
  232. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_parameters.py +0 -0
  233. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_scaling.py +0 -0
  234. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_spatial.py +0 -0
  235. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_variable.py +0 -0
  236. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/expr/test_vmap.py +0 -0
  237. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/test_augmentation.py +0 -0
  238. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/test_hashing.py +0 -0
  239. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/test_lower_cvxpy.py +0 -0
  240. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/test_lower_jax.py +0 -0
  241. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/test_preprocessing.py +0 -0
  242. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/symbolic/test_unified.py +0 -0
  243. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_brachistochrone.py +0 -0
  244. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_cvxpygen_optional.py +0 -0
  245. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_discretization.py +0 -0
  246. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_examples.py +0 -0
  247. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_expert.py +0 -0
  248. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_init.py +0 -0
  249. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_integrators.py +0 -0
  250. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_plotting.py +0 -0
  251. {openscvx-0.3.2.dev315 → openscvx-0.3.2.dev317}/tests/test_propagation.py +0 -0
@@ -60,7 +60,7 @@ jobs:
60
60
 
61
61
  - name: Build documentation (check only)
62
62
  if: ${{ !inputs.version }}
63
- run: mkdocs build
63
+ run: mkdocs build --strict
64
64
 
65
65
  - name: Fetch gh-pages branch
66
66
  if: inputs.version
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.3.2.dev315
3
+ Version: 0.3.2.dev317
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
@@ -511,8 +511,5 @@ for state in problem.states:
511
511
 
512
512
  ## See Also
513
513
 
514
- - [Basic Problem Setup](../Usage/basic_problem_setup.md) - How to define problems
515
- - [API: State](../Usage/api_state.md) - State class documentation
516
- - [API: Control](../Usage/api_control.md) - Control class documentation
517
- - [API: Problem](../Usage/api_problem.md) - Main problem class
518
- - [Discretization](../Overview/discretization.md) - How discretization works in OpenSCvx
514
+ - [Hello world tutorial](../UsersGuide/01_hello_world_brachistochrone.md) - How to define problems
515
+ - [Discretization](../Foundations/discretization.md) - How discretization works in OpenSCvx
@@ -0,0 +1,44 @@
1
+ # Users Guide
2
+
3
+ Welcome to the OpenSCvx Users Guide. This section aims to provides a progressive and useful introduction to trajectory optimization with OpenSCvx, starting from first principles and building toward complex, representative problems.
4
+
5
+ ## Learning Path
6
+
7
+ The tutorials are designed to be read in order. Each builds on concepts from the previous, introducing new features in the context of increasingly realistic problems.
8
+
9
+ | Tutorial | Problem | You Will Learn |
10
+ |----------|---------|----------------|
11
+ | [01 Hello Brachistochrone](01_hello_world_brachistochrone.md) | Minimum-time descent curve | Core API: states, controls, dynamics, time, CTCS constraints, solving |
12
+ | [02 Drone Racing](02_drone_racing_constraints.md) | Racing through gates | Nodal constraints, `.at()`, `.over()`, `.convex()`, keyframe initialization |
13
+ | [03 Obstacle Avoidance](03_obstacle_avoidance_vmap.md) | 6-DOF navigation | Quaternion dynamics, spatial utilities, `ox.Parameter`, `ox.Vmap` |
14
+ | [04 Viewpoint Constraints](04_viewpoint_constraints.md) | Perception-constrained racing | Custom symbolic functions, Vmap with custom functions, attitude initialization |
15
+ | [05 Visualization](05_visualization.md) | — | 2D plots with Plotly, 3D interactive visualization with viser, Plotly-in-viser |
16
+ | [06 Dubin's Car](06_logic.md) | Conditional path planning | Conditional statements, signal temporal logic (STL) |
17
+ | [07 Multi-Link Arms](07_lie.md) | Articulated robot control | Lie algebra, propagated states |
18
+
19
+ ## Quick Start
20
+
21
+ If you're new to OpenSCvx, start with [Hello Brachistochrone](01_hello_world_brachistochrone.md). By the end of that tutorial you will have solved your first trajectory optimization problem and understand the core workflow:
22
+
23
+ 1. Define states and controls with `ox.State` and `ox.Control`
24
+ 2. Specify dynamics as a dictionary of symbolic expressions
25
+ 3. Add constraints using `ox.ctcs()` for continuous enforcement
26
+ 4. Create a `Problem`, initialize, solve, and post-process
27
+
28
+ From there, each subsequent tutorial introduces new capabilities while reinforcing the fundamentals.
29
+
30
+ ## Interactive Notebooks
31
+
32
+ Some tutorials include Google Colab notebooks for interactive learning:
33
+
34
+ - [03 6-DOF Obstacle Avoidance ![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1xLPC_UJWC35oPRIAY3vkxi8WEYnHCysQ?usp=sharing)
35
+ - [04 Viewpoint Constraints ![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1b3NEx288h4r4HuvCOj-fexmt90PPhKUw?usp=sharing)
36
+
37
+ These notebooks let you run the examples without local setup and experiment with parameters in real-time.
38
+
39
+ ## Beyond the Tutorials
40
+
41
+ After completing the tutorials, explore:
42
+
43
+ - [Examples](../Examples/abstract/brachistochrone.md) — Complete problem implementations across domains
44
+ - [API Reference](../Reference/problem.md) — Detailed documentation for all classes and functions
@@ -0,0 +1,320 @@
1
+ # 01 Hello Brachistochrone
2
+
3
+ In this _hello world_ tutorial we will introduce the reader to the fundamental concepts and API needed to define and solve a problem using OpenSCvx as well as how the results can be accessed.
4
+ Our example of choice in this endeavor is the _Brachistochrone problem_, which we briefly describe below before delving into the implementation.
5
+ Finally, we will lay out some further reading and tease the next tutorial for the interested reader.
6
+
7
+ This tutorial covers:
8
+
9
+ - Problem creation
10
+ - Variable instantiation
11
+ - Dynamics and constraint definition
12
+ - Solving the problem
13
+ - Accessing results
14
+
15
+ ## The Brachistochrone Problem
16
+
17
+ The [Brachistochrone problem](https://en.wikipedia.org/wiki/Brachistochrone_curve) is concerned with finding the fastest path between 2 points under the influence of gravity and without friction.
18
+ Bernouli originally posed the problem as:
19
+
20
+ > _Given two points A and B in a vertical plane, what is the curve traced out by a point acted on only by gravity, which starts at A and reaches B in the shortest time._
21
+
22
+ As with most good things in life, this can be written as an optimal control problem in the Mayer form as:
23
+
24
+ $$
25
+ \begin{align}
26
+ \min_{\mathbf{x}, \mathbf{u}, t_f}\ &t_f & \\
27
+ \mathrm{s.t.}\ &\dot{\mathbf{x}}(t) = f(\mathbf{x}(t), \mathbf{u}(t)) & \forall t\in[0, t_f], \quad &\textrm{dynamics} \\
28
+ &\mathbf{x}_{\min} \leq \mathbf{x}(t) \leq \mathbf{x}_{\max} & \forall t\in[0, t_f], \quad &\textrm{state bounds} \\
29
+ &\mathbf{u}_{\min} \leq \mathbf{u}(t) \leq \mathbf{u}_{\max} & \forall t\in[0, t_f], \quad &\textrm{control bounds} \\
30
+ &\mathbf{x}(0) = \mathbf{x}_{\mathrm{init}}, & & \textrm{initial}\\
31
+ &\mathbf{p}(t_f) = \mathbf{p}_{\mathrm{final}} & & \textrm{terminal}
32
+ \end{align}
33
+ $$
34
+
35
+ where the state $\mathbf{x} = [x, y, v]^\top$ consists of 2D position $\mathbf{p} = [x, y]^\top$ and speed $v$, the control $\mathbf{u} = \theta$ is the angle from vertical, and the dynamics are given by:
36
+
37
+ $$
38
+ f(\mathbf{x}, \mathbf{u}) = \begin{bmatrix} v \sin(\theta) \\ -v \cos(\theta) \\ g \cos(\theta) \end{bmatrix}
39
+ $$
40
+
41
+ Note that the terminal velocity is unconstrained.
42
+
43
+ This problem is particularly interesting to us because of two main reasons:
44
+
45
+ 1. It is a very nice, small toy problem we can use to introduce the core concepts of OpenSCvx. It is quick to formulate and quick to solve.
46
+ 2. _It has an analytical solution._ The Brachistochrone problem was solved by none other than Isaac Newton in 1697, who showed that the optimal path is given by a cycloid, the curve traced by a point on the rim of a rolling wheel:
47
+
48
+ $$
49
+ \begin{align}
50
+ x(\phi) &= r(\phi - \sin\phi) \\
51
+ y(\phi) &= r(1 - \cos\phi)
52
+ \end{align}
53
+ $$
54
+
55
+ where $\phi \in [0, \phi_f]$ is the curve parameter and $r$ is the radius of the generating circle, determined by the boundary conditions. The minimum time of descent is:
56
+
57
+ $$
58
+ t_f^* = \sqrt{\frac{r}{g}} \phi_f
59
+ $$
60
+
61
+ Because of these advantageous properties the Brachistochrone problem is extensively leveraged as a [unit test](https://github.com/OpenSCvx/OpenSCvx/blob/main/tests/test_brachistochrone.py).
62
+ We would highly recommend that anyone setting out to develop some kind of optimization software do the same.
63
+ It may not be necessary _nor_ sufficient, but a lot of things have to be working properly to solve Brachistochrone problem.
64
+
65
+ ## Creating an OpenSCvx Problem
66
+
67
+ During the development of OpenSCvx we spent a great deal of time trying to make the library as easy to work with as possible.
68
+ If it isn't easy to use, people will just write their own.
69
+ For that reason, we took inspiration from fantastic libraries such as [NumPy](https://github.com/numpy/numpy), [JAX](https://github.com/jax-ml/jax), and [CVXPY](https://github.com/cvxpy/cvxpy) and developed a symbolic expression system.
70
+
71
+ This not only allows us to keep the syntax close to the mathematical notation but also enables us to do lots of preprocessing, validation, canonicalization, and augmentation under the hood without the user ever having to know about it if they choose not to.
72
+ It also means that we can keep the syntax similar to popular libraries such as Numpy and JAX to reduce the learning curve for new users.
73
+
74
+ Before we begin defining our problem we can take care of our imports as well as some high-level parameters.
75
+ Typically, OpenSCvx is imported as `ox` for brevity. This will be the standard throughout these tutorials
76
+
77
+ ```python
78
+ import openscvx as ox
79
+ ```
80
+
81
+ Next, we can define our number of discrete decision nodes `n`, our initial guess for the completion time `total_time`, and define the gravitational acceleration `g`
82
+
83
+ ```python
84
+ n = 2
85
+ total_time = 2.0
86
+ g = 9.81
87
+ ```
88
+
89
+ Note that `total_time` is an _initial guess_ for the time. We will solve this as a free final-time problem and find the optimal solution.
90
+
91
+ ### Variable Definition
92
+
93
+ Now, we can start implementing the Brachistochrone problem, starting by creating the necessary state and control variables.
94
+ Each `ox.State` can be instantiated with a name and shape such as
95
+
96
+ ```python
97
+ position = ox.State("position", shape=(2,)) # 2D position [x, y]
98
+ ```
99
+
100
+ Then, we need to define the bounds. In this example we arbitrarily choose a 10 by 10 box for our position
101
+
102
+ ```python
103
+ position.max = [10.0, 10.0]
104
+ position.min = [0.0, 0.0]
105
+ ```
106
+
107
+ Finally, we must define the initial and final conditions. In this case we will find the Brachistochrone curve from $[0, 10]$ to $[10, 5]$ and set the values accordingly:
108
+
109
+ ```python
110
+ position.initial = np.array([0.0, 10.0])
111
+ position.final = [10.0, 5.0]
112
+ ```
113
+
114
+ All of these values can be either raw Python lists, NumPy or JAX arrays.
115
+ We can then similarly define the velocity state and it's bounds, resulting in the following code defining both states:
116
+
117
+ ```python
118
+ # Define state components
119
+ position = ox.State("position", shape=(2,)) # 2D position [x, y]
120
+ position.max = np.array([10.0, 10.0])
121
+ position.min = np.array([0.0, 0.0])
122
+ position.initial = np.array([0.0, 10.0])
123
+ position.final = [10.0, 5.0]
124
+
125
+ velocity = ox.State("velocity", shape=(1,)) # Scalar speed
126
+ velocity.max = np.array([10.0])
127
+ velocity.min = np.array([0.0])
128
+ velocity.initial = np.array([0.0])
129
+ velocity.final = [("free", 10.0)]
130
+
131
+ # Define list of all states (needed for Problem and constraints)
132
+ states = [position, velocity]
133
+ ```
134
+
135
+ The `.final` value of Velocity is defined as a tuple `("free", 10.0)`. This sets the corresponding value as "free" for the optimizer to choose.
136
+ This is how we can encode that the final velocity is unconstrained. The numerical value is necessary as an initial guess.
137
+ When only a value is specified as in the case of `position`, the values are assumed to be fixed.
138
+
139
+ To summarize, we can use the following syntax to define the initial and final conditions:
140
+
141
+ - Fixed value: `value` or `("fixed", value)`
142
+ - Free variable: `("free", guess)` - Can be optimized within bounds
143
+ - Minimize: `("minimize", guess)` - Variable to be minimized
144
+ - Maximize: `("maximize", guess)` - Variable to be maximized
145
+
146
+ For our control `theta` we can follow a similar syntax to define the symbolic `ox.Control` object:
147
+
148
+ ```python
149
+ # Define control
150
+ theta = ox.Control("theta", shape=(1,)) # Angle from vertical
151
+ theta.max = np.array([100.5 * jnp.pi / 180])
152
+ theta.min = np.array([0.0])
153
+ theta.guess = np.linspace(5 * jnp.pi / 180, 100.5 * jnp.pi / 180, n).reshape(-1, 1)
154
+
155
+ controls = [theta]
156
+ ```
157
+
158
+ Here, we do _not_ specify an initial or final value for the control but we _do_ need to specify an initial guess.
159
+ The initial guess must be of shape $(n_u \times n_{\mathrm{nodes}})$, providing an initial guess for each control at every node.
160
+ Technically, we can also provide a `.guess` for states. However, these default to a linear interpolation between initial and final conditions which is sufficient in most cases.
161
+
162
+ ### Dynamics
163
+
164
+ Dynamics are defined as a dictionary mapping state names to their time derivatives using symbolic expressions:
165
+
166
+ ```python
167
+ # Define dynamics as dictionary mapping state names to their derivatives
168
+ dynamics = {
169
+ "position": ox.Concat(
170
+ velocity[0] * ox.Sin(theta[0]), # x_dot
171
+ -velocity[0] * ox.Cos(theta[0]), # y_dot
172
+ ),
173
+ "velocity": g * ox.Cos(theta[0]),
174
+ }
175
+ ```
176
+
177
+ !!! Note
178
+ Every state passed to the problem must have a matching element in the dynamics dictionary under the same name.
179
+
180
+ The symbolic expressions support standard Python operators:
181
+
182
+ - Arithmetic: `+`, `-`, `*`, `/`, `**`
183
+ - Matrix multiplication: `@`
184
+ - Comparisons: `<=`, `>=`, `==` (for constraint definitions, see below)
185
+ - Indexing: `[...]`
186
+ - Transpose: `.T`
187
+
188
+ Common symbolic functions include:
189
+
190
+ - `ox.Concat()`: Concatenation
191
+ - `ox.Sin()`/`ox.Cos()`: Trigonometric functions
192
+ - `ox.linalg.Norm()`: Vector/matrix norms
193
+
194
+ !!! Note
195
+ Under the hood, symbolic expressions are compiled using JAX, so use `jax.numpy` for numerical constants and functions when needed.
196
+
197
+ ### Constraints (Continuous)
198
+
199
+ We already defined the box constraints when we instantiated the variables.
200
+ These are enforced at the discrete decision nodes automatically.
201
+ However, OpenSCvx offers another way to enforce constraints: we can also enforce the constraints _between_ the discrete nodes.
202
+ We call this continuous-time constraint-satisfaction (CTCS).
203
+ While the full description of CTCS is beyond the scope of this tutorial, what's important is that this is handled internally without user interaction.
204
+
205
+ We can define the boundary constraints as inequalities _i.e._ `state.min <= state` and `state <= state.max`.
206
+ To mark these as CTCS constraints we simply wrap them in `ox.ctcs(...)`
207
+
208
+ ```python
209
+ # Generate box constraints for all states
210
+ constraints = []
211
+ for state in states:
212
+ constraints.extend(
213
+ [
214
+ ox.ctcs(state <= state.max),
215
+ ox.ctcs(state.min <= state)
216
+ ]
217
+ )
218
+ ```
219
+
220
+ This style of constraint definition as a list should feel familiar to CVXPY users.
221
+ Note that we do not need to specify continuous box constraints for the controls.
222
+ This is because, by default, the controls are interpolated using first-order hold.
223
+ Therefore, by constraining the discrete nodes to lie within the bounds we can trivially see that the continuous case is guaranteed as well.
224
+
225
+ We will explore the various forms of constraints supported by OpenSCvx more in the [next tutorial](02_drone_racing_constraints.md)
226
+
227
+ ### Time
228
+
229
+ The last remaining piece we need is `Time`. OpenSCvx allows for free final-time problems with non-constant spacing.
230
+ We can define our time object similar to a `State` object, including the initial and final values as well as the min. and max. bounds.
231
+ Similar to the states the `final` value is defined as a tuple indicating that the final time is to be minimized as well as providing an initial guess for the total time.
232
+
233
+ ```python
234
+ time = ox.Time(
235
+ initial=0.0,
236
+ final=("minimize", total_time),
237
+ min=0.0,
238
+ max=total_time,
239
+ )
240
+ ```
241
+
242
+ OpenSCvx treats the `Time` object like any other state; it can be used to formulate time-dependent dynamics or constraints.
243
+ This is a more advanced subject for later tutorials.
244
+
245
+ ### Defining the Problem
246
+
247
+ Now we have everything we need to instantiate the `Problem` with our `dynamics`, `states`, `controls`, `time`, `constraints`, as well as the number of nodes `N`
248
+
249
+ ```python
250
+ problem = ox.Problem(
251
+ dynamics=dynamics,
252
+ states=states,
253
+ controls=controls,
254
+ time=time,
255
+ constraints=constraints,
256
+ N=n,
257
+ )
258
+ ```
259
+
260
+ ### Solving the Problem
261
+
262
+ Solving the problem is split into three steps:
263
+
264
+ 1. `problem.initialize()` initializes the problem, validating, preprocessing, canonicalizing, and augmenting the symbolic expressions before lowering them to JAX and CVXPY code.
265
+ 2. `problem.solve()` iteratively solves the OCP and generates the discrete state and control solution at the decision nodes.
266
+ 3. `problem.post_process()` propagates the nodal solution at high temporal fidelity. This lets us generate high resolution trajectories decoupled from the number of optimization nodes.
267
+
268
+ ```python
269
+ problem.initialize()
270
+ results = problem.solve()
271
+ results = problem.post_process()
272
+ ```
273
+
274
+ Both `.solve()` and `.post_process()` return an `OptimizationResults` object with the former containing only the nodal solution while the latter also includes the high-fidelity trajectories.
275
+
276
+ ### Accessing the results
277
+
278
+ But how can we easily access the results? Could we _somehow_ leverage the symbolic variables to make this easy?
279
+
280
+ Fear not; once a problem has been solved and post-processed we can access the results using the exact same variable names we defined earlier for our states and controls.
281
+ We can do so both for the discrete decision nodes in `results.nodes` and the high-resolution `results.trajectory` _e.g._
282
+
283
+ ```python
284
+ pos_nodes = results.nodes["position"]
285
+ theta_traj = results.trajectory["theta"]
286
+ ```
287
+
288
+ ### Visualizing the Results
289
+
290
+ OpenSCvx provides built-in plotting utilities for quick visualization of your results. The simplest way to see your solution is with `plot_states()` and `plot_controls()`:
291
+
292
+ ```python
293
+ from openscvx.plotting import plot_states, plot_controls
294
+
295
+ # Plot all state trajectories in a subplot grid
296
+ plot_states(results, ["position", "velocity"]).show()
297
+
298
+ # Plot control trajectories
299
+ plot_controls(results, ["theta"]).show()
300
+ ```
301
+
302
+ These functions create interactive Plotly figures showing:
303
+
304
+ - **Green lines**: High-fidelity propagated trajectory
305
+ - **Cyan markers**: Discrete optimization nodes
306
+ - **Red dashed lines**: Variable bounds
307
+
308
+ For the Brachistochrone problem, you'll see the position trace out the characteristic cycloid curve, while the control angle `theta` smoothly varies from near-vertical at the start to nearly horizontal at the end.
309
+
310
+ For more advanced visualization options including 3D interactive plots, see [Tutorial 05: Visualization](05_visualization.md).
311
+
312
+ ## Further Reading
313
+
314
+ - [Complete Brachistochrone Example](../Examples/abstract/brachistochrone.md)
315
+ - [Drone Racing: Constraints and 3DoF Dynamics](02_drone_racing_constraints.md)
316
+ - [Visualization: 2D Plots and 3D Interactive](05_visualization.md)
317
+
318
+ At this point you are well-equipped to go out and start constructing trajectory optimization problems.
319
+ If you are so-inclined you can dive into the [API reference documentation](../Reference/problem.md) or the [examples](../Examples/drone/drone_racing.md) and figure the rest out yourself.
320
+ For the interested reader, we will continue our guided tour of the various features in OpenSCvx by examining a slightly more interesting example which shows off different kinds of constraints we can define.
@@ -0,0 +1,289 @@
1
+ # 02 Drone Racing: Constraints and 3-DOF Dynamics
2
+
3
+ In this tutorial we build on the concepts from [Hello Brachistochrone](01_hello_world_brachistochrone.md) to tackle a more interesting problem: time-optimal drone racing through gates.
4
+ We will introduce 3D double-integrator dynamics and explore the various constraint types available in OpenSCvx.
5
+
6
+ This tutorial covers:
7
+
8
+ - 3DoF double-integrator (point mass) dynamics
9
+ - Nodal constraints with `.at()`
10
+ - Convex constraint marking with `.convex()`
11
+ - Keyframe-based initialization with `ox.init.linspace()`
12
+
13
+ ## The Drone Racing Problem
14
+
15
+ We consider a simplified drone racing scenario where a point-mass drone must fly through a sequence of gates in minimum time, returning to its starting position (loop closure).
16
+
17
+ $$
18
+ \begin{align}
19
+ \min_{\mathbf{x}, \mathbf{u}, t_f}\ &t_f & \\
20
+ \mathrm{s.t.}\ &\dot{\mathbf{x}}(t) = f(\mathbf{x}(t), \mathbf{u}(t)) & \forall t\in[0, t_f], \quad &\textrm{dynamics} \\
21
+ &\mathbf{x}_{\min} \leq \mathbf{x}(t) \leq \mathbf{x}_{\max} & \forall t\in[0, t_f], \quad &\textrm{state bounds} \\
22
+ &\mathbf{u}_{\min} \leq \mathbf{u}(t) \leq \mathbf{u}_{\max} & \forall t\in[0, t_f], \quad &\textrm{control bounds} \\
23
+ &\lVert A_i \mathbf{p}(t_i) - \mathbf{c}_i \rVert_\infty \leq 1 & \forall i \in [1, N_{\mathrm{gates}}], \quad &\textrm{gate constraints} \\
24
+ &\mathbf{x}(0) = \mathbf{x}_{\mathrm{init}}, & & \textrm{initial}\\
25
+ &\mathbf{p}(t_f) = \mathbf{p}_{\mathrm{init}} & & \textrm{loop closure}
26
+ \end{align}
27
+ $$
28
+
29
+ where the state $\mathbf{x} = [\mathbf{p}^\top, \mathbf{v}^\top]^\top$ consists of 3D position and velocity, and the control $\mathbf{u} = \mathbf{f}$ is the force vector. The double-integrator dynamics are:
30
+
31
+ $$
32
+ f(\mathbf{x}, \mathbf{u}) = \begin{bmatrix} \mathbf{v} \\ \frac{1}{m}\mathbf{f} + \mathbf{g} \end{bmatrix}
33
+ $$
34
+
35
+ The gate constraints use an infinity-norm formulation: the drone must pass through an ellipsoidal region defined by the matrix $A_i$ centered at $\mathbf{c}_i$ at specific times $t_i$.
36
+
37
+ ## Implementation
38
+
39
+ ### Variables
40
+
41
+ The state and control definitions follow the same pattern as the Brachistochrone problem, now in 3D:
42
+
43
+ ```python
44
+ import openscvx as ox
45
+
46
+ # 3D position and velocity states
47
+ position = ox.State("position", shape=(3,))
48
+ position.max = np.array([200.0, 100, 50])
49
+ position.min = np.array([-200.0, -100, 15])
50
+ position.initial = np.array([10.0, 0, 20])
51
+ position.final = [10.0, 0, 20] # Loop closure: return to start
52
+
53
+ velocity = ox.State("velocity", shape=(3,))
54
+ velocity.max = np.array([100, 100, 100])
55
+ velocity.min = np.array([-100, -100, -100])
56
+ velocity.initial = np.array([0, 0, 0])
57
+ velocity.final = [("free", 0), ("free", 0), ("free", 0)]
58
+
59
+ # 3D force control
60
+ force = ox.Control("force", shape=(3,))
61
+ f_max = 4.179 * 9.81
62
+ force.max = np.array([f_max, f_max, f_max])
63
+ force.min = np.array([-f_max, -f_max, -f_max])
64
+ force.guess = np.repeat(np.array([[0.0, 0, 10]]), n, axis=0)
65
+
66
+ states = [position, velocity]
67
+ controls = [force]
68
+ ```
69
+
70
+ Note that `position.final` equals `position.initial`. This enforces loop closure, requiring the drone to return to its starting position.
71
+
72
+ ### Dynamics
73
+
74
+ The double-integrator dynamics are straightforward: velocity is the derivative of position, and acceleration (force/mass plus gravity) is the derivative of velocity:
75
+
76
+ ```python
77
+ m = 1.0 # Mass
78
+ g_const = -9.81 # Gravity (negative z)
79
+
80
+ dynamics = {
81
+ "position": velocity,
82
+ "velocity": (1 / m) * force + np.array([0, 0, g_const]),
83
+ }
84
+ ```
85
+
86
+ This is simpler than the Brachistochrone dynamics because there's no angle—we directly control the force vector.
87
+
88
+ ### Path Constraints
89
+
90
+ As before, we enforce box constraints on states continuously:
91
+
92
+ ```python
93
+ constraints = []
94
+ for state in states:
95
+ constraints.extend([
96
+ ox.ctcs(state <= state.max),
97
+ ox.ctcs(state.min <= state)
98
+ ])
99
+ ```
100
+
101
+ With our states now in 3D it becomes clear to us why such constraints are also referred to as _path constraints_; we are enforcing the entire path to follow the constraints, not just the discrete nodes.
102
+
103
+ ### Discrete Constraints: Gate Passage
104
+
105
+ The key new concept is **nodal constraints**, constraints enforced at specific nodes rather than continuously.
106
+ Gate passage constraints are a perfect example: the drone must be within each gate at a specific node.
107
+
108
+ We use the `.at([node])` method to specify which nodes the constraint applies to:
109
+
110
+ ```python
111
+ # Gate passage constraint at a specific node
112
+ gate_constraint = (
113
+ ox.linalg.Norm(A_gate @ position - c_gate, ord="inf") <= 1.0
114
+ ).at([node])
115
+ ```
116
+
117
+ The infinity norm $\lVert \cdot \rVert_\infty$ defines a box-shaped region, which when combined with the scaling matrix `A_gate` creates an ellipsoidal gate region.
118
+
119
+ It should be noted that by default constraints are interpreted as nodal constraints, however they are applied to _all_ nodes when not otherwise noted. Writing
120
+
121
+ ```python
122
+ gate_constraint = (
123
+ ox.linalg.Norm(A_gate @ position - c_gate, ord="inf") <= 1.0
124
+ )
125
+ ```
126
+
127
+ would result in all nodes being constrained to lie within the gate.
128
+
129
+ There is a similar syntax for defining CTCS constraints: we can use the `.over((k, j))` method to define a continuous interval between nodes where a constraint should be enforced:
130
+
131
+ ```python
132
+ # Enforce altitude constraint continuously between nodes 0 and 5
133
+ altitude_constraint = (position[2] >= 15.0).over((0, 5))
134
+
135
+ # Enforce obstacle avoidance only during the approach phase
136
+ obstacle_center = np.array([50, -20, 20])
137
+ safe_distance = 5.0
138
+ diff = position - obstacle_center
139
+ obstacle_constraint = (diff.T @ diff >= safe_distance**2).over((5, 10))
140
+ ```
141
+
142
+ The `.over()` method also accepts optional parameters for the penalty function type (`penalty`), grouping index (`idx`), and whether to also check nodally (`check_nodally`).
143
+
144
+ !!! note
145
+ You can also directly instantiate `NodalConstraint` or `CTCS` objects if you prefer:
146
+
147
+ ```python
148
+ from openscvx.symbolic.expr.constraint import NodalConstraint, CTCS
149
+
150
+ # Equivalent to (position[2] >= 15.0).at([5, 10])
151
+ altitude_nodal = NodalConstraint(position[2] >= 15.0, nodes=[5, 10])
152
+
153
+ # Equivalent to (position[2] >= 15.0).over((0, 15))
154
+ altitude_ctcs = CTCS(position[2] >= 15.0, nodes=(0, 15))
155
+ ```
156
+
157
+ #### Marking Convex Constraints
158
+
159
+ When a constraint is convex, we can mark it with `.convex()`:
160
+
161
+ ```python
162
+ gate_constraint = (
163
+ ox.linalg.Norm(A_gate @ position - c_gate, ord="inf") <= 1.0
164
+ ).convex().at([node])
165
+ ```
166
+
167
+ This tells OpenSCvx that no successive convexification is needed for this constraint, it is then lowered directly as a CVXPY constraint when the problem is lowered.
168
+ This helps improve numerical performance as the solver is operating directly on the true constraint, not a linearized version thereof.
169
+
170
+ #### Full Gate Setup
171
+
172
+ Here's the complete gate constraint setup for multiple gates:
173
+
174
+ ```python
175
+ n_gates = 10
176
+ gate_centers = [
177
+ np.array([59.436, 0.000, 20.0]),
178
+ np.array([92.964, -23.750, 25.524]),
179
+ # ... more gate centers
180
+ ]
181
+
182
+ # Assign nodes to gates (evenly spaced)
183
+ nodes_per_gate = 2
184
+ gate_nodes = np.arange(nodes_per_gate, n, nodes_per_gate)
185
+
186
+ # Add gate constraints
187
+ for node, center in zip(gate_nodes, gate_centers):
188
+ A_gate_scaled = A_gate @ center # Pre-scaled center
189
+ gate_constraint = (
190
+ ox.linalg.Norm(A_gate @ position - A_gate_scaled, ord="inf") <= 1.0
191
+ ).convex().at([node])
192
+ constraints.append(gate_constraint)
193
+ ```
194
+
195
+ ### Keyframe Initialization
196
+
197
+ For problems with waypoints or gates, a linear interpolation between start and end isn't a good initial guess.
198
+ In fact, the start and end positions are identical to enforce loop closure. We need a more customized initial guess.
199
+
200
+ In this drone racing example the gate ordering is not free. There is a specific order in which the gates must be traversed.
201
+ We can use this _a priori_ knowledge to construct our initial guess; placing the required nodes directly in the center of the corresponding gate and linearly interpolating at the nodes in between gates.
202
+
203
+ To facilitate such initial guesses, OpenSCvx provides `ox.init.linspace()` for keyframe-based initialization:
204
+
205
+ ```python
206
+ position.guess = ox.init.linspace(
207
+ keyframes=[position.initial] + gate_centers + [position.final],
208
+ nodes=[0] + list(gate_nodes) + [n - 1],
209
+ )
210
+ ```
211
+
212
+ This lets us specify lists of key values, or `keyframes` to borrow an animation term and the corresponding nodes. At the nodes in between the keyframes the values will just be linearly interpolated.
213
+ This creates an initial trajectory that passes through each gate center at the appropriate node, giving the solver a much better starting point.
214
+
215
+ OpenSCvx also provides similar `ox.init.slerp(...)` and `ox.init.nlerp(...)` functions for _spherical linear interpolation_ (SLERP) and _normalized linear interpolation_ (NLERP) for quaternion initialization.
216
+
217
+ ### Problem Definition and Solution
218
+
219
+ ```python
220
+ time = ox.Time(
221
+ initial=0.0,
222
+ final=("minimize", total_time),
223
+ min=0.0,
224
+ max=total_time,
225
+ )
226
+
227
+ problem = ox.Problem(
228
+ dynamics=dynamics,
229
+ states=states,
230
+ controls=controls,
231
+ time=time,
232
+ constraints=constraints,
233
+ N=n,
234
+ )
235
+
236
+ problem.initialize()
237
+ results = problem.solve()
238
+ results = problem.post_process()
239
+ ```
240
+
241
+ ### Visualizing 3D Trajectories
242
+
243
+ With our drone now flying in 3D, simple time series plots don't capture the full picture. The `plot_projections_2d()` function shows XY, XZ, and YZ plane views:
244
+
245
+ ```python
246
+ from openscvx.plotting import plot_states, plot_projections_2d
247
+
248
+ # Time series of states
249
+ plot_states(results, ["position", "velocity"]).show()
250
+
251
+ # 2D projections colored by velocity
252
+ plot_projections_2d(
253
+ results,
254
+ var_name="position",
255
+ velocity_var_name="velocity"
256
+ ).show()
257
+ ```
258
+
259
+ For fully interactive 3D visualization with animated playback, gates, and thrust vectors, OpenSCvx integrates with [viser](https://viser.studio/). See [Tutorial 05: Visualization](05_visualization.md) for details on building rich 3D visualizations.
260
+
261
+ ## Constraint Types Summary
262
+
263
+ | Type | Syntax | Use Case |
264
+ |------|--------|----------|
265
+ | Continuous (all nodes) | `ox.ctcs(expr)` | Path constraints enforced between all nodes |
266
+ | Continuous (interval) | `expr.over((start, end))` | Path constraints over a specific interval |
267
+ | Nodal (specific) | `expr.at([nodes])` | Waypoints, gate passage, events |
268
+ | Nodal (all) | `expr` | Constraint at every node |
269
+ | Convex | `expr.convex()` | Mark convex constraints for direct CVXPY lowering |
270
+ | Combined | `expr.convex().at([nodes])` | Convex nodal constraints |
271
+
272
+ !!! note "Cross-Node Constraints"
273
+ OpenSCvx also supports **cross-node constraints** that couple values at different nodes within a single constraint expression. These are created by using `.at(k)` on variables (not constraints):
274
+
275
+ ```python
276
+ # Rate limit: position change between consecutive nodes
277
+ rate_limit = position.at(5) - position.at(4) <= max_step
278
+ ```
279
+
280
+ Cross-node constraints are automatically detected and handled differently from nodal constraints—they operate on the full trajectory arrays rather than being evaluated node-by-node. We will cover these in a more advanced tutorial.
281
+
282
+ ## Further Reading
283
+
284
+ - [Complete Drone Racing Example](../Examples/drone/dr_double_integrator.md)
285
+ - [Full 6-DOF Drone Racing](../Examples/drone/drone_racing.md) — adds attitude dynamics
286
+ - [API Reference: Constraints](../Reference/symbolic/expr/constraint.md)
287
+ - [Obstacle Avoidance: 6-DOF Dynamics, Parameters, and Vmap](03_obstacle_avoidance_vmap.md)
288
+ - [Viewpoint Constraints: Custom Functions and Perception](04_viewpoint_constraints.md)
289
+ - [Visualization: 2D Plots and 3D Interactive](05_visualization.md)