openscvx 0.4.1.dev119__tar.gz → 0.4.1.dev121__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 (312) hide show
  1. {openscvx-0.4.1.dev119/openscvx.egg-info → openscvx-0.4.1.dev121}/PKG-INFO +1 -1
  2. openscvx-0.4.1.dev121/examples/abstract/impulsive.py +103 -0
  3. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/plotting_viser.py +81 -0
  4. openscvx-0.4.1.dev121/examples/spacecraft/hohmann_transfer.py +205 -0
  5. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/_version.py +3 -3
  6. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/augmented_lagrangian.py +28 -15
  7. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/base.py +104 -5
  8. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/penalized_trust_region.py +61 -1
  9. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/discretization/__init__.py +7 -1
  10. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/discretization/linearize_discretize.py +49 -0
  11. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/byof.py +14 -0
  12. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/lowering.py +54 -5
  13. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/validation.py +44 -0
  14. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/cvxpy_variables.py +6 -0
  15. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/problem.py +2 -0
  16. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/unified.py +199 -36
  17. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/__init__.py +21 -1
  18. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/plotting.py +151 -31
  19. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/__init__.py +4 -8
  20. openscvx-0.4.1.dev121/openscvx/plotting/viser/orbits.py +85 -0
  21. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/problem.py +42 -2
  22. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/propagation/post_processing.py +14 -2
  23. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/propagation/propagation.py +37 -6
  24. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/solvers/ptr_solver.py +92 -17
  25. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/augmentation.py +26 -12
  26. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/builder.py +51 -6
  27. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/control.py +47 -2
  28. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lower.py +80 -4
  29. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/preprocessing.py +21 -0
  30. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/problem.py +3 -0
  31. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/unified.py +92 -16
  32. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/caching.py +10 -4
  33. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121/openscvx.egg-info}/PKG-INFO +1 -1
  34. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/SOURCES.txt +5 -0
  35. openscvx-0.4.1.dev121/tests/hohmann_analytical.py +55 -0
  36. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_variable.py +5 -26
  37. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_augmentation.py +15 -15
  38. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_preprocessing.py +194 -2
  39. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_unified.py +33 -0
  40. openscvx-0.4.1.dev121/tests/test_impulsive.py +210 -0
  41. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/assets/logo.svg +0 -0
  42. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/release-drafter.yml +0 -0
  43. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/_docs.yml +0 -0
  44. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/branch-name.yml +0 -0
  45. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/docs.yml +0 -0
  46. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/lint.yml +0 -0
  47. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/nightly.yml +0 -0
  48. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/release-drafter.yml +0 -0
  49. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/release.yml +0 -0
  50. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/tests-integration.yml +0 -0
  51. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/tests-unit.yml +0 -0
  52. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.gitignore +0 -0
  53. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/CONTRIBUTING.md +0 -0
  54. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/LICENSE +0 -0
  55. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/README.md +0 -0
  56. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/constraint_reformulation.md +0 -0
  57. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/control_parameterization.md +0 -0
  58. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/discretization.md +0 -0
  59. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/ocp.md +0 -0
  60. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/scvx.md +0 -0
  61. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/time_dilation.md +0 -0
  62. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UnderTheHood/lowering_architecture.md +0 -0
  63. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  64. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/00_introduction.md +0 -0
  65. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  66. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  67. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  68. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  69. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/05_visualization.md +0 -0
  70. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/06_logic.md +0 -0
  71. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/07_lie.md +0 -0
  72. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/favicon.png +0 -0
  73. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ct-scvx_dark.png +0 -0
  74. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ct-scvx_light.png +0 -0
  75. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ctcs_dark.png +0 -0
  76. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ctcs_light.png +0 -0
  77. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/problem_class_dark.png +0 -0
  78. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/problem_class_light.png +0 -0
  79. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/logo.svg +0 -0
  80. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/citation.md +0 -0
  81. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/examples.md +0 -0
  82. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/getting-started.md +0 -0
  83. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/index.md +0 -0
  84. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/javascripts/mathjax.js +0 -0
  85. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/abstract/brachistochrone.py +0 -0
  86. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/arm/three_link_arm.py +0 -0
  87. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car.py +0 -0
  88. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car_conditional.py +0 -0
  89. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car_disjoint.py +0 -0
  90. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car_stljax.py +0 -0
  91. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/cinema_vp.py +0 -0
  92. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_double_integrator.py +0 -0
  93. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_vp.py +0 -0
  94. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_vp_nodal.py +0 -0
  95. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_vp_polytope.py +0 -0
  96. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/drone_racing.py +0 -0
  97. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/logo.py +0 -0
  98. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/logo_utils/acl_logo.svg +0 -0
  99. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/logo_utils/svg_path_utils.py +0 -0
  100. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/obstacle_avoidance.py +0 -0
  101. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  102. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  103. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/plotting.py +0 -0
  104. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
  105. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
  106. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
  107. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/cinema_vp_realtime.py +0 -0
  108. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/drone_racing_realtime.py +0 -0
  109. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/dubins_car_realtime.py +0 -0
  110. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  111. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/rocket/3DoF_pdg.py +0 -0
  112. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/spacecraft/proxops_cw.py +0 -0
  113. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/ctlos_cine.gif +0 -0
  114. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/ctlos_dr.gif +0 -0
  115. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/dtlos_cine.gif +0 -0
  116. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/dtlos_dr.gif +0 -0
  117. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/openscvx_logo.svg +0 -0
  118. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/openscvx_logo_square.png +0 -0
  119. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/oscvx_structure_full_dark.svg +0 -0
  120. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/video_preview.png +0 -0
  121. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/__init__.py +0 -0
  122. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background.avif +0 -0
  123. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
  124. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
  125. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
  126. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
  127. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars.avif +0 -0
  128. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
  129. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
  130. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
  131. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
  132. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon.avif +0 -0
  133. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
  134. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
  135. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
  136. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
  137. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
  138. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
  139. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
  140. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
  141. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
  142. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space.avif +0 -0
  143. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
  144. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
  145. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
  146. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
  147. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth.avif +0 -0
  148. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
  149. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
  150. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
  151. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
  152. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/javascripts/parallax.js +0 -0
  153. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/logo.svg +0 -0
  154. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/stylesheets/custom.css +0 -0
  155. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/stylesheets/parallax.css +0 -0
  156. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/home.html +0 -0
  157. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/main.html +0 -0
  158. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/partials/parallax/hero.html +0 -0
  159. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/partials/parallax.html +0 -0
  160. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/mkdocs.yml +0 -0
  161. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/__init__.py +0 -0
  162. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/__main__.py +0 -0
  163. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/__init__.py +0 -0
  164. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/constant_proximal_weight.py +0 -0
  165. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/optimization_results.py +0 -0
  166. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/ramp_proximal_weight.py +0 -0
  167. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/config.py +0 -0
  168. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/discretization/base.py +0 -0
  169. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/__init__.py +0 -0
  170. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/init/__init__.py +0 -0
  171. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/init/interpolation.py +0 -0
  172. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/integrators/__init__.py +0 -0
  173. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/integrators/runge_kutta.py +0 -0
  174. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/loader.py +0 -0
  175. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/__init__.py +0 -0
  176. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/cvxpy_constraints.py +0 -0
  177. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/dynamics.py +0 -0
  178. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/jax_constraints.py +0 -0
  179. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/parameters.py +0 -0
  180. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/scp_iteration.py +0 -0
  181. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/animated.py +0 -0
  182. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/plotly_integration.py +0 -0
  183. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/primitives.py +0 -0
  184. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/scp.py +0 -0
  185. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/server.py +0 -0
  186. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/propagation/__init__.py +0 -0
  187. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/solvers/__init__.py +0 -0
  188. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/solvers/base.py +0 -0
  189. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/__init__.py +0 -0
  190. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/constraint_set.py +0 -0
  191. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/__init__.py +0 -0
  192. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/arithmetic.py +0 -0
  193. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/array.py +0 -0
  194. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/constraint.py +0 -0
  195. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/expr.py +0 -0
  196. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  197. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  198. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/se3.py +0 -0
  199. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/so3.py +0 -0
  200. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/linalg.py +0 -0
  201. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/logic.py +0 -0
  202. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/math.py +0 -0
  203. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/spatial.py +0 -0
  204. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/state.py +0 -0
  205. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/stl.py +0 -0
  206. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/time.py +0 -0
  207. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/variable.py +0 -0
  208. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/vmap.py +0 -0
  209. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/hashing.py +0 -0
  210. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/__init__.py +0 -0
  211. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  212. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  213. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  214. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  215. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  216. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  217. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  218. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  219. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  220. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  221. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  222. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  223. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  224. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  225. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  226. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  227. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  228. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
  229. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  230. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  231. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  232. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  233. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
  234. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  235. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  236. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  237. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  238. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  239. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/__init__.py +0 -0
  240. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/_registry.py +0 -0
  241. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/array.py +0 -0
  242. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/constraint.py +0 -0
  243. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/lie.py +0 -0
  244. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/linalg.py +0 -0
  245. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/logic.py +0 -0
  246. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/math.py +0 -0
  247. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/parser.py +0 -0
  248. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/spatial.py +0 -0
  249. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/stl.py +0 -0
  250. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/tokenizer.py +0 -0
  251. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/sparsity.py +0 -0
  252. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/__init__.py +0 -0
  253. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/cache.py +0 -0
  254. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/printing.py +0 -0
  255. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/profiling.py +0 -0
  256. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/utils.py +0 -0
  257. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/dependency_links.txt +0 -0
  258. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/entry_points.txt +0 -0
  259. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/requires.txt +0 -0
  260. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/top_level.txt +0 -0
  261. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/pyproject.toml +0 -0
  262. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/scripts/gen_example_pages.py +0 -0
  263. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/scripts/gen_ref_pages.py +0 -0
  264. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/setup.cfg +0 -0
  265. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/__init__.py +0 -0
  266. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/brachistochrone_analytical.py +0 -0
  267. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/fixtures/brachistochrone.json +0 -0
  268. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/fixtures/brachistochrone.yaml +0 -0
  269. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/__init__.py +0 -0
  270. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/__init__.py +0 -0
  271. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_arithmetic.py +0 -0
  272. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_array.py +0 -0
  273. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_constraint.py +0 -0
  274. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_expr.py +0 -0
  275. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_lie.py +0 -0
  276. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_linalg.py +0 -0
  277. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_logic.py +0 -0
  278. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_math.py +0 -0
  279. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_node_reference.py +0 -0
  280. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_parameters.py +0 -0
  281. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_scaling.py +0 -0
  282. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_spatial.py +0 -0
  283. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_vmap.py +0 -0
  284. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/__init__.py +0 -0
  285. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_array.py +0 -0
  286. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_constraint.py +0 -0
  287. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_lie.py +0 -0
  288. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_linalg.py +0 -0
  289. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_load.py +0 -0
  290. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_logic.py +0 -0
  291. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_math.py +0 -0
  292. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_parser.py +0 -0
  293. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_spatial.py +0 -0
  294. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_stl.py +0 -0
  295. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_tokenizer.py +0 -0
  296. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_vmap.py +0 -0
  297. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_hashing.py +0 -0
  298. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_lower_cvxpy.py +0 -0
  299. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_lower_jax.py +0 -0
  300. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_sparsity.py +0 -0
  301. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_autotuning.py +0 -0
  302. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_brachistochrone.py +0 -0
  303. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_cvxpygen_optional.py +0 -0
  304. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_discretization.py +0 -0
  305. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_examples.py +0 -0
  306. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_expert.py +0 -0
  307. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_init.py +0 -0
  308. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_integrators.py +0 -0
  309. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_loader.py +0 -0
  310. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_optimization_results.py +0 -0
  311. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_plotting.py +0 -0
  312. {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_propagation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.4.1.dev119
3
+ Version: 0.4.1.dev121
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
@@ -0,0 +1,103 @@
1
+ """Impulsive control example with mixed continuous/impulsive inputs.
2
+
3
+ This example demonstrates a simple 1D position/velocity system with:
4
+
5
+ - Continuous acceleration control
6
+ - Impulsive delta-v control applied only at selected nodes
7
+ - Time-dilation for minimum-time objective
8
+ - CTCS bounds on states
9
+ """
10
+
11
+ import os
12
+ import sys
13
+
14
+ import numpy as np
15
+
16
+ # Add grandparent directory to path to import examples.plotting
17
+ current_dir = os.path.dirname(os.path.abspath(__file__))
18
+ grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
19
+ sys.path.append(grandparent_dir)
20
+
21
+ import openscvx as ox
22
+ from openscvx import Problem
23
+ from openscvx.plotting import plot_controls, plot_states
24
+
25
+ n = 10
26
+
27
+ p = ox.State("position", shape=(1,))
28
+ p.max = np.array([1.0])
29
+ p.min = np.array([0.0])
30
+ p.initial = np.array([0.0])
31
+ p.final = np.array([1.0])
32
+ p.guess = np.linspace(p.initial, p.final, n).reshape(-1, 1)
33
+
34
+ v = ox.State("velocity", shape=(1,)) # Scalar speed
35
+ v.max = np.array([1.0])
36
+ v.min = np.array([-1.0])
37
+ v.initial = np.array([0.0])
38
+ v.final = np.array([0.0])
39
+ v.guess = np.linspace(v.initial, v.final, n).reshape(-1, 1)
40
+
41
+ dv = ox.Control(
42
+ "delta_v",
43
+ shape=(1,),
44
+ impulsive=True,
45
+ nodes=[0, n - 1],
46
+ )
47
+ dv.max = np.array([0.2])
48
+ dv.min = np.array([-0.2])
49
+ dv.guess = np.linspace(np.array([0]), np.array([0]), n)
50
+ dv.scaling_min = np.array([-0.2])
51
+ dv.scaling_max = np.array([0.2])
52
+
53
+ a = ox.Control("acceleration", shape=(1,))
54
+ a.max = np.array([0.01])
55
+ a.min = np.array([-0.01])
56
+ a.guess = np.linspace(np.array([0]), np.array([0]), n)
57
+ a.scaling_min = np.array([-1])
58
+ a.scaling_max = np.array([1])
59
+
60
+ dynamics = {
61
+ "position": v,
62
+ "velocity": a,
63
+ }
64
+
65
+ dynamics_discrete = {
66
+ "position": p,
67
+ "velocity": v + ox.Power(ox.Max(ox.Abs(dv), 1e-6), 1.5) * dv,
68
+ }
69
+
70
+ states = [p, v]
71
+ controls = [dv, a]
72
+
73
+ constraints = []
74
+ for state in states:
75
+ constraints.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
76
+
77
+ time = ox.Time(initial=0.0, final=ox.Minimize(10.0), min=0.0, max=20.0)
78
+
79
+ problem = Problem(
80
+ dynamics=dynamics,
81
+ dynamics_discrete=dynamics_discrete,
82
+ states=states,
83
+ controls=controls,
84
+ time=time,
85
+ constraints=constraints,
86
+ N=n,
87
+ )
88
+
89
+ problem.algorithm.lam_prox = 1e-2 # Weight on the Trust Reigon
90
+ problem.algorithm.lam_vc = 5e1 # Weight on the Trust Reigon
91
+ problem.algorithm.lam_cost = 1e0 # Weight on the Minimal Time Objective
92
+ problem.algorithm.k_max = 100
93
+
94
+ plotting_dict = {}
95
+
96
+ if __name__ == "__main__":
97
+ problem.initialize()
98
+ results = problem.solve()
99
+ results = problem.post_process()
100
+ results.update(plotting_dict)
101
+
102
+ plot_states(results).show()
103
+ plot_controls(results).show()
@@ -21,6 +21,7 @@ from openscvx.plotting.viser import (
21
21
  add_animated_vector_norm_plot,
22
22
  add_animation_controls,
23
23
  add_attitude_frame,
24
+ add_circular_orbit,
24
25
  add_ellipsoid_obstacles,
25
26
  add_gates,
26
27
  add_ghost_trajectory,
@@ -1174,3 +1175,83 @@ def compute_velocity_colors_realtime(vel: np.ndarray, cmap) -> np.ndarray:
1174
1175
  dtype=np.uint8,
1175
1176
  )
1176
1177
  return colors
1178
+
1179
+
1180
+ def _as_3d(points: np.ndarray) -> np.ndarray:
1181
+ """Ensure points are shape (..., 3) by appending z=0 when needed."""
1182
+ points = np.asarray(points, dtype=np.float64)
1183
+ if points.ndim == 1:
1184
+ if points.shape[0] == 2:
1185
+ return np.array([points[0], points[1], 0.0], dtype=np.float64)
1186
+ if points.shape[0] == 3:
1187
+ return points
1188
+ raise ValueError(f"Expected 2D or 3D point, got shape {points.shape}")
1189
+ if points.ndim == 2:
1190
+ if points.shape[1] == 2:
1191
+ z = np.zeros((points.shape[0], 1), dtype=np.float64)
1192
+ return np.concatenate([points, z], axis=1)
1193
+ if points.shape[1] == 3:
1194
+ return points
1195
+ raise ValueError(f"Expected points with 2 or 3 columns, got shape {points.shape}")
1196
+ raise ValueError(f"Expected points with ndim 1 or 2, got ndim {points.ndim}")
1197
+
1198
+
1199
+ def create_hohmann_transfer_server(
1200
+ results: OptimizationResults,
1201
+ *,
1202
+ r1: float,
1203
+ r2: float,
1204
+ position_key: str = "position",
1205
+ velocity_key: str = "velocity",
1206
+ loop_animation: bool = True,
1207
+ show_grid: bool = False,
1208
+ scene_scale: float = 1.0,
1209
+ orbit_n_points: int = 512,
1210
+ orbit_color_inner: tuple[int, int, int] = (120, 180, 255),
1211
+ orbit_color_outer: tuple[int, int, int] = (255, 180, 120),
1212
+ transfer_point_size: float = 0.10,
1213
+ marker_radius: float = 0.6,
1214
+ ) -> viser.ViserServer:
1215
+ """Create an animated viser server for a planar Hohmann transfer.
1216
+
1217
+ Draws two static circular orbit rings (r1, r2) and animates the transfer
1218
+ trajectory stored in ``results.trajectory``.
1219
+ """
1220
+ pos = results.trajectory.get(position_key)
1221
+ if pos is None:
1222
+ raise KeyError(f"results.trajectory is missing '{position_key}'")
1223
+ pos_3d = _as_3d(pos) / scene_scale
1224
+
1225
+ vel = results.trajectory.get(velocity_key)
1226
+ vel_3d = None if vel is None else _as_3d(vel)
1227
+ colors = compute_velocity_colors(vel_3d, fallback_length=pos_3d.shape[0])
1228
+
1229
+ traj_time = np.asarray(results.trajectory["time"], dtype=np.float64).flatten()
1230
+
1231
+ server = create_server(pos_3d, show_grid=show_grid)
1232
+
1233
+ # Static orbit rings
1234
+ add_circular_orbit(
1235
+ server,
1236
+ r1 / scene_scale,
1237
+ name="inner_orbit",
1238
+ n_points=orbit_n_points,
1239
+ color=orbit_color_inner,
1240
+ line_width=2.5,
1241
+ )
1242
+ add_circular_orbit(
1243
+ server,
1244
+ r2 / scene_scale,
1245
+ name="outer_orbit",
1246
+ n_points=orbit_n_points,
1247
+ color=orbit_color_outer,
1248
+ line_width=2.5,
1249
+ )
1250
+
1251
+ # Static ghost + animated trail + marker
1252
+ add_ghost_trajectory(server, pos_3d, colors, opacity=0.25, point_size=transfer_point_size)
1253
+ _, update_trail = add_animated_trail(server, pos_3d, colors, point_size=transfer_point_size)
1254
+ _, update_marker = add_position_marker(server, pos_3d, radius=marker_radius)
1255
+
1256
+ add_animation_controls(server, traj_time, [update_trail, update_marker], loop=loop_animation)
1257
+ return server
@@ -0,0 +1,205 @@
1
+ """Hohmann transfer with impulsive delta-v controls (LEO → GEO).
2
+
3
+ This example mirrors the Hohmann transfer calculation from
4
+ [_Orbital Mechanics & Astrodynamics_](https://orbital-mechanics.space/orbital-maneuvers/hohmann-transfer-example.html)
5
+ by Bryan Weber and embeds it in a trajectory optimization problem that uses impulsive
6
+ delta-v controls.
7
+
8
+ We consider a planar, two-body Earth-centered problem:
9
+
10
+ - Initial circular orbit: 250 km altitude LEO
11
+ - Final circular orbit: GEO with 1 sidereal-day period
12
+ - 2D position/velocity states in an inertial frame (km, km/s)
13
+ - Central gravity with mu = 3.986e5 km^3/s^2
14
+ - Two impulsive delta-v maneuvers applied at the initial and final nodes
15
+ - A scalar cost state that accumulates the L2 norm of each impulse and is
16
+ minimized at the final time (approximate minimum-fuel objective)
17
+
18
+ Continuous dynamics between impulses:
19
+
20
+ x_dot = vx
21
+ y_dot = vy
22
+ vx_dot = -mu * x / r^3
23
+ vy_dot = -mu * y / r^3
24
+
25
+ Discrete dynamics at impulsive nodes:
26
+
27
+ - position unchanged
28
+ - velocity += delta_v
29
+ - cost += ||delta_v||
30
+
31
+ The transfer time is fixed to the Hohmann half-period of the transfer ellipse.
32
+ In the __main__ block we also compute and print the analytic Hohmann Δv and
33
+ propellant mass from Weber's example for comparison.
34
+ """
35
+
36
+ import os
37
+ import sys
38
+
39
+ import numpy as np
40
+
41
+ # Add grandparent directory to path to import examples.plotting
42
+ current_dir = os.path.dirname(os.path.abspath(__file__))
43
+ grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
44
+ sys.path.append(grandparent_dir)
45
+
46
+ import openscvx as ox
47
+ from examples.plotting_viser import create_hohmann_transfer_server
48
+ from openscvx import Problem
49
+ from openscvx.plotting import plot_controls, plot_scp_iterations, plot_states
50
+
51
+ # Problem configuration (match Weber's LEO → GEO example)
52
+ n = 15
53
+
54
+ mu = 3.986e5 # km^3 / s^2
55
+ R_E = 6378.0 # km
56
+ r_leo = 250.0 + R_E
57
+
58
+ sidereal_day = 86164.0905 # s
59
+ r_cubed = mu * sidereal_day**2 / (4 * np.pi**2)
60
+ r_geo = r_cubed ** (1.0 / 3.0)
61
+
62
+ r1 = r_leo
63
+ r2 = r_geo
64
+
65
+ # Hohmann transfer half-period for the transfer ellipse (fixed final time)
66
+ a_transfer = 0.5 * (r1 + r2)
67
+ T_transfer = np.pi * np.sqrt(a_transfer**3 / mu)
68
+
69
+ # States: planar position, planar velocity, and scalar accumulated cost
70
+ position = ox.State("position", shape=(2,))
71
+ position.initial = np.array([r1, 0.0])
72
+ position.final = np.array([-r2, 0.0]) # Opposite side for half-period transfer
73
+ position.min = np.array([-(1.5 * r2), -(1.5 * r2)])
74
+ position.max = np.array([1.5 * r2, 1.5 * r2])
75
+
76
+ # Initial guess: follow an approximate Hohmann-like arc that never passes
77
+ # through the origin (to avoid r ≈ 0 causing singular dynamics).
78
+ theta_guess = np.linspace(0.0, np.pi, n)
79
+ radius_guess = np.linspace(r1, r2, n)
80
+ position_guess = np.stack(
81
+ [radius_guess * np.cos(theta_guess), radius_guess * np.sin(theta_guess)],
82
+ axis=1,
83
+ )
84
+ position.guess = position_guess
85
+
86
+ velocity = ox.State("velocity", shape=(2,))
87
+ v_c1 = np.sqrt(mu / r1)
88
+ v_c2 = np.sqrt(mu / r2)
89
+ velocity.initial = np.array([0.0, v_c1])
90
+ velocity.final = np.array([0.0, -v_c2])
91
+ velocity_min_mag = -2.0 * max(v_c1, v_c2)
92
+ velocity_max_mag = 2.0 * max(v_c1, v_c2)
93
+ velocity.min = np.array([velocity_min_mag, velocity_min_mag])
94
+ velocity.max = np.array([velocity_max_mag, velocity_max_mag])
95
+ velocity.guess = np.tile(np.array([0.0, (v_c1 + v_c2) / 2.0]), (n, 1))
96
+
97
+ cost = ox.State("cost", shape=(1,))
98
+ cost.initial = np.array([0.0])
99
+ # Minimize final accumulated impulse norm with a loose upper bound
100
+ cost.final = [("minimize", 10.0)]
101
+ cost.min = np.array([0.0])
102
+ cost.max = np.array([10.0])
103
+ cost.guess = np.zeros((n, 1))
104
+
105
+ # Impulsive delta-v control applied only at the first and last nodes
106
+ dv = ox.Control(
107
+ "delta_v",
108
+ shape=(2,),
109
+ impulsive=True,
110
+ nodes=[0, n - 1],
111
+ )
112
+ dv_bound = 5.0 # km/s
113
+ dv.min = np.array([-dv_bound, -dv_bound])
114
+ dv.max = np.array([dv_bound, dv_bound])
115
+ dv.guess = np.zeros((n, 2))
116
+
117
+ states = [position, velocity, cost]
118
+ controls = [dv]
119
+
120
+ # Continuous dynamics: planar two-body gravity with no continuous thrust.
121
+ # We clamp the radius away from zero for robustness in the linearization.
122
+ r = ox.linalg.Norm(position)
123
+
124
+ dynamics = {
125
+ "position": velocity,
126
+ "velocity": ox.Concat(
127
+ -mu * position[0] / r**3,
128
+ -mu * position[1] / r**3,
129
+ ),
130
+ "cost": 0.0,
131
+ }
132
+
133
+ # Discrete dynamics at impulsive nodes: update velocity and accumulate cost.
134
+ # Use a small epsilon inside the norm to avoid NaNs in derivatives at dv = 0.
135
+ eps_impulse = 1e-6
136
+ d_impulse = ox.linalg.Norm(dv + eps_impulse)
137
+
138
+ dynamics_discrete = {
139
+ "position": position,
140
+ "velocity": velocity + dv,
141
+ "cost": cost + d_impulse,
142
+ }
143
+
144
+ # Box constraints on all states
145
+ constraints = []
146
+ for state in states:
147
+ constraints.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
148
+
149
+ time = ox.Time(
150
+ initial=0.0,
151
+ final=T_transfer,
152
+ min=0.0,
153
+ max=T_transfer,
154
+ )
155
+
156
+ problem = Problem(
157
+ dynamics=dynamics,
158
+ dynamics_discrete=dynamics_discrete,
159
+ states=states,
160
+ controls=controls,
161
+ time=time,
162
+ constraints=constraints,
163
+ N=n,
164
+ )
165
+
166
+ problem.discretizer.ode_solver = "Dopri8"
167
+ problem.settings.prp.dt = 10.0
168
+
169
+ plotting_dict = {}
170
+
171
+ if __name__ == "__main__":
172
+ # Analytic Hohmann values from Weber's example for reference
173
+ v_leo = np.sqrt(mu / r_leo)
174
+ v_geo = np.sqrt(mu / r_geo)
175
+ r_p = r_leo
176
+ r_a = r_geo
177
+ h_t = np.sqrt(2 * mu * r_a * r_p / (r_a + r_p))
178
+ v_tp = h_t / r_p
179
+ v_ta = h_t / r_a
180
+
181
+ Delta_v_analytic = abs(v_geo - v_ta) + abs(v_tp - v_leo)
182
+ I_sp = 450.5 # s
183
+ goes_mass = 5192.0 # kg
184
+ Delta_m_analytic = goes_mass * (1.0 - np.exp(-Delta_v_analytic / (I_sp * 9.81e-3)))
185
+
186
+ print(f"Analytic Hohmann Δv ≈ {Delta_v_analytic:.3f} km/s")
187
+ print(f"Analytic propellant mass ≈ {Delta_m_analytic:.3f} kg")
188
+
189
+ problem.initialize()
190
+ results = problem.solve()
191
+ results = problem.post_process()
192
+ results.update(plotting_dict)
193
+
194
+ # Always show standard Plotly diagnostics.
195
+ plot_states(results).show()
196
+ plot_controls(results).show()
197
+ plot_scp_iterations(results).show()
198
+
199
+ server = create_hohmann_transfer_server(
200
+ results,
201
+ r1=r1,
202
+ r2=r2,
203
+ scene_scale=1000.0, # km -> Mm-ish for nicer viewing
204
+ )
205
+ server.sleep_forever()
@@ -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.4.1.dev119'
32
- __version_tuple__ = version_tuple = (0, 4, 1, 'dev119')
31
+ __version__ = version = '0.4.1.dev121'
32
+ __version_tuple__ = version_tuple = (0, 4, 1, 'dev121')
33
33
 
34
- __commit_id__ = commit_id = 'gc981f1ca1'
34
+ __commit_id__ = commit_id = 'g87edfed70'
@@ -130,8 +130,15 @@ class AugmentedLagrangian(AutotuningBase):
130
130
  weights: Normalized initial weights from the algorithm
131
131
  """
132
132
  # Calculate nonlinear penalty for current candidate
133
- nonlinear_cost, nonlinear_penalty, nodal_penalty = self.calculate_nonlinear_penalty(
134
- candidate.x_prop,
133
+ candidate_x_prop = (
134
+ candidate.x_prop_plus[1:] if candidate.x_prop_plus is not None else candidate.x_prop
135
+ )
136
+ (
137
+ nonlinear_cost,
138
+ nonlinear_penalty,
139
+ nodal_penalty,
140
+ ) = self.calculate_nonlinear_penalty(
141
+ candidate_x_prop,
135
142
  candidate.x,
136
143
  candidate.u,
137
144
  state.lam_vc,
@@ -155,18 +162,24 @@ class AugmentedLagrangian(AutotuningBase):
155
162
  lam_prox_k = deepcopy(state.lam_prox)
156
163
 
157
164
  if state.k > 1:
158
- prev_nonlinear_cost, prev_nonlinear_penalty, prev_nodal_penalty = (
159
- self.calculate_nonlinear_penalty(
160
- state.x_prop(),
161
- state.x,
162
- state.u,
163
- state.lam_vc,
164
- state.lam_vb,
165
- state.lam_cost,
166
- nodal_constraints,
167
- params,
168
- settings,
169
- )
165
+ state_x_prop_plus = state.x_prop_plus()
166
+ state_x_prop = (
167
+ state_x_prop_plus[1:] if state_x_prop_plus is not None else state.x_prop()
168
+ )
169
+ (
170
+ prev_nonlinear_cost,
171
+ prev_nonlinear_penalty,
172
+ prev_nodal_penalty,
173
+ ) = self.calculate_nonlinear_penalty(
174
+ state_x_prop,
175
+ state.x,
176
+ state.u,
177
+ state.lam_vc,
178
+ state.lam_vb,
179
+ state.lam_cost,
180
+ nodal_constraints,
181
+ params,
182
+ settings,
170
183
  )
171
184
 
172
185
  J_nonlin_prev = prev_nonlinear_cost + prev_nonlinear_penalty + prev_nodal_penalty
@@ -208,7 +221,7 @@ class AugmentedLagrangian(AutotuningBase):
208
221
  adaptive_state = "Accept Lower"
209
222
 
210
223
  # Update virtual control weight matrix
211
- nu = (settings.sim.inv_S_x @ abs(candidate.x[1:] - candidate.x_prop).T).T
224
+ nu = (settings.sim.inv_S_x @ abs(candidate.x[1:] - candidate_x_prop).T).T
212
225
 
213
226
  # Vectorized update: use mask to select between two update rules
214
227
  mask = nu > self.ep
@@ -7,7 +7,7 @@ during SCP iterations.
7
7
 
8
8
  from abc import ABC, abstractmethod
9
9
  from dataclasses import dataclass, field
10
- from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union
10
+ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set, Tuple, Union
11
11
 
12
12
  import numpy as np
13
13
 
@@ -166,7 +166,11 @@ class CandidateIterate:
166
166
  x: Optional[np.ndarray] = None
167
167
  u: Optional[np.ndarray] = None
168
168
  V: Optional[np.ndarray] = None
169
+ W: Optional[np.ndarray] = None
169
170
  x_prop: Optional[np.ndarray] = None
171
+ x_prop_plus: Optional[np.ndarray] = None
172
+ D_d: Optional[np.ndarray] = None
173
+ E_d: Optional[np.ndarray] = None
170
174
  VC: Optional[np.ndarray] = None
171
175
  TR: Optional[np.ndarray] = None
172
176
  lam_vc: Optional[Union[float, np.ndarray]] = None
@@ -191,9 +195,18 @@ class DiscretizationResult:
191
195
  A_d: np.ndarray # (N-1, n_x, n_x)
192
196
  B_d: np.ndarray # (N-1, n_x, n_u)
193
197
  C_d: np.ndarray # (N-1, n_x, n_u)
198
+ x_prop_plus: Optional[np.ndarray] = None # (N, n_x), discrete dynamics on node states
199
+ D_d: Optional[np.ndarray] = None # (N, n_x, n_x), d(x_prop_plus)/d(x_node)
200
+ E_d: Optional[np.ndarray] = None # (N, n_x, n_u), d(x_prop_plus)/d(u_node)
194
201
 
195
202
  @classmethod
196
- def from_V(cls, V: np.ndarray, n_x: int, n_u: int, N: int) -> "DiscretizationResult":
203
+ def from_V(
204
+ cls,
205
+ V: np.ndarray,
206
+ n_x: int,
207
+ n_u: int,
208
+ N: int,
209
+ ) -> "DiscretizationResult":
197
210
  """Unpack the final timestep of a raw discretization matrix ``V``."""
198
211
  i1, i2 = n_x, n_x + n_x * n_x
199
212
  i3, i4 = i2 + n_x * n_u, i2 + 2 * n_x * n_u
@@ -206,6 +219,37 @@ class DiscretizationResult:
206
219
  C_d=V_final[:, i3:i4].reshape(N - 1, n_x, n_u),
207
220
  )
208
221
 
222
+ @classmethod
223
+ def from_VW(
224
+ cls,
225
+ V: np.ndarray,
226
+ W: np.ndarray,
227
+ n_x: int,
228
+ n_u: int,
229
+ N: int,
230
+ ) -> "DiscretizationResult":
231
+ """Unpack continuous and impulsive discretization blocks from ``V`` and ``W``."""
232
+ base = cls.from_V(V=V, n_x=n_x, n_u=n_u, N=N)
233
+
234
+ W_arr = np.asarray(W)
235
+ i_w = n_x + n_x * n_x + n_x * n_u
236
+ i1 = n_x
237
+ i2 = i1 + n_x * n_x
238
+ i3 = i2 + n_x * n_u
239
+
240
+ W_final = W_arr[:, -1].reshape(-1, i_w)
241
+
242
+ return cls(
243
+ V=base.V,
244
+ x_prop=base.x_prop,
245
+ A_d=base.A_d,
246
+ B_d=base.B_d,
247
+ C_d=base.C_d,
248
+ x_prop_plus=W_final[:, :i1],
249
+ D_d=W_final[:, i1:i2].reshape(W_final.shape[0], n_x, n_x),
250
+ E_d=W_final[:, i2:i3].reshape(W_final.shape[0], n_x, n_u),
251
+ )
252
+
209
253
 
210
254
  class AutotuningBase(ABC):
211
255
  """Base class for autotuning methods in SCP algorithms.
@@ -429,9 +473,25 @@ class AlgorithmState:
429
473
  self.U.append(cand.u)
430
474
 
431
475
  if cand.V is not None:
432
- self.discretizations.append(
433
- DiscretizationResult.from_V(cand.V, n_x=self.n_x, n_u=self.n_u, N=self.N)
434
- )
476
+ if cand.W is not None:
477
+ self.discretizations.append(
478
+ DiscretizationResult.from_VW(
479
+ cand.V,
480
+ cand.W,
481
+ n_x=self.n_x,
482
+ n_u=self.n_u,
483
+ N=self.N,
484
+ )
485
+ )
486
+ else:
487
+ self.discretizations.append(
488
+ DiscretizationResult.from_V(
489
+ cand.V,
490
+ n_x=self.n_x,
491
+ n_u=self.n_u,
492
+ N=self.N,
493
+ )
494
+ )
435
495
  if cand.VC is not None:
436
496
  self.VC_history.append(cand.VC)
437
497
  if cand.TR is not None:
@@ -473,6 +533,24 @@ class AlgorithmState:
473
533
  DiscretizationResult.from_V(V, n_x=self.n_x, n_u=self.n_u, N=self.N)
474
534
  )
475
535
 
536
+ def add_impulsive_discretization(
537
+ self,
538
+ W: np.ndarray,
539
+ ) -> None:
540
+ """Attach impulsive discretization data to the latest discretization entry."""
541
+ if not self.discretizations:
542
+ raise ValueError(
543
+ "Cannot attach impulsive discretization before adding the base discretization."
544
+ )
545
+ last = self.discretizations[-1]
546
+ self.discretizations[-1] = DiscretizationResult.from_VW(
547
+ V=last.V,
548
+ W=W,
549
+ n_x=self.n_x,
550
+ n_u=self.n_u,
551
+ N=self.N,
552
+ )
553
+
476
554
  @property
477
555
  def V_history(self) -> List[np.ndarray]:
478
556
  """Backward-compatible view of raw discretization matrices.
@@ -563,6 +641,24 @@ class AlgorithmState:
563
641
  return None
564
642
  return self.discretizations[index].C_d
565
643
 
644
+ def x_prop_plus(self, index: int = -1) -> np.ndarray:
645
+ """Extract discrete dynamics evaluated at x_prop."""
646
+ if not self.discretizations:
647
+ return None
648
+ return self.discretizations[index].x_prop_plus
649
+
650
+ def D_d(self, index: int = -1) -> np.ndarray:
651
+ """Extract Jacobian of x_prop_plus w.r.t. x_prop."""
652
+ if not self.discretizations:
653
+ return None
654
+ return self.discretizations[index].D_d
655
+
656
+ def E_d(self, index: int = -1) -> np.ndarray:
657
+ """Extract Jacobian of x_prop_plus w.r.t. discrete controls."""
658
+ if not self.discretizations:
659
+ return None
660
+ return self.discretizations[index].E_d
661
+
566
662
  @property
567
663
  def lam_prox(self) -> float:
568
664
  """Get current trust region weight.
@@ -753,6 +849,7 @@ class Algorithm(ABC):
753
849
  emitter: callable,
754
850
  params: dict,
755
851
  settings: "Config",
852
+ discretization_solver_impulsive: Optional[Callable] = None,
756
853
  ) -> None:
757
854
  """Initialize the algorithm and store compiled infrastructure.
758
855
 
@@ -767,6 +864,8 @@ class Algorithm(ABC):
767
864
  emitter: Callback for emitting iteration progress data
768
865
  params: Problem parameters dictionary (for warm-start only)
769
866
  settings: Configuration object (for warm-start only)
867
+ discretization_solver_impulsive: Optional solver for discrete/impulsive
868
+ dynamics evaluated on ``(x_prop, u_discrete)``
770
869
  """
771
870
  raise NotImplementedError
772
871