openscvx 0.5.3.dev16__tar.gz → 0.5.3.dev17__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.
- {openscvx-0.5.3.dev16/openscvx.egg-info → openscvx-0.5.3.dev17}/PKG-INFO +1 -1
- openscvx-0.5.3.dev17/docs/UnderTheHood/batching_jit_grad.md +134 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UnderTheHood/lowering_architecture.md +7 -2
- openscvx-0.5.3.dev17/examples/abstract/brachistochrone_batched.py +126 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/mkdocs.yml +2 -1
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/_version.py +3 -3
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/optimization_results.py +233 -1
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/scvx/iteration.py +18 -1
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/problem.py +141 -46
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/solvers/moreau_ptr_solver.py +16 -3
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx.egg-info/SOURCES.txt +6 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_brachistochrone.py +1 -1
- openscvx-0.5.3.dev17/tests/test_solve_jax_bare_brachistochrone.py +70 -0
- openscvx-0.5.3.dev17/tests/test_solve_jax_jit_brachistochrone.py +36 -0
- openscvx-0.5.3.dev17/tests/test_solve_jax_vmap_brachistochrone.py +51 -0
- openscvx-0.5.3.dev17/tests/test_solve_jax_vmap_converged_no_drift.py +128 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/docs.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/lint.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/release.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/tests-integration.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.github/workflows/tests-unit.yml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.gitignore +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/.gitmodules +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/CONTRIBUTING.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/LICENSE +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/README.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/Foundations/discretization.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/08_mpcc.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/favicon.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/viser-client/index.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/viser-recordings/drone_racing.viser +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/citation.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/examples.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/index.md +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/docs/versions.json +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/_viser_embed_export.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/abstract/hypersensitive.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/abstract/impulsive.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/abstract/stl_integer_variable.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/abstract/stl_or.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/_camera.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/_render.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/_sensor_view.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/logo.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/animations/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/arm/3_dof_arm.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/arm/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/arm/7_dof_arm_collision.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/arm/7_dof_arm_vp.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/arm/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/car/dubins_car.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/car/dubins_car_obstacle_conditional.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/car/dubins_car_obstacle_stl.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/car/dubins_car_stl_or.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/car/dubins_car_waypoint_stl.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/logo.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/logo_utils/svg_path_utils.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mjx/cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mjx/double_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mjx/skydio_x2_mjx.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mjx/triple_cartpole_game.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mjx/triple_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mpc/double_integrator_discrete.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mpc/double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mpc/dubins_car_circle_analytical.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mpc/dubins_car_circle_discrete.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/plotting.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/plotting_viser.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/3DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/6DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/rocket/6DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/spacecraft/halo_orbit.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/spacecraft/hohmann_transfer.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/spacecraft/let_transfer.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/examples/spacecraft/relative_loitering.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/figures/video_preview.png +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/assets/stylesheets/home-hero.css +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/assets/stylesheets/home-viser.css +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/home.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/main.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/partials/home-diagram.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/partials/home-dropin-banner.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/partials/home-hero.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/partials/home-pipeline.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/material/overrides/partials/home-viser-strip.html +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/__main__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/scvx/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/algorithms/weights.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/config.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/discretize_linearize.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/linearize_discretize.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/sparse_utils/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/expert/byof.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/expert/lowering.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/expert/validation.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/init/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/init/inverse_kinematics.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/integrations/base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/integrations/menagerie.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/integrations/mjx.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/integrators/diffrax.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/loader.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/cvxpy_variables.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/problem.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/lowered/unified.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/plotting.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/viser/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/viser/animated.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/viser/orbits.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/propagation/post_processing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/propagation/propagation.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/solvers/base.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/solvers/ptr_solver.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/solvers/qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/augmentation.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/builder.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/control.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/expr.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/parameter.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/stljax.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/time.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/variable.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lower.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/state.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/stljax.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/preprocessing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/problem.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/symbolic/unified.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/utils/cache.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/utils/caching.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/utils/printing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx/utils/utils.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/pyproject.toml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/setup.cfg +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/_marks.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/_iteration_helpers.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/test_iteration_fn_jit.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/test_iteration_fn_parity.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/algorithms/test_make_solve_loop.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/conftest.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/expr/test_gmsr.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/hohmann_analytical.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/integrations/test_mjx.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/integrations/test_mjx_dynamics.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/_iteration_callback_helpers.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_iteration_callback_moreau.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_iteration_callback_qpax.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_iteration_callback_vmap.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/solvers/test_subproblem_pytree.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_math.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_stl.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_variable.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/test_augmentation.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/test_preprocessing.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/symbolic/test_unified.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_autotuning.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_discretization.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_examples.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_expert.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_impulsive.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_init.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_integrators.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_loader.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_optimization_results.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_plotting.py +0 -0
- {openscvx-0.5.3.dev16 → openscvx-0.5.3.dev17}/tests/test_propagation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openscvx
|
|
3
|
-
Version: 0.5.3.
|
|
3
|
+
Version: 0.5.3.dev17
|
|
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,134 @@
|
|
|
1
|
+
# Batching, JIT, and Grad with `Problem.solve_jax()`
|
|
2
|
+
|
|
3
|
+
`Problem` exposes two solve entry points. `solve()` is the familiar
|
|
4
|
+
Python-loop driver — real-time prints, wall-clock `time_limit`, `continuous`
|
|
5
|
+
mode, populated per-iteration history. `solve_jax()` is its JAX-pure
|
|
6
|
+
sibling: it drives the same fused `iteration_fn` body inside a
|
|
7
|
+
`lax.while_loop` and returns an `OptimizationResults`
|
|
8
|
+
(`openscvx/algorithms/optimization_results.py`) pytree, so it composes with
|
|
9
|
+
`jax.vmap`, `jax.jit`, and `jax.grad`.
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
problem = ox.Problem(..., solver={"backend": "qpax"})
|
|
13
|
+
problem.initialize()
|
|
14
|
+
|
|
15
|
+
# Familiar interactive solve — unchanged: prints, history, time limit.
|
|
16
|
+
result = problem.solve()
|
|
17
|
+
|
|
18
|
+
# JAX-pure single solve — silent, no per-iteration history, batchable.
|
|
19
|
+
result = problem.solve_jax()
|
|
20
|
+
|
|
21
|
+
# Batched — standard jax.vmap, no library-specific API.
|
|
22
|
+
batched = jax.vmap(problem.solve_jax, in_axes=(0, 0, None))(
|
|
23
|
+
x_initial_stack, x_final_stack, params
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# JIT-compile once, solve forever (e.g. MPC inner loop).
|
|
27
|
+
fast_solve = jax.jit(problem.solve_jax)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A worked example lives at `examples/abstract/brachistochrone_batched.py`
|
|
31
|
+
— four brachistochrone problems with shifted starting x-coordinates,
|
|
32
|
+
solved in parallel via `jax.vmap(problem.solve_jax)`.
|
|
33
|
+
|
|
34
|
+
## When to choose `solve()` vs. `solve_jax()`
|
|
35
|
+
|
|
36
|
+
| Use case | Entry point |
|
|
37
|
+
|---|---|
|
|
38
|
+
| Interactive use, real-time prints, plotting per iteration | `solve()` |
|
|
39
|
+
| Wall-clock `time_limit` or `continuous=True` | `solve()` |
|
|
40
|
+
| Per-iteration trajectories / weights / diagnostics | `solve()` |
|
|
41
|
+
| Batched solve over many initial conditions or parameters | `solve_jax()` |
|
|
42
|
+
| JIT-compile once, solve forever (MPC inner loop, scenario sweeps) | `solve_jax()` |
|
|
43
|
+
| `jax.grad` through the solver (best-effort, untested) | `solve_jax()` |
|
|
44
|
+
|
|
45
|
+
The split exists to make user intent explicit — a single dispatched
|
|
46
|
+
`solve()` would have to infer from arguments which path the caller wants,
|
|
47
|
+
and silent routing failures (like `continuous=True` quietly skipping the
|
|
48
|
+
Python loop) are exactly the failure class the two-method design exists to
|
|
49
|
+
prevent. See `plans/jax-pure-solve.md`'s Decision Log for the longer
|
|
50
|
+
discussion.
|
|
51
|
+
|
|
52
|
+
## What `solve_jax()` returns
|
|
53
|
+
|
|
54
|
+
`solve_jax()` returns the same `OptimizationResults` type as `solve()`,
|
|
55
|
+
but built via
|
|
56
|
+
`OptimizationResults.from_final_state(state, problem=...)` instead of
|
|
57
|
+
`from_history(history, final_state, ...)`. The differences:
|
|
58
|
+
|
|
59
|
+
* **Per-iteration history is empty.** `X = [state.x]` and `U = [state.u]`
|
|
60
|
+
are single-element lists so `result.x` / `result.u` continue to return
|
|
61
|
+
the final iterate; every `*_history` field is `[]`. List growth doesn't
|
|
62
|
+
fit inside `lax.while_loop`. If you need per-iteration trajectories, use
|
|
63
|
+
`solve()` or wrap `solve_jax()` in `lax.scan` manually.
|
|
64
|
+
* **Post-process fields stay `None`.** `t_full`, `x_full`, `u_full`,
|
|
65
|
+
`cost`, `ctcs_violation` are populated only by `post_process()`. They're
|
|
66
|
+
outside the JAX pytree's children surface, so a batched `solve_jax`
|
|
67
|
+
result doesn't force every consumer to handle `None` leaves. If you want
|
|
68
|
+
to post-process, call `post_process()` per batch element after the
|
|
69
|
+
batched solve.
|
|
70
|
+
* **`converged` is a `jnp.bool_`** under the JAX-pure path (a `(B,)` array
|
|
71
|
+
under vmap), not a Python `bool` like `solve()` returns. Most uses
|
|
72
|
+
(`if result.converged: ...`) work either way.
|
|
73
|
+
|
|
74
|
+
## `solve_jax()` arguments
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
problem.solve_jax(
|
|
78
|
+
x_initial=None, # boundary-condition pin (full unified state vector,
|
|
79
|
+
# ``jnp.nan`` at non-Fix entries — see
|
|
80
|
+
# ``AlgorithmState.from_settings``); falls back
|
|
81
|
+
# to the default from settings
|
|
82
|
+
x_final=None, # terminal pin, same conventions
|
|
83
|
+
parameters=None, # parameters dict for this solve; falls back to
|
|
84
|
+
# ``self._parameters``
|
|
85
|
+
*,
|
|
86
|
+
max_iters=None, # SCP iteration cap; non-default rebuilds the
|
|
87
|
+
# cached ``lax.while_loop`` closure (one extra
|
|
88
|
+
# trace; subsequent calls at the same cap hit
|
|
89
|
+
# the cache)
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Positional kwargs (rather than a single `inputs` pytree) keep
|
|
94
|
+
`jax.vmap(problem.solve_jax, in_axes=(0, 0, None))` ergonomic;
|
|
95
|
+
multi-argument gradient uses `jax.grad(..., argnums=(0, 1))`.
|
|
96
|
+
|
|
97
|
+
## Caveats
|
|
98
|
+
|
|
99
|
+
### CVXPy under `vmap` is sequential
|
|
100
|
+
|
|
101
|
+
The CVXPy backend's `iteration_callback`
|
|
102
|
+
(`openscvx/solvers/cvxpy_ptr_solver.py`) host-calls CVXPy through
|
|
103
|
+
`jax.pure_callback` with `vmap_method="sequential"`. Host
|
|
104
|
+
CVXPy is not thread-safe, so a `jax.vmap(problem.solve_jax)` over the
|
|
105
|
+
CVXPy backend runs `B` sequential CVXPy solves. The QPAX and Moreau
|
|
106
|
+
backends are pure JAX end-to-end and run in parallel under vmap.
|
|
107
|
+
|
|
108
|
+
### Moreau warm-start is bypassed
|
|
109
|
+
|
|
110
|
+
The Moreau backend's `_warm_start` carry is host-side mutable state that
|
|
111
|
+
`lax.while_loop` doesn't thread. Both `solve()` and `solve_jax()` cold-start
|
|
112
|
+
the inner Moreau solve every SCP iteration (see the Moreau module
|
|
113
|
+
docstring and `plans/jax-pure-solve.md`'s Decision Log 2026-05-27).
|
|
114
|
+
Restoring warm-start to the SCP loop requires threading `(x, z, s)`
|
|
115
|
+
through an `AlgorithmState.moreau_carry` field — a Future Extension.
|
|
116
|
+
|
|
117
|
+
### `jax.grad` is best-effort, untested
|
|
118
|
+
|
|
119
|
+
The QPAX backend's `solve_qp` is not differentiable (its convergence flag
|
|
120
|
+
costs reverse-mode autodiff). The CVXPy backend's `pure_callback` is
|
|
121
|
+
non-differentiable by default. End-to-end gradient validation through
|
|
122
|
+
`solve_jax` is a follow-up — `jax.grad(loss_fn)(x0)` will run without
|
|
123
|
+
erroring under QPAX (`solve_qp_primal` would be `custom_vjp`, but the
|
|
124
|
+
backend currently uses `solve_qp`), but the gradient's correctness is not
|
|
125
|
+
yet pinned down by a test against finite differences.
|
|
126
|
+
|
|
127
|
+
### Convergence under `vmap` with mixed-rate elements
|
|
128
|
+
|
|
129
|
+
`lax.while_loop` continues while *any* batch element still needs
|
|
130
|
+
iterations — converged elements would otherwise keep receiving body calls
|
|
131
|
+
while their peers iterate. `make_solve_loop`'s body selects the unchanged
|
|
132
|
+
state for converged elements (`jax.tree.map(jnp.where, ...)`), so a
|
|
133
|
+
batched solve agrees with the single-problem `solve_jax` on each element.
|
|
134
|
+
The cost is per-iteration work × slowest-element iteration count.
|
|
@@ -188,8 +188,13 @@ JAX export, used only by `post_process()` — so a compiled problem holds **one
|
|
|
188
188
|
`iteration_fn` plus one propagation export**, not three discretization /
|
|
189
189
|
propagation exports stitched together in Python. Because `iteration_fn` is a
|
|
190
190
|
registered pytree in and out, it also composes with `jax.jit`; `make_solve_loop`
|
|
191
|
-
wraps it in `lax.while_loop`
|
|
192
|
-
|
|
191
|
+
wraps it in `lax.while_loop` to drive the **JAX-pure** `Problem.solve_jax()`
|
|
192
|
+
entry point, which composes with `jax.vmap`, `jax.jit`, and `jax.grad` (see
|
|
193
|
+
`docs/UnderTheHood/batching_jit_grad.md`). The familiar
|
|
194
|
+
`Problem.solve()` keeps its Python-loop shape — real-time prints, wall-clock
|
|
195
|
+
`time_limit`, populated per-iteration history — and is the right entry
|
|
196
|
+
point for interactive use. `solve_jax()` is the right entry point for
|
|
197
|
+
batched / JIT'd / differentiable workflows.
|
|
193
198
|
|
|
194
199
|
Picking a backend at construction time:
|
|
195
200
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Batched brachistochrone via ``jax.vmap(problem.solve_jax)``.
|
|
2
|
+
|
|
3
|
+
Solves four brachistochrone problems in parallel — each with a different
|
|
4
|
+
starting x-coordinate — using the JAX-pure
|
|
5
|
+
:meth:`~openscvx.problem.Problem.solve_jax` entry point. ``solve_jax``
|
|
6
|
+
returns an :class:`~openscvx.algorithms.optimization_results.OptimizationResults`
|
|
7
|
+
pytree, so ``jax.vmap`` over the boundary-condition pins is the standard JAX
|
|
8
|
+
composition pattern; no library-specific batching API is needed.
|
|
9
|
+
|
|
10
|
+
Run with ``python examples/abstract/brachistochrone_batched.py``.
|
|
11
|
+
|
|
12
|
+
Notes:
|
|
13
|
+
|
|
14
|
+
* Under the QPAX backend used here, vmap'd subproblem solves run in
|
|
15
|
+
parallel (no host callback). The CVXPy backend is single-threaded under
|
|
16
|
+
vmap because :func:`jax.pure_callback` uses ``vmap_method="sequential"``
|
|
17
|
+
for thread safety — see :meth:`solve_jax`'s docstring.
|
|
18
|
+
* The Moreau warm-start is bypassed on the JAX-pure path (its carry is
|
|
19
|
+
host-side state that ``lax.while_loop`` doesn't thread). Use QPAX or
|
|
20
|
+
CVXPy when you want batched solves.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import os
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
import jax
|
|
27
|
+
import jax.numpy as jnp
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
31
|
+
grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
|
|
32
|
+
sys.path.append(grandparent_dir)
|
|
33
|
+
|
|
34
|
+
import openscvx as ox
|
|
35
|
+
from openscvx import Problem
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def build_problem(n: int = 8):
|
|
39
|
+
g = 9.81
|
|
40
|
+
|
|
41
|
+
position = ox.State("position", shape=(2,))
|
|
42
|
+
position.max = np.array([10.0, 10.0])
|
|
43
|
+
position.min = np.array([0.0, 0.0])
|
|
44
|
+
position.initial = np.array([0.0, 10.0])
|
|
45
|
+
position.final = [10.0, 5.0]
|
|
46
|
+
|
|
47
|
+
velocity = ox.State("velocity", shape=(1,))
|
|
48
|
+
velocity.max = np.array([10.0])
|
|
49
|
+
velocity.min = np.array([0.0])
|
|
50
|
+
velocity.initial = np.array([0.0])
|
|
51
|
+
velocity.final = [("free", 10.0)]
|
|
52
|
+
|
|
53
|
+
theta = ox.Control("theta", shape=(1,))
|
|
54
|
+
theta.max = np.array([100.5 * jnp.pi / 180])
|
|
55
|
+
theta.min = np.array([0.0])
|
|
56
|
+
theta.guess = np.linspace(5 * jnp.pi / 180, 100.5 * jnp.pi / 180, n).reshape(-1, 1)
|
|
57
|
+
|
|
58
|
+
states = [position, velocity]
|
|
59
|
+
controls = [theta]
|
|
60
|
+
|
|
61
|
+
dynamics = {
|
|
62
|
+
"position": ox.Concat(
|
|
63
|
+
velocity[0] * ox.Sin(theta[0]),
|
|
64
|
+
-velocity[0] * ox.Cos(theta[0]),
|
|
65
|
+
),
|
|
66
|
+
"velocity": g * ox.Cos(theta[0]),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
constraint_exprs = []
|
|
70
|
+
for state in states:
|
|
71
|
+
constraint_exprs.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
|
|
72
|
+
|
|
73
|
+
time = ox.Time(
|
|
74
|
+
initial=0.0,
|
|
75
|
+
final=("minimize", 2.0),
|
|
76
|
+
min=0.0,
|
|
77
|
+
max=2.0,
|
|
78
|
+
uniform_time_grid=True,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return Problem(
|
|
82
|
+
dynamics=dynamics,
|
|
83
|
+
states=states,
|
|
84
|
+
controls=controls,
|
|
85
|
+
time=time,
|
|
86
|
+
constraints=constraint_exprs,
|
|
87
|
+
N=n,
|
|
88
|
+
float_dtype="float64",
|
|
89
|
+
algorithm={
|
|
90
|
+
"autotuner": "ConstantProximalWeight",
|
|
91
|
+
"lam_prox": 1e0,
|
|
92
|
+
"lam_cost": 6e-1,
|
|
93
|
+
"ep_tr": 1e-5,
|
|
94
|
+
"ep_vb": 1e-5,
|
|
95
|
+
"ep_vc": 1e-9,
|
|
96
|
+
"k_max": 30,
|
|
97
|
+
},
|
|
98
|
+
solver={"backend": "qpax"},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
problem = build_problem()
|
|
104
|
+
problem.initialize()
|
|
105
|
+
|
|
106
|
+
# The default boundary-condition pin (the full unified state vector with
|
|
107
|
+
# ``jnp.nan`` at non-Fix entries — see
|
|
108
|
+
# :meth:`AlgorithmState.from_settings`).
|
|
109
|
+
default_pin = problem.state.x_init_pin
|
|
110
|
+
|
|
111
|
+
# Stack four ICs by varying the starting x-coordinate (component 0 of
|
|
112
|
+
# the unified state vector is the x-position).
|
|
113
|
+
shifts = jnp.array([0.0, 0.3, 0.6, 0.9])
|
|
114
|
+
x_initial_stack = jnp.stack([default_pin.at[0].set(default_pin[0] + s) for s in shifts])
|
|
115
|
+
|
|
116
|
+
# Bare ``jax.vmap`` — the library exposes no batching wrapper of its own.
|
|
117
|
+
batched_solve = jax.vmap(problem.solve_jax, in_axes=(0, None, None))
|
|
118
|
+
results = batched_solve(x_initial_stack, None, None)
|
|
119
|
+
|
|
120
|
+
# ``results`` is a batched ``OptimizationResults`` pytree — every child
|
|
121
|
+
# (``X``, ``U``, ``t_final``, ``converged``, ...) has a leading batch axis.
|
|
122
|
+
print(f"Batched solve over {x_initial_stack.shape[0]} initial conditions:")
|
|
123
|
+
print(f" result.x.shape: {results.x.shape}")
|
|
124
|
+
print(f" result.u.shape: {results.u.shape}")
|
|
125
|
+
print(f" result.t_final: {np.asarray(results.t_final).reshape(-1)}")
|
|
126
|
+
print(f" result.converged: {np.asarray(results.converged)}")
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.5.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 5, 3, '
|
|
21
|
+
__version__ = version = '0.5.3.dev17'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 3, 'dev17')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gb2143958f'
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Any, Optional, Union
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
4
4
|
|
|
5
|
+
import jax
|
|
6
|
+
import jax.numpy as jnp
|
|
5
7
|
import numpy as np
|
|
6
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from openscvx.algorithms.base import AlgorithmHistory, AlgorithmState
|
|
11
|
+
from openscvx.problem import Problem
|
|
7
12
|
|
|
13
|
+
|
|
14
|
+
@jax.tree_util.register_pytree_node_class
|
|
8
15
|
@dataclass
|
|
9
16
|
class OptimizationResults:
|
|
10
17
|
"""
|
|
@@ -44,6 +51,30 @@ class OptimizationResults:
|
|
|
44
51
|
Added by propagate_trajectory_results.
|
|
45
52
|
plotting_data (dict[str, Any]): Flexible storage for plotting and application data.
|
|
46
53
|
|
|
54
|
+
!!! note "JAX pytree registration"
|
|
55
|
+
``OptimizationResults`` is a registered JAX pytree, so the output of
|
|
56
|
+
:meth:`~openscvx.problem.Problem.solve_jax` composes with
|
|
57
|
+
``jax.vmap`` / ``jax.jit`` / ``jax.grad``. The pytree *children* are
|
|
58
|
+
``converged``, ``t_final``, ``nodes``, ``trajectory``, ``X``, ``U``,
|
|
59
|
+
and every ``*_history`` field — the data the user actually consumes.
|
|
60
|
+
Post-process fields (``t_full``, ``x_full``, ``u_full``, ``cost``,
|
|
61
|
+
``ctcs_violation``), ``plotting_data``, and the internal ``_states`` /
|
|
62
|
+
``_controls`` metadata are stashed in the treedef's *aux* instead, so
|
|
63
|
+
a batched ``solve_jax`` doesn't force callers to handle ``None``
|
|
64
|
+
leaves and ``post_process()`` outputs stay outside the vmap surface
|
|
65
|
+
(post-process per element after the batched solve).
|
|
66
|
+
|
|
67
|
+
The two construction paths produce different histories:
|
|
68
|
+
|
|
69
|
+
* :meth:`from_history` (``solve()``) populates the per-iteration
|
|
70
|
+
``X`` / ``U`` / ``*_history`` lists from the Python loop's
|
|
71
|
+
:class:`~openscvx.algorithms.base.AlgorithmHistory`.
|
|
72
|
+
* :meth:`from_final_state` (``solve_jax()``) wraps only the final
|
|
73
|
+
iterate: ``X = [state.x]`` and ``U = [state.u]`` (single-element
|
|
74
|
+
lists so ``result.x`` / ``result.u`` continue to return the final
|
|
75
|
+
iterate), and every ``*_history`` field is empty — per-iteration
|
|
76
|
+
history requires Python-side list growth that can't run inside
|
|
77
|
+
``lax.while_loop``.
|
|
47
78
|
|
|
48
79
|
!!! note "For Developers"
|
|
49
80
|
The ``metadata={"npz": ...}`` parameter on each field below is a built-in feature of
|
|
@@ -66,6 +97,30 @@ class OptimizationResults:
|
|
|
66
97
|
_OPT_ARRAY = "optional_array"
|
|
67
98
|
_OPT_SCALAR = "optional_scalar"
|
|
68
99
|
|
|
100
|
+
# Pytree children: fields whose contents flow through ``jax.vmap`` /
|
|
101
|
+
# ``jax.jit`` / ``jax.grad``. Excludes ``_states`` / ``_controls`` (Python
|
|
102
|
+
# symbolic metadata), ``plotting_data`` (user scratch), and the
|
|
103
|
+
# post-process fields (``None`` until ``post_process()`` runs and not part
|
|
104
|
+
# of the ``solve_jax`` vmap surface — see the class docstring).
|
|
105
|
+
_PYTREE_CHILDREN = (
|
|
106
|
+
"converged",
|
|
107
|
+
"t_final",
|
|
108
|
+
"nodes",
|
|
109
|
+
"trajectory",
|
|
110
|
+
"X",
|
|
111
|
+
"U",
|
|
112
|
+
"discretization_history",
|
|
113
|
+
"J_tr_history",
|
|
114
|
+
"J_vb_history",
|
|
115
|
+
"J_vc_history",
|
|
116
|
+
"TR_history",
|
|
117
|
+
"VC_history",
|
|
118
|
+
"lam_prox_history",
|
|
119
|
+
"actual_reduction_history",
|
|
120
|
+
"pred_reduction_history",
|
|
121
|
+
"acceptance_ratio_history",
|
|
122
|
+
)
|
|
123
|
+
|
|
69
124
|
# Core optimization results
|
|
70
125
|
converged: bool = field(metadata={"npz": "scalar"})
|
|
71
126
|
t_final: float = field(metadata={"npz": "scalar"})
|
|
@@ -138,6 +193,183 @@ class OptimizationResults:
|
|
|
138
193
|
"""Initialize the results object."""
|
|
139
194
|
pass
|
|
140
195
|
|
|
196
|
+
# ------------------------------------------------------------------
|
|
197
|
+
# JAX pytree registration
|
|
198
|
+
# ------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
def tree_flatten(self):
|
|
201
|
+
"""Split into JAX-traceable children and host-side aux.
|
|
202
|
+
|
|
203
|
+
See :attr:`_PYTREE_CHILDREN` for the children list. Aux carries the
|
|
204
|
+
symbolic metadata, scratch dict, and post-process fields verbatim so
|
|
205
|
+
the round-trip ``tree_unflatten(*tree_flatten(self))`` preserves the
|
|
206
|
+
instance.
|
|
207
|
+
"""
|
|
208
|
+
children = tuple(getattr(self, name) for name in self._PYTREE_CHILDREN)
|
|
209
|
+
aux = (
|
|
210
|
+
self._states,
|
|
211
|
+
self._controls,
|
|
212
|
+
self.t_full,
|
|
213
|
+
self.x_full,
|
|
214
|
+
self.u_full,
|
|
215
|
+
self.cost,
|
|
216
|
+
self.ctcs_violation,
|
|
217
|
+
self.plotting_data,
|
|
218
|
+
)
|
|
219
|
+
return children, aux
|
|
220
|
+
|
|
221
|
+
@classmethod
|
|
222
|
+
def tree_unflatten(cls, aux, children):
|
|
223
|
+
kwargs = dict(zip(cls._PYTREE_CHILDREN, children))
|
|
224
|
+
(
|
|
225
|
+
_states,
|
|
226
|
+
_controls,
|
|
227
|
+
t_full,
|
|
228
|
+
x_full,
|
|
229
|
+
u_full,
|
|
230
|
+
cost,
|
|
231
|
+
ctcs_violation,
|
|
232
|
+
plotting_data,
|
|
233
|
+
) = aux
|
|
234
|
+
return cls(
|
|
235
|
+
**kwargs,
|
|
236
|
+
_states=_states,
|
|
237
|
+
_controls=_controls,
|
|
238
|
+
t_full=t_full,
|
|
239
|
+
x_full=x_full,
|
|
240
|
+
u_full=u_full,
|
|
241
|
+
cost=cost,
|
|
242
|
+
ctcs_violation=ctcs_violation,
|
|
243
|
+
plotting_data=plotting_data,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# ------------------------------------------------------------------
|
|
247
|
+
# Constructors
|
|
248
|
+
# ------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
@classmethod
|
|
251
|
+
def from_final_state(
|
|
252
|
+
cls,
|
|
253
|
+
state: "AlgorithmState",
|
|
254
|
+
*,
|
|
255
|
+
problem: "Problem",
|
|
256
|
+
) -> "OptimizationResults":
|
|
257
|
+
"""JAX-pure construction from the final SCP iterate.
|
|
258
|
+
|
|
259
|
+
Used by :meth:`~openscvx.problem.Problem.solve_jax`. Every leaf is a
|
|
260
|
+
``jnp.ndarray`` derived from ``state`` via static slicing, so the
|
|
261
|
+
result composes with ``jax.vmap`` / ``jax.jit`` / ``jax.grad`` over a
|
|
262
|
+
batched ``state``. ``X = [state.x]`` and ``U = [state.u]`` are
|
|
263
|
+
single-element lists so ``result.x`` / ``result.u`` continue to
|
|
264
|
+
return the final iterate; every ``*_history`` field is empty — the
|
|
265
|
+
Python-loop ``.solve()`` is the path that populates them.
|
|
266
|
+
"""
|
|
267
|
+
settings = problem.settings
|
|
268
|
+
symbolic = problem.symbolic
|
|
269
|
+
algorithm = problem._algorithm
|
|
270
|
+
|
|
271
|
+
has_impulsive_controls = any(
|
|
272
|
+
control.parameterization == "impulsive" for control in symbolic.controls
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
nodes_dict: dict[str, jnp.ndarray] = {}
|
|
276
|
+
for sym_state in symbolic.states:
|
|
277
|
+
state_nodes = state.x[..., sym_state._slice]
|
|
278
|
+
if has_impulsive_controls:
|
|
279
|
+
bc = jnp.asarray(settings.sim.x.initial[sym_state._slice])
|
|
280
|
+
state_nodes = state_nodes.at[..., 0, :].set(bc)
|
|
281
|
+
nodes_dict[sym_state.name] = state_nodes
|
|
282
|
+
|
|
283
|
+
for control in symbolic.controls:
|
|
284
|
+
nodes_dict[control.name] = state.u[..., control._slice]
|
|
285
|
+
|
|
286
|
+
# Match the host-path ``t_final`` shape: a 1-vector along the time
|
|
287
|
+
# slice of the last node (vmap'd to ``(B, 1)``).
|
|
288
|
+
t_final = state.x[..., -1, :][..., settings.sim.time_slice]
|
|
289
|
+
|
|
290
|
+
converged = (
|
|
291
|
+
(state.J_tr < algorithm.ep_tr)
|
|
292
|
+
& (state.J_vb < algorithm.ep_vb)
|
|
293
|
+
& (state.J_vc < algorithm.ep_vc)
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
return cls(
|
|
297
|
+
converged=converged,
|
|
298
|
+
t_final=t_final,
|
|
299
|
+
nodes=nodes_dict,
|
|
300
|
+
trajectory={},
|
|
301
|
+
_states=symbolic.states_prop,
|
|
302
|
+
_controls=symbolic.controls,
|
|
303
|
+
X=[state.x],
|
|
304
|
+
U=[state.u],
|
|
305
|
+
discretization_history=[],
|
|
306
|
+
J_tr_history=[],
|
|
307
|
+
J_vb_history=[],
|
|
308
|
+
J_vc_history=[],
|
|
309
|
+
TR_history=[],
|
|
310
|
+
VC_history=[],
|
|
311
|
+
lam_prox_history=[],
|
|
312
|
+
actual_reduction_history=[],
|
|
313
|
+
pred_reduction_history=[],
|
|
314
|
+
acceptance_ratio_history=[],
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
@classmethod
|
|
318
|
+
def from_history(
|
|
319
|
+
cls,
|
|
320
|
+
history: "AlgorithmHistory",
|
|
321
|
+
final_state: "AlgorithmState",
|
|
322
|
+
*,
|
|
323
|
+
problem: "Problem",
|
|
324
|
+
converged: bool,
|
|
325
|
+
) -> "OptimizationResults":
|
|
326
|
+
"""Host-side construction from the Python-loop iteration history.
|
|
327
|
+
|
|
328
|
+
Used by :meth:`~openscvx.problem.Problem.solve` to package the full
|
|
329
|
+
per-iteration log produced by the Python ``while`` loop. Symmetric
|
|
330
|
+
with :meth:`from_final_state` — same return type, populated history.
|
|
331
|
+
"""
|
|
332
|
+
settings = problem.settings
|
|
333
|
+
symbolic = problem.symbolic
|
|
334
|
+
|
|
335
|
+
has_impulsive_controls = any(
|
|
336
|
+
control.parameterization == "impulsive" for control in symbolic.controls
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
x_arr = np.asarray(final_state.x)
|
|
340
|
+
u_arr = np.asarray(final_state.u)
|
|
341
|
+
|
|
342
|
+
nodes_dict: dict[str, np.ndarray] = {}
|
|
343
|
+
for sym_state in symbolic.states:
|
|
344
|
+
state_nodes = x_arr[:, sym_state._slice].copy()
|
|
345
|
+
if has_impulsive_controls:
|
|
346
|
+
state_nodes[0] = settings.sim.x.initial[sym_state._slice]
|
|
347
|
+
nodes_dict[sym_state.name] = state_nodes
|
|
348
|
+
|
|
349
|
+
for control in symbolic.controls:
|
|
350
|
+
nodes_dict[control.name] = u_arr[:, control._slice]
|
|
351
|
+
|
|
352
|
+
return cls(
|
|
353
|
+
converged=converged,
|
|
354
|
+
t_final=x_arr[:, settings.sim.time_slice][-1],
|
|
355
|
+
nodes=nodes_dict,
|
|
356
|
+
trajectory={},
|
|
357
|
+
_states=symbolic.states_prop,
|
|
358
|
+
_controls=symbolic.controls,
|
|
359
|
+
X=history.X,
|
|
360
|
+
U=history.U,
|
|
361
|
+
discretization_history=history.V_history,
|
|
362
|
+
J_tr_history=float(final_state.J_tr),
|
|
363
|
+
J_vb_history=float(final_state.J_vb),
|
|
364
|
+
J_vc_history=float(final_state.J_vc),
|
|
365
|
+
TR_history=history.TR,
|
|
366
|
+
VC_history=history.VC,
|
|
367
|
+
lam_prox_history=history.lam_prox.copy(),
|
|
368
|
+
actual_reduction_history=history.actual_reduction.copy(),
|
|
369
|
+
pred_reduction_history=history.pred_reduction.copy(),
|
|
370
|
+
acceptance_ratio_history=history.acceptance_ratio.copy(),
|
|
371
|
+
)
|
|
372
|
+
|
|
141
373
|
def update_plotting_data(self, **kwargs: Any) -> None:
|
|
142
374
|
"""
|
|
143
375
|
Update the plotting data with additional information.
|
|
@@ -361,8 +361,25 @@ def make_solve_loop(
|
|
|
361
361
|
return (state.k <= k_max) & jnp.logical_not(_converged(state, ep_tr, ep_vb, ep_vc))
|
|
362
362
|
|
|
363
363
|
def body(state: AlgorithmState) -> AlgorithmState:
|
|
364
|
+
# Under ``jax.vmap`` the ``lax.while_loop`` keeps running until
|
|
365
|
+
# every batch element has converged; without a freeze, the body
|
|
366
|
+
# would keep mutating already-converged elements (their iterates
|
|
367
|
+
# drift through repeated subproblem solves, and the autotuner
|
|
368
|
+
# would keep advancing ``lam_prox`` / ``lam_cost``). Selecting
|
|
369
|
+
# ``state`` for converged elements pins them to their first
|
|
370
|
+
# post-convergence iterate, so a batched solve agrees with the
|
|
371
|
+
# single-problem ``solve_jax`` on each element.
|
|
372
|
+
is_converged = _converged(state, ep_tr, ep_vb, ep_vc)
|
|
364
373
|
next_state, _ = iteration_fn(state, params)
|
|
365
|
-
|
|
374
|
+
|
|
375
|
+
def freeze(nxt, prev):
|
|
376
|
+
# ``is_converged`` is scalar (single-problem) or shape ``(B,)``
|
|
377
|
+
# (vmap'd); reshape with trailing 1-axes so it broadcasts over
|
|
378
|
+
# each leaf's remaining dims.
|
|
379
|
+
mask_shape = is_converged.shape + (1,) * (nxt.ndim - is_converged.ndim)
|
|
380
|
+
return jnp.where(is_converged.reshape(mask_shape), prev, nxt)
|
|
381
|
+
|
|
382
|
+
return jax.tree.map(freeze, next_state, state)
|
|
366
383
|
|
|
367
384
|
return jax.lax.while_loop(cond, body, state)
|
|
368
385
|
|