openscvx 0.4.1.dev140__tar.gz → 0.4.1.dev144__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.dev140/openscvx.egg-info → openscvx-0.4.1.dev144}/PKG-INFO +1 -1
  2. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/_version.py +3 -3
  3. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/algorithms/__init__.py +2 -1
  4. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/algorithms/augmented_lagrangian.py +12 -6
  5. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/algorithms/base.py +181 -25
  6. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/algorithms/penalized_trust_region.py +8 -10
  7. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/cvxpy_variables.py +4 -2
  8. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/problem.py +14 -0
  9. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/propagation/post_processing.py +35 -4
  10. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/solvers/base.py +1 -1
  11. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/solvers/ptr_solver.py +15 -8
  12. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/augmentation.py +43 -1
  13. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/constraint.py +106 -4
  14. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lower.py +4 -2
  15. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144/openscvx.egg-info}/PKG-INFO +1 -1
  16. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_autotuning.py +100 -35
  17. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/assets/logo.svg +0 -0
  18. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/release-drafter.yml +0 -0
  19. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/_docs.yml +0 -0
  20. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/branch-name.yml +0 -0
  21. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/docs.yml +0 -0
  22. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/lint.yml +0 -0
  23. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/nightly.yml +0 -0
  24. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/release-drafter.yml +0 -0
  25. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/release.yml +0 -0
  26. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/tests-integration.yml +0 -0
  27. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.github/workflows/tests-unit.yml +0 -0
  28. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/.gitignore +0 -0
  29. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/CONTRIBUTING.md +0 -0
  30. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/LICENSE +0 -0
  31. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/README.md +0 -0
  32. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/Foundations/constraint_reformulation.md +0 -0
  33. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/Foundations/control_parameterization.md +0 -0
  34. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/Foundations/discretization.md +0 -0
  35. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/Foundations/ocp.md +0 -0
  36. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/Foundations/scvx.md +0 -0
  37. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/Foundations/time_dilation.md +0 -0
  38. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UnderTheHood/lowering_architecture.md +0 -0
  39. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  40. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/00_introduction.md +0 -0
  41. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  42. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  43. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  44. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  45. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/05_visualization.md +0 -0
  46. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/06_logic.md +0 -0
  47. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/UsersGuide/07_lie.md +0 -0
  48. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/favicon.png +0 -0
  49. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/images/ct-scvx_dark.png +0 -0
  50. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/images/ct-scvx_light.png +0 -0
  51. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/images/ctcs_dark.png +0 -0
  52. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/images/ctcs_light.png +0 -0
  53. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/images/problem_class_dark.png +0 -0
  54. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/images/problem_class_light.png +0 -0
  55. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/assets/logo.svg +0 -0
  56. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/citation.md +0 -0
  57. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/examples.md +0 -0
  58. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/getting-started.md +0 -0
  59. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/index.md +0 -0
  60. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/docs/javascripts/mathjax.js +0 -0
  61. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/abstract/brachistochrone.py +0 -0
  62. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/abstract/impulsive.py +0 -0
  63. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/arm/three_link_arm.py +0 -0
  64. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/car/dubins_car.py +0 -0
  65. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/car/dubins_car_conditional.py +0 -0
  66. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/car/dubins_car_disjoint.py +0 -0
  67. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/car/dubins_car_stljax.py +0 -0
  68. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/cinema_vp.py +0 -0
  69. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/dr_double_integrator.py +0 -0
  70. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/dr_vp.py +0 -0
  71. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/dr_vp_nodal.py +0 -0
  72. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/dr_vp_polytope.py +0 -0
  73. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/drone_racing.py +0 -0
  74. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/logo.py +0 -0
  75. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/logo_utils/acl_logo.svg +0 -0
  76. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/logo_utils/svg_path_utils.py +0 -0
  77. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/obstacle_avoidance.py +0 -0
  78. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  79. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  80. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/plotting.py +0 -0
  81. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/plotting_viser.py +0 -0
  82. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
  83. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
  84. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
  85. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/realtime/cinema_vp_realtime.py +0 -0
  86. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/realtime/drone_racing_realtime.py +0 -0
  87. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/realtime/dubins_car_realtime.py +0 -0
  88. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  89. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/rocket/3DoF_pdg.py +0 -0
  90. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/spacecraft/hohmann_transfer.py +0 -0
  91. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/examples/spacecraft/proxops_cw.py +0 -0
  92. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/ctlos_cine.gif +0 -0
  93. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/ctlos_dr.gif +0 -0
  94. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/dtlos_cine.gif +0 -0
  95. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/dtlos_dr.gif +0 -0
  96. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/openscvx_logo.svg +0 -0
  97. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/openscvx_logo_square.png +0 -0
  98. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/oscvx_structure_full_dark.svg +0 -0
  99. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/figures/video_preview.png +0 -0
  100. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/__init__.py +0 -0
  101. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/1-background.avif +0 -0
  102. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
  103. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
  104. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
  105. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
  106. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/2-mars.avif +0 -0
  107. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
  108. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
  109. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
  110. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
  111. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/3-moon.avif +0 -0
  112. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
  113. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
  114. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
  115. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
  116. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
  117. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
  118. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
  119. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
  120. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
  121. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/5-space.avif +0 -0
  122. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
  123. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
  124. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
  125. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
  126. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/6-earth.avif +0 -0
  127. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
  128. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
  129. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
  130. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
  131. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/javascripts/parallax.js +0 -0
  132. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/logo.svg +0 -0
  133. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/stylesheets/custom.css +0 -0
  134. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/assets/stylesheets/parallax.css +0 -0
  135. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/home.html +0 -0
  136. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/main.html +0 -0
  137. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/partials/parallax/hero.html +0 -0
  138. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/material/overrides/partials/parallax.html +0 -0
  139. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/mkdocs.yml +0 -0
  140. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/__init__.py +0 -0
  141. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/__main__.py +0 -0
  142. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/algorithms/constant_proximal_weight.py +0 -0
  143. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/algorithms/optimization_results.py +0 -0
  144. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/algorithms/ramp_proximal_weight.py +0 -0
  145. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/config.py +0 -0
  146. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/discretization/__init__.py +0 -0
  147. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/discretization/base.py +0 -0
  148. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/discretization/linearize_discretize.py +0 -0
  149. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/expert/__init__.py +0 -0
  150. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/expert/byof.py +0 -0
  151. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/expert/lowering.py +0 -0
  152. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/expert/validation.py +0 -0
  153. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/init/__init__.py +0 -0
  154. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/init/interpolation.py +0 -0
  155. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/integrators/__init__.py +0 -0
  156. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/integrators/runge_kutta.py +0 -0
  157. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/loader.py +0 -0
  158. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/__init__.py +0 -0
  159. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/cvxpy_constraints.py +0 -0
  160. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/dynamics.py +0 -0
  161. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/jax_constraints.py +0 -0
  162. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/parameters.py +0 -0
  163. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/problem.py +0 -0
  164. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/lowered/unified.py +0 -0
  165. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/__init__.py +0 -0
  166. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/plotting.py +0 -0
  167. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/scp_iteration.py +0 -0
  168. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/viser/__init__.py +0 -0
  169. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/viser/animated.py +0 -0
  170. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/viser/orbits.py +0 -0
  171. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/viser/plotly_integration.py +0 -0
  172. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/viser/primitives.py +0 -0
  173. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/viser/scp.py +0 -0
  174. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/plotting/viser/server.py +0 -0
  175. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/propagation/__init__.py +0 -0
  176. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/propagation/propagation.py +0 -0
  177. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/solvers/__init__.py +0 -0
  178. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/__init__.py +0 -0
  179. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/builder.py +0 -0
  180. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/constraint_set.py +0 -0
  181. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/__init__.py +0 -0
  182. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/arithmetic.py +0 -0
  183. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/array.py +0 -0
  184. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/control.py +0 -0
  185. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/expr.py +0 -0
  186. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  187. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  188. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/lie/se3.py +0 -0
  189. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/lie/so3.py +0 -0
  190. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/linalg.py +0 -0
  191. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/logic.py +0 -0
  192. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/math.py +0 -0
  193. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/spatial.py +0 -0
  194. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/state.py +0 -0
  195. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/stl.py +0 -0
  196. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/time.py +0 -0
  197. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/variable.py +0 -0
  198. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/expr/vmap.py +0 -0
  199. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/hashing.py +0 -0
  200. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/__init__.py +0 -0
  201. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  202. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  203. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  204. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  205. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  206. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  207. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  208. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  209. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  210. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  211. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  212. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  213. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  214. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  215. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  216. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  217. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  218. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
  219. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  220. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  221. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  222. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  223. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
  224. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  225. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  226. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  227. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  228. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  229. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/__init__.py +0 -0
  230. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/_registry.py +0 -0
  231. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/array.py +0 -0
  232. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/constraint.py +0 -0
  233. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/lie.py +0 -0
  234. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/linalg.py +0 -0
  235. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/logic.py +0 -0
  236. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/math.py +0 -0
  237. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/parser.py +0 -0
  238. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/spatial.py +0 -0
  239. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/stl.py +0 -0
  240. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/parser/tokenizer.py +0 -0
  241. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/preprocessing.py +0 -0
  242. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/problem.py +0 -0
  243. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/sparsity.py +0 -0
  244. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/symbolic/unified.py +0 -0
  245. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/utils/__init__.py +0 -0
  246. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/utils/cache.py +0 -0
  247. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/utils/caching.py +0 -0
  248. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/utils/printing.py +0 -0
  249. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/utils/profiling.py +0 -0
  250. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx/utils/utils.py +0 -0
  251. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx.egg-info/SOURCES.txt +0 -0
  252. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx.egg-info/dependency_links.txt +0 -0
  253. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx.egg-info/entry_points.txt +0 -0
  254. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx.egg-info/requires.txt +0 -0
  255. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/openscvx.egg-info/top_level.txt +0 -0
  256. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/pyproject.toml +0 -0
  257. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/scripts/gen_example_pages.py +0 -0
  258. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/scripts/gen_ref_pages.py +0 -0
  259. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/setup.cfg +0 -0
  260. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/__init__.py +0 -0
  261. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/brachistochrone_analytical.py +0 -0
  262. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/fixtures/brachistochrone.json +0 -0
  263. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/fixtures/brachistochrone.yaml +0 -0
  264. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/hohmann_analytical.py +0 -0
  265. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/__init__.py +0 -0
  266. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/__init__.py +0 -0
  267. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_arithmetic.py +0 -0
  268. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_array.py +0 -0
  269. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_constraint.py +0 -0
  270. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_expr.py +0 -0
  271. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_lie.py +0 -0
  272. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_linalg.py +0 -0
  273. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_logic.py +0 -0
  274. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_math.py +0 -0
  275. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_node_reference.py +0 -0
  276. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_parameters.py +0 -0
  277. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_scaling.py +0 -0
  278. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_spatial.py +0 -0
  279. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_variable.py +0 -0
  280. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/expr/test_vmap.py +0 -0
  281. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/__init__.py +0 -0
  282. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_array.py +0 -0
  283. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_constraint.py +0 -0
  284. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_lie.py +0 -0
  285. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_linalg.py +0 -0
  286. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_load.py +0 -0
  287. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_logic.py +0 -0
  288. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_math.py +0 -0
  289. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_parser.py +0 -0
  290. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_spatial.py +0 -0
  291. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_stl.py +0 -0
  292. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_tokenizer.py +0 -0
  293. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/parser/test_vmap.py +0 -0
  294. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/test_augmentation.py +0 -0
  295. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/test_hashing.py +0 -0
  296. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/test_lower_cvxpy.py +0 -0
  297. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/test_lower_jax.py +0 -0
  298. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/test_preprocessing.py +0 -0
  299. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/test_sparsity.py +0 -0
  300. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/symbolic/test_unified.py +0 -0
  301. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_brachistochrone.py +0 -0
  302. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_cvxpygen_optional.py +0 -0
  303. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_discretization.py +0 -0
  304. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_examples.py +0 -0
  305. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_expert.py +0 -0
  306. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_impulsive.py +0 -0
  307. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_init.py +0 -0
  308. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_integrators.py +0 -0
  309. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_loader.py +0 -0
  310. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_optimization_results.py +0 -0
  311. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/tests/test_plotting.py +0 -0
  312. {openscvx-0.4.1.dev140 → openscvx-0.4.1.dev144}/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.dev140
3
+ Version: 0.4.1.dev144
4
4
  Summary: A general Python-based successive convexification implementation which uses a JAX backend.
5
5
  Author-email: Chris Hayner and Griffin Norris <haynec@uw.edu>
6
6
  License: Apache Software License
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.4.1.dev140'
32
- __version_tuple__ = version_tuple = (0, 4, 1, 'dev140')
31
+ __version__ = version = '0.4.1.dev144'
32
+ __version_tuple__ = version_tuple = (0, 4, 1, 'dev144')
33
33
 
34
- __commit_id__ = commit_id = 'gff491002c'
34
+ __commit_id__ = commit_id = 'g137ef78b8'
@@ -50,7 +50,8 @@ Note:
50
50
  lam_prox: float
51
51
  lam_cost: float
52
52
  lam_vc: ...
53
- lam_vb: float
53
+ lam_vb_nodal: np.ndarray # (N, n_nodal)
54
+ lam_vb_cross: np.ndarray # (n_cross,)
54
55
 
55
56
  @dataclass
56
57
  class TrajectoryHistory:
@@ -164,7 +164,8 @@ class AugmentedLagrangian(AutotuningBase):
164
164
  candidate.x,
165
165
  candidate.u,
166
166
  state.lam_vc,
167
- state.lam_vb,
167
+ state.lam_vb_nodal,
168
+ state.lam_vb_cross,
168
169
  state.lam_cost,
169
170
  nodal_constraints,
170
171
  params,
@@ -197,7 +198,8 @@ class AugmentedLagrangian(AutotuningBase):
197
198
  state.x,
198
199
  state.u,
199
200
  state.lam_vc,
200
- state.lam_vb,
201
+ state.lam_vb_nodal,
202
+ state.lam_vb_cross,
201
203
  state.lam_cost,
202
204
  nodal_constraints,
203
205
  params,
@@ -232,7 +234,8 @@ class AugmentedLagrangian(AutotuningBase):
232
234
  candidate.lam_vc = self._update_virtual_control_weights(
233
235
  candidate, candidate_x_prop, settings, state.lam_vc, state.lam_prox
234
236
  )
235
- candidate.lam_vb = weights.lam_vb
237
+ candidate.lam_vb_nodal = state.lam_vb_nodal
238
+ candidate.lam_vb_cross = state.lam_vb_cross
236
239
 
237
240
  state.accept_solution(candidate)
238
241
  adaptive_state = "Accept Higher"
@@ -245,7 +248,8 @@ class AugmentedLagrangian(AutotuningBase):
245
248
  candidate.lam_vc = self._update_virtual_control_weights(
246
249
  candidate, candidate_x_prop, settings, state.lam_vc, state.lam_prox
247
250
  )
248
- candidate.lam_vb = weights.lam_vb
251
+ candidate.lam_vb_nodal = state.lam_vb_nodal
252
+ candidate.lam_vb_cross = state.lam_vb_cross
249
253
 
250
254
  state.accept_solution(candidate)
251
255
  adaptive_state = "Accept Constant"
@@ -257,14 +261,16 @@ class AugmentedLagrangian(AutotuningBase):
257
261
  candidate.lam_vc = self._update_virtual_control_weights(
258
262
  candidate, candidate_x_prop, settings, state.lam_vc, state.lam_prox
259
263
  )
260
- candidate.lam_vb = weights.lam_vb
264
+ candidate.lam_vb_nodal = state.lam_vb_nodal
265
+ candidate.lam_vb_cross = state.lam_vb_cross
261
266
  state.accept_solution(candidate)
262
267
  adaptive_state = "Accept Lower"
263
268
 
264
269
  else:
265
270
  state.lam_prox_history.append(lam_prox_k)
266
271
  candidate.lam_vc = state.lam_vc
267
- candidate.lam_vb = weights.lam_vb
272
+ candidate.lam_vb_nodal = state.lam_vb_nodal
273
+ candidate.lam_vb_cross = state.lam_vb_cross
268
274
  state.accept_solution(candidate)
269
275
  adaptive_state = "Initial"
270
276
 
@@ -124,22 +124,38 @@ class Weights:
124
124
  lam_vc: Virtual control penalty weight (normalized).
125
125
  lam_cost: Cost weight per state (normalized). Scalar or array of
126
126
  shape ``(n_states,)`` for per-state weighting.
127
- lam_vb: Virtual buffer penalty weight (normalized).
127
+ lam_vb: Global virtual buffer penalty weight (normalized). Scalar
128
+ default applied to every constraint. Use ``.weight()`` on
129
+ individual constraints for per-constraint or per-node overrides.
130
+ lam_vb_nodal: Virtual buffer penalty weights for nodal constraints
131
+ (normalized), shape ``(N, n_nodal)``. Set by
132
+ :meth:`set_vb_arrays`.
133
+ lam_vb_cross: Virtual buffer penalty weights for cross-node
134
+ constraints (normalized), shape ``(n_cross,)``. Set by
135
+ :meth:`set_vb_arrays`.
128
136
  """
129
137
 
130
138
  lam_prox: float = 1e0
131
139
  lam_vc: float = 1e1
132
140
  lam_cost: Union[float, np.ndarray] = 1e-1
133
141
  lam_vb: float = 0.0
142
+ lam_vb_nodal: Optional[np.ndarray] = None
143
+ lam_vb_cross: Optional[np.ndarray] = None
134
144
 
135
145
  def __post_init__(self):
146
+ # Coerce lists/lists-of-lists to numpy arrays.
147
+ if isinstance(self.lam_cost, (list, tuple)):
148
+ self.lam_cost = np.asarray(self.lam_cost, dtype=float)
149
+
136
150
  # Snapshot the user-specified values so normalize() is idempotent.
137
151
  self._raw_lam_prox = self.lam_prox
138
152
  self._raw_lam_vc = self.lam_vc
139
153
  self._raw_lam_cost = (
140
154
  self.lam_cost.copy() if isinstance(self.lam_cost, np.ndarray) else self.lam_cost
141
155
  )
142
- self._raw_lam_vb = self.lam_vb
156
+ self._raw_lam_vb = float(self.lam_vb)
157
+ self._raw_lam_vb_nodal: Optional[np.ndarray] = None
158
+ self._raw_lam_vb_cross: Optional[np.ndarray] = None
143
159
 
144
160
  def normalize(self) -> None:
145
161
  """Normalize weights so the largest equals 1.0.
@@ -153,12 +169,42 @@ class Weights:
153
169
  if isinstance(self._raw_lam_cost, np.ndarray)
154
170
  else self._raw_lam_cost
155
171
  )
156
- scale = max(self._raw_lam_prox, self._raw_lam_vc, raw_cost_max, self._raw_lam_vb)
172
+ if self._raw_lam_vb_nodal is not None:
173
+ raw_vb_max = max(
174
+ float(np.max(self._raw_lam_vb_nodal)),
175
+ float(np.max(self._raw_lam_vb_cross)),
176
+ )
177
+ else:
178
+ raw_vb_max = self._raw_lam_vb
179
+ scale = max(self._raw_lam_prox, self._raw_lam_vc, raw_cost_max, raw_vb_max)
157
180
  if scale > 0:
158
181
  self.lam_prox = self._raw_lam_prox / scale
159
182
  self.lam_vc = self._raw_lam_vc / scale
160
183
  self.lam_cost = self._raw_lam_cost / scale
161
184
  self.lam_vb = self._raw_lam_vb / scale
185
+ if self._raw_lam_vb_nodal is not None:
186
+ self.lam_vb_nodal = self._raw_lam_vb_nodal / scale
187
+ self.lam_vb_cross = self._raw_lam_vb_cross / scale
188
+
189
+ def set_vb_arrays(
190
+ self,
191
+ lam_vb_nodal: np.ndarray,
192
+ lam_vb_cross: np.ndarray,
193
+ ) -> None:
194
+ """Set pre-built virtual buffer weight arrays.
195
+
196
+ Stores the given arrays as both the current (normalized) and raw
197
+ (pre-normalization) values. Call :meth:`normalize` afterwards to
198
+ rescale all weights consistently.
199
+
200
+ Args:
201
+ lam_vb_nodal: Weight array of shape ``(N, n_nodal)``.
202
+ lam_vb_cross: Weight array of shape ``(n_cross,)``.
203
+ """
204
+ self.lam_vb_nodal = lam_vb_nodal
205
+ self.lam_vb_cross = lam_vb_cross
206
+ self._raw_lam_vb_nodal = lam_vb_nodal.copy()
207
+ self._raw_lam_vb_cross = lam_vb_cross.copy()
162
208
 
163
209
 
164
210
  @dataclass
@@ -175,7 +221,8 @@ class CandidateIterate:
175
221
  TR: Optional[np.ndarray] = None
176
222
  lam_vc: Optional[Union[float, np.ndarray]] = None
177
223
  lam_cost: Optional[Union[float, np.ndarray]] = None
178
- lam_vb: Optional[float] = None
224
+ lam_vb_nodal: Optional[np.ndarray] = None
225
+ lam_vb_cross: Optional[np.ndarray] = None
179
226
  J_lin: Optional[float] = None
180
227
  J_nonlin: Optional[float] = None
181
228
 
@@ -306,7 +353,8 @@ class AutotuningBase(ABC):
306
353
  x_bar: np.ndarray,
307
354
  u_bar: np.ndarray,
308
355
  lam_vc: np.ndarray,
309
- lam_vb: float,
356
+ lam_vb_nodal: np.ndarray,
357
+ lam_vb_cross: np.ndarray,
310
358
  lam_cost: Union[float, np.ndarray],
311
359
  nodal_constraints: "LoweredJaxConstraints",
312
360
  params: dict,
@@ -324,7 +372,10 @@ class AutotuningBase(ABC):
324
372
  x_bar: Previous iteration state (n_nodes, n_states)
325
373
  u_bar: Solution control (n_nodes, n_controls)
326
374
  lam_vc: Virtual control weight (scalar or matrix)
327
- lam_vb: Virtual buffer penalty weight (scalar)
375
+ lam_vb_nodal: Virtual buffer penalty weights for nodal
376
+ constraints, shape ``(N, n_nodal)``.
377
+ lam_vb_cross: Virtual buffer penalty weights for cross-node
378
+ constraints, shape ``(n_cross,)``.
328
379
  lam_cost: Cost weight. Scalar (applied uniformly) or
329
380
  array of shape ``(n_states,)`` for per-state weighting.
330
381
  nodal_constraints: Lowered JAX constraints
@@ -340,28 +391,28 @@ class AutotuningBase(ABC):
340
391
  nodal_penalty = 0.0
341
392
 
342
393
  # Evaluate nodal constraints
343
- for constraint in nodal_constraints.nodal:
394
+ for idx, constraint in enumerate(nodal_constraints.nodal):
344
395
  # Nodal constraint function is vmapped: func(x, u, node, params)
345
396
  # When called with arrays, it evaluates at all nodes
346
397
  g = constraint.func(x_bar, u_bar, 0, params)
347
398
  # Only sum violations at nodes where constraint is enforced
348
399
  if constraint.nodes is not None:
349
- # Filter to only specified nodes
350
- # Convert to numpy array for JAX compatibility
351
400
  nodes_array = np.array(constraint.nodes)
352
401
  g_filtered = g[nodes_array]
402
+ w = lam_vb_nodal[nodes_array, idx]
353
403
  else:
354
- # If no nodes specified, check all nodes
355
404
  g_filtered = g
356
- nodal_penalty += lam_vb * np.sum(np.maximum(0, g_filtered))
405
+ w = lam_vb_nodal[:, idx]
406
+ nodal_penalty += np.sum(w * np.maximum(0, g_filtered))
357
407
 
358
408
  # Evaluate cross-node constraints
359
- for constraint in nodal_constraints.cross_node:
409
+ for idx, constraint in enumerate(nodal_constraints.cross_node):
410
+ w = lam_vb_cross[idx]
360
411
  # Cross-node constraint function signature: func(X, U, params)
361
412
  # No node argument - operates on full trajectory
362
413
  g = constraint.func(x_bar, u_bar, params)
363
414
  # Cross-node constraints return scalar or array, sum all violations
364
- nodal_penalty += lam_vb * np.sum(np.maximum(0, g))
415
+ nodal_penalty += w * np.sum(np.maximum(0, g))
365
416
 
366
417
  # lam_cost weighting is applied inside calculate_cost_from_state,
367
418
  # so the returned cost is already weighted (no outer multiplication).
@@ -421,7 +472,8 @@ class AlgorithmState:
421
472
  lam_prox: Current trust region weight (may adapt during solve)
422
473
  lam_cost: Current cost weight (may relax during solve)
423
474
  lam_vc: Current virtual control penalty weight
424
- lam_vb: Current virtual buffer penalty weight
475
+ lam_vb_nodal: Current per-node nodal virtual buffer penalty weights
476
+ lam_vb_cross: Current cross-node virtual buffer penalty weights
425
477
  n_x: Number of states (for unpacking V vectors)
426
478
  n_u: Number of controls (for unpacking V vectors)
427
479
  N: Number of trajectory nodes (for unpacking V vectors)
@@ -455,7 +507,8 @@ class AlgorithmState:
455
507
  TR_history: List[np.ndarray] = field(default_factory=list)
456
508
  lam_vc_history: List[Union[float, np.ndarray]] = field(default_factory=list)
457
509
  lam_cost_history: List[Union[float, np.ndarray]] = field(default_factory=list)
458
- lam_vb_history: List[float] = field(default_factory=list)
510
+ lam_vb_nodal_history: List[np.ndarray] = field(default_factory=list)
511
+ lam_vb_cross_history: List[np.ndarray] = field(default_factory=list)
459
512
  lam_prox_history: List[float] = field(default_factory=list)
460
513
  x_full: List[np.ndarray] = field(default_factory=list)
461
514
  x_prop_full: List[np.ndarray] = field(default_factory=list)
@@ -501,8 +554,10 @@ class AlgorithmState:
501
554
  self.lam_vc_history.append(cand.lam_vc)
502
555
  if cand.lam_cost is not None:
503
556
  self.lam_cost_history.append(cand.lam_cost)
504
- if cand.lam_vb is not None:
505
- self.lam_vb_history.append(cand.lam_vb)
557
+ if cand.lam_vb_nodal is not None:
558
+ self.lam_vb_nodal_history.append(cand.lam_vb_nodal)
559
+ if cand.lam_vb_cross is not None:
560
+ self.lam_vb_cross_history.append(cand.lam_vb_cross)
506
561
 
507
562
  if cand.J_nonlin is not None:
508
563
  self.J_nonlin_history.append(cand.J_nonlin)
@@ -694,18 +749,37 @@ class AlgorithmState:
694
749
  return self.lam_vc_history[-1]
695
750
 
696
751
  @property
697
- def lam_vb(self) -> float:
698
- """Get current virtual buffer penalty weight.
752
+ def lam_vb_nodal(self) -> np.ndarray:
753
+ """Get current virtual buffer penalty weights for nodal constraints.
754
+
755
+ Returns:
756
+ Array of shape ``(N, n_nodal_constraints)``.
757
+ """
758
+ if not self.lam_vb_nodal_history:
759
+ raise ValueError(
760
+ "lam_vb_nodal_history is empty. Initialize state using from_settings()."
761
+ )
762
+ return self.lam_vb_nodal_history[-1]
763
+
764
+ @property
765
+ def lam_vb_cross(self) -> np.ndarray:
766
+ """Get current virtual buffer penalty weights for cross-node constraints.
699
767
 
700
768
  Returns:
701
- Current virtual buffer penalty weight (latest entry in lam_vb_history)
769
+ Array of shape ``(n_cross_node_constraints,)``.
702
770
  """
703
- if not self.lam_vb_history:
704
- raise ValueError("lam_vb_history is empty. Initialize state using from_settings().")
705
- return self.lam_vb_history[-1]
771
+ if not self.lam_vb_cross_history:
772
+ raise ValueError(
773
+ "lam_vb_cross_history is empty. Initialize state using from_settings()."
774
+ )
775
+ return self.lam_vb_cross_history[-1]
706
776
 
707
777
  @classmethod
708
- def from_settings(cls, settings: "Config", weights: "Weights") -> "AlgorithmState":
778
+ def from_settings(
779
+ cls,
780
+ settings: "Config",
781
+ weights: "Weights",
782
+ ) -> "AlgorithmState":
709
783
  """Create initial algorithm state from configuration.
710
784
 
711
785
  Copies only the trajectory arrays from settings, leaving all metadata
@@ -749,7 +823,8 @@ class AlgorithmState:
749
823
  TR_history=[],
750
824
  lam_vc_history=[lam_vc_array],
751
825
  lam_cost_history=[lam_cost_init],
752
- lam_vb_history=[weights.lam_vb],
826
+ lam_vb_nodal_history=[weights.lam_vb_nodal.copy()],
827
+ lam_vb_cross_history=[weights.lam_vb_cross.copy()],
753
828
  lam_prox_history=[weights.lam_prox],
754
829
  )
755
830
 
@@ -840,6 +915,87 @@ class Algorithm(ABC):
840
915
  return _expand_lam_cost_dict(lam_cost, states)
841
916
  return lam_cost
842
917
 
918
+ def _resolve_lam_vb(
919
+ self,
920
+ N: int,
921
+ nodal_constraints: list,
922
+ cross_node_constraints: list,
923
+ n_byof_nodal: int = 0,
924
+ n_byof_cross: int = 0,
925
+ ) -> None:
926
+ """Resolve per-constraint virtual buffer weight arrays and re-normalize.
927
+
928
+ Inspects each symbolic constraint's shape (to account for vector
929
+ decomposition) and ``.weight()`` overrides, populates
930
+ ``weights.lam_vb_nodal`` and ``weights.lam_vb_cross``, then
931
+ re-normalizes all weights so the overrides participate in the scale.
932
+
933
+ Args:
934
+ N: Number of trajectory nodes.
935
+ nodal_constraints: Symbolic ``NodalConstraint`` objects (post-
936
+ preprocessing, pre-lowering).
937
+ cross_node_constraints: Symbolic ``CrossNodeConstraint`` objects.
938
+ n_byof_nodal: Number of byof nodal constraints (each adds one
939
+ column with the default weight).
940
+ n_byof_cross: Number of byof cross-node constraints (each adds
941
+ one entry with the default weight).
942
+ """
943
+ default_vb = float(self.weights._raw_lam_vb)
944
+
945
+ # Count decomposed nodal constraints (vector → multiple scalars).
946
+ # Vector constraints are decomposed element-wise during lowering
947
+ # (see decompose_vector_nodal_constraints), so each element gets its
948
+ # own column. We mirror that here via check_shape() to ensure the
949
+ # array dimensions match the post-decomposition constraint count.
950
+ n_nodal = 0
951
+ for nc in nodal_constraints:
952
+ shape = nc.constraint.lhs.check_shape()
953
+ n_nodal += int(np.prod(shape)) if len(shape) > 0 else 1
954
+
955
+ # Byof constraints are scalar (one column each), added after symbolic.
956
+ n_nodal += n_byof_nodal
957
+ n_cross = len(cross_node_constraints) + n_byof_cross
958
+
959
+ # max(..., 1) avoids size-0 CVXPy parameters.
960
+ n_nodal_param = max(n_nodal, 1)
961
+ n_cross_param = max(n_cross, 1)
962
+
963
+ lam_vb_nodal = np.full((N, n_nodal_param), default_vb)
964
+ lam_vb_cross = np.full(n_cross_param, default_vb)
965
+
966
+ # Apply per-constraint .weight() overrides for nodal constraints.
967
+ col = 0
968
+ for nc in nodal_constraints:
969
+ shape = nc.constraint.lhs.check_shape()
970
+ n_elem = int(np.prod(shape)) if len(shape) > 0 else 1
971
+
972
+ w = nc._lam_vb
973
+ if w is not None:
974
+ nodes = nc.nodes if nc.nodes is not None else list(range(N))
975
+ if isinstance(w, (int, float)):
976
+ lam_vb_nodal[nodes, col : col + n_elem] = float(w)
977
+ elif isinstance(w, np.ndarray):
978
+ if w.ndim == 1:
979
+ # (n_elem,) — broadcast across nodes
980
+ for i in range(n_elem):
981
+ val = float(w[0]) if len(w) == 1 else float(w[i])
982
+ lam_vb_nodal[nodes, col + i] = val
983
+ elif w.ndim == 2:
984
+ # (n_nodes, n_elem) — per-node-per-element
985
+ for i in range(n_elem):
986
+ c_i = 0 if w.shape[1] == 1 else i
987
+ lam_vb_nodal[nodes, col + i] = w[:, c_i]
988
+
989
+ col += n_elem
990
+
991
+ # Apply per-constraint .weight() overrides for cross-node constraints.
992
+ for idx, cc in enumerate(cross_node_constraints):
993
+ if cc._lam_vb is not None:
994
+ lam_vb_cross[idx] = float(cc._lam_vb)
995
+
996
+ self.weights.set_vb_arrays(lam_vb_nodal, lam_vb_cross)
997
+ self.weights.normalize()
998
+
843
999
  @abstractmethod
844
1000
  def initialize(
845
1001
  self,
@@ -241,21 +241,18 @@ class PenalizedTrustRegion(Algorithm):
241
241
 
242
242
  @property
243
243
  def lam_vb(self) -> float:
244
- """Virtual buffer penalty weight.
244
+ """Global virtual buffer penalty weight (pre-normalization).
245
245
 
246
- This is the user-specified value before normalization. Setting this
247
- property triggers automatic re-normalization of all weights.
248
-
249
- !!! note
250
- The autotuner may modify the normalized weight in
251
- ``self.weights.lam_vb`` during iteration. Those changes are
252
- internal and do not alter the value returned here.
246
+ This is the user-specified scalar default before normalization.
247
+ Setting this property triggers automatic re-normalization.
248
+ Per-constraint overrides are set via ``.weight()`` on individual
249
+ constraints.
253
250
  """
254
251
  return self.weights._raw_lam_vb
255
252
 
256
253
  @lam_vb.setter
257
254
  def lam_vb(self, value: float) -> None:
258
- self.weights._raw_lam_vb = value
255
+ self.weights._raw_lam_vb = float(value)
259
256
  self.weights.normalize()
260
257
 
261
258
  def get_columns(self, verbosity: int = Verbosity.STANDARD) -> List[Column]:
@@ -614,7 +611,8 @@ class PenalizedTrustRegion(Algorithm):
614
611
  lam_prox=state.lam_prox,
615
612
  lam_cost=state.lam_cost,
616
613
  lam_vc=state.lam_vc,
617
- lam_vb=state.lam_vb,
614
+ lam_vb_nodal=state.lam_vb_nodal,
615
+ lam_vb_cross=state.lam_vb_cross,
618
616
  )
619
617
 
620
618
  # Solve the convex subproblem
@@ -31,7 +31,8 @@ class CVXPyVariables:
31
31
  lam_prox: Trust region weight parameter (scalar, nonneg)
32
32
  lam_cost: Cost function weight parameter (n_states, nonneg)
33
33
  lam_vc: Virtual control penalty weights (N-1 x n_states, nonneg)
34
- lam_vb: Virtual buffer penalty weight (scalar, nonneg)
34
+ lam_vb_nodal: Virtual buffer penalty weights for nodal constraints (N x n_nodal, nonneg)
35
+ lam_vb_cross: Virtual buffer penalty weights for cross-node constraints (n_cross, nonneg)
35
36
 
36
37
  x: State variable (N x n_states)
37
38
  dx: State error variable (N x n_states)
@@ -79,7 +80,8 @@ class CVXPyVariables:
79
80
  lam_prox: "cp.Parameter"
80
81
  lam_cost: "cp.Parameter"
81
82
  lam_vc: "cp.Parameter"
82
- lam_vb: "cp.Parameter"
83
+ lam_vb_nodal: "cp.Parameter"
84
+ lam_vb_cross: "cp.Parameter"
83
85
 
84
86
  # State variables and parameters
85
87
  x: "cp.Variable"
@@ -285,6 +285,20 @@ class Problem:
285
285
  )
286
286
  self._algorithm = algorithm
287
287
 
288
+ # Build per-constraint lam_vb arrays from symbolic constraints and
289
+ # re-normalize so that per-constraint .weight() overrides participate
290
+ # in the normalization scale. Include byof constraint counts so the
291
+ # arrays are sized to match the post-lowering constraint list.
292
+ n_byof_nodal = len(byof.get("nodal_constraints", [])) if byof else 0
293
+ n_byof_cross = len(byof.get("cross_nodal_constraints", [])) if byof else 0
294
+ self._algorithm._resolve_lam_vb(
295
+ N=self.symbolic.N,
296
+ nodal_constraints=self.symbolic.constraints.nodal,
297
+ cross_node_constraints=self.symbolic.constraints.cross_node,
298
+ n_byof_nodal=n_byof_nodal,
299
+ n_byof_cross=n_byof_cross,
300
+ )
301
+
288
302
  # Resolve discretizer: None → default, dict → LinearizeDiscretize(**dict), instance → use
289
303
  if discretizer is None:
290
304
  self._discretizer = LinearizeDiscretize()
@@ -26,6 +26,12 @@ def propagate_trajectory_results(
26
26
  This function takes the optimal control solution and propagates it through the
27
27
  nonlinear dynamics to compute the actual state trajectory and other metrics.
28
28
 
29
+ When ``states_prop`` includes propagation-only states (e.g. via ``dynamics_prop`` /
30
+ ``states_prop``), ``x_full`` has shape ``(n_times, n_prop_states)`` with
31
+ ``n_prop_states > n_opt_states``. The discrete dynamics and cost use only the
32
+ optimization-state portion; propagation-only states are preserved from the last
33
+ propagated step and included in ``trajectory``.
34
+
29
35
  Args:
30
36
  params (dict): System parameters.
31
37
  settings (Config): Configuration settings.
@@ -100,14 +106,39 @@ def propagate_trajectory_results(
100
106
  settings.sim.x_prop = original_x_prop
101
107
 
102
108
  # Calculate cost using utility function and metadata from settings
109
+ # dynamics_discrete operates on optimization states only; when propagation has
110
+ # extra states, pass only the opt-state portion and then reattach the prop-only tail
111
+ x_minus = np.asarray(x_full[-1, :n_opt_states])
112
+ x_plus = np.asarray(
113
+ dynamics_discrete(
114
+ x_minus,
115
+ np.asarray(u[-1]),
116
+ int(settings.sim.n - 1),
117
+ params,
118
+ )
119
+ ).reshape(-1)
120
+ if n_prop_states > n_opt_states:
121
+ # Preserve propagation-only states (not updated by discrete dynamics)
122
+ full_final = np.concatenate([x_plus, np.asarray(x_full[-1, n_opt_states:])], axis=0)
123
+ else:
124
+ full_final = x_plus
125
+ x_for_cost = np.concatenate([x_full[:-1], full_final[None, :]], axis=0)
126
+
103
127
  cost = calculate_cost_from_boundaries(
104
- x_full, settings.sim.x.initial_type, settings.sim.x.final_type
128
+ x_for_cost[:, :n_opt_states],
129
+ settings.sim.x.initial_type,
130
+ settings.sim.x.final_type,
105
131
  )
106
132
 
107
- # Calculate CTCS constraint violation
108
- ctcs_violation = x_full[-1, settings.sim.ctcs_slice_prop]
133
+ # Calculate CTCS constraint violation (use state after final impulse when applicable)
134
+ if dynamics_discrete is not None and np.any(settings.sim.u.is_impulsive):
135
+ ctcs_violation = full_final[settings.sim.ctcs_slice_prop]
136
+ else:
137
+ ctcs_violation = x_full[-1, settings.sim.ctcs_slice_prop]
109
138
 
110
- # Build trajectory dictionary with all states and controls
139
+ # Build trajectory dictionary with all states and controls.
140
+ # result._states is states_prop (opt + propagation-only); each state._slice
141
+ # indexes into the full propagation state, so propagation-only states are included.
111
142
  trajectory_dict = {}
112
143
 
113
144
  # Add all states (user-defined and augmented)
@@ -79,7 +79,7 @@ class ConvexSolver(ABC):
79
79
  ...
80
80
 
81
81
  def update_penalties(self, **kwargs):
82
- # Set lam_prox, lam_cost, lam_vc, lam_vb
82
+ # Set lam_prox, lam_cost, lam_vc, lam_vb_nodal, lam_vb_cross
83
83
  ...
84
84
 
85
85
  def solve(self):
@@ -350,7 +350,8 @@ class PTRSolver(ConvexSolver):
350
350
  lam_prox = ocp_vars.lam_prox
351
351
  lam_cost = ocp_vars.lam_cost
352
352
  lam_vc = ocp_vars.lam_vc
353
- lam_vb = ocp_vars.lam_vb
353
+ lam_vb_nodal = ocp_vars.lam_vb_nodal
354
+ lam_vb_cross = ocp_vars.lam_vb_cross
354
355
  _ = ocp_vars.x_nonscaled
355
356
  dx = ocp_vars.dx
356
357
  du = ocp_vars.du
@@ -359,7 +360,8 @@ class PTRSolver(ConvexSolver):
359
360
  nu_vb_cross = ocp_vars.nu_vb_cross
360
361
 
361
362
  cost = cp.sum(lam_cost) * 0
362
- cost += lam_vb * 0
363
+ cost += cp.sum(lam_vb_nodal) * 0
364
+ cost += cp.sum(lam_vb_cross) * 0
363
365
 
364
366
  # Boundary condition cost terms (use scaled x for numerical conditioning)
365
367
  x = ocp_vars.x
@@ -381,18 +383,18 @@ class PTRSolver(ConvexSolver):
381
383
  # Virtual Control Slack
382
384
  cost += sum(cp.sum(lam_vc[i - 1] * cp.abs(nu[i - 1])) for i in range(1, settings.sim.n))
383
385
 
384
- # Virtual buffer penalty for nodal constraints
386
+ # Virtual buffer penalty for nodal constraints (per-node weighting)
385
387
  idx_ncvx = 0
386
388
  if jax_constraints.nodal:
387
389
  for constraint in jax_constraints.nodal:
388
- cost += lam_vb * cp.sum(cp.pos(nu_vb[idx_ncvx]))
390
+ cost += lam_vb_nodal[:, idx_ncvx] @ cp.pos(nu_vb[idx_ncvx])
389
391
  idx_ncvx += 1
390
392
 
391
393
  # Virtual slack penalty for cross-node constraints
392
394
  idx_cross = 0
393
395
  if jax_constraints.cross_node:
394
396
  for constraint in jax_constraints.cross_node:
395
- cost += lam_vb * cp.pos(nu_vb_cross[idx_cross])
397
+ cost += lam_vb_cross[idx_cross] * cp.pos(nu_vb_cross[idx_cross])
396
398
  idx_cross += 1
397
399
 
398
400
  return cost
@@ -725,7 +727,8 @@ class PTRSolver(ConvexSolver):
725
727
  lam_prox: float,
726
728
  lam_cost: Union[float, np.ndarray],
727
729
  lam_vc: np.ndarray,
728
- lam_vb: float,
730
+ lam_vb_nodal: np.ndarray,
731
+ lam_vb_cross: np.ndarray,
729
732
  ) -> None:
730
733
  """Update SCP penalty weights.
731
734
 
@@ -737,12 +740,16 @@ class PTRSolver(ConvexSolver):
737
740
  lam_cost: Cost function weight. Scalar or array of shape
738
741
  ``(n_states,)`` for per-state weighting.
739
742
  lam_vc: Virtual control penalty weights, shape (N-1, n_states)
740
- lam_vb: Virtual buffer penalty weight (for constraint violations)
743
+ lam_vb_nodal: Virtual buffer penalty weights for nodal constraints,
744
+ shape ``(N, n_nodal_constraints)``.
745
+ lam_vb_cross: Virtual buffer penalty weights for cross-node
746
+ constraints, shape ``(n_cross_node_constraints,)``.
741
747
  """
742
748
  self._set_param("lam_prox", lam_prox)
743
749
  self._set_param("lam_cost", lam_cost)
744
750
  self._set_param("lam_vc", lam_vc)
745
- self._set_param("lam_vb", lam_vb)
751
+ self._set_param("lam_vb_nodal", lam_vb_nodal)
752
+ self._set_param("lam_vb_cross", lam_vb_cross)
746
753
 
747
754
  def update_boundary_conditions(
748
755
  self,