openscvx 0.5.3.dev19__tar.gz → 0.5.3.dev20__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.dev19/openscvx.egg-info → openscvx-0.5.3.dev20}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UnderTheHood/batching_jit_grad.md +75 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/abstract/brachistochrone_batched.py +40 -11
- openscvx-0.5.3.dev20/examples/drone/2d_obstacle_avoidance_batched_ic.py +326 -0
- openscvx-0.5.3.dev20/examples/drone/drone_racing_batched_gates.py +414 -0
- openscvx-0.5.3.dev20/examples/rocket/6DoF_pdg_batched_ic.py +434 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/_version.py +3 -3
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/base.py +42 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/scvx/iteration.py +1 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/scvx/penalized_trust_region.py +35 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/base.py +16 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/problem.py +536 -9
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/propagation/post_processing.py +26 -20
- openscvx-0.5.3.dev20/openscvx/solvers/cones.py +100 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/solvers/cvxpy_ptr_solver.py +12 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/solvers/moreau_ptr_solver.py +254 -32
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/solvers/ptr_solver.py +189 -1
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/solvers/qpax_ptr_solver.py +104 -35
- openscvx-0.5.3.dev20/openscvx/symbolic/affine.py +162 -0
- openscvx-0.5.3.dev20/openscvx/symbolic/canonicalize.py +247 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/utils/caching.py +142 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/utils/printing.py +81 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx.egg-info/SOURCES.txt +9 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/_iteration_callback_helpers.py +1 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_iteration_callback_cvxpy.py +1 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_iteration_callback_vmap.py +1 -1
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_moreau_ptr_solver.py +58 -6
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_qpax_ptr_solver.py +5 -5
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_subproblem_pytree.py +1 -0
- openscvx-0.5.3.dev20/tests/test_solve_batched_brachistochrone.py +71 -0
- openscvx-0.5.3.dev20/tests/test_solve_batched_cvxpy_export_errors.py +38 -0
- openscvx-0.5.3.dev20/tests/test_solve_batched_export_roundtrip.py +123 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/docs.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/lint.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/release.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/tests-integration.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.github/workflows/tests-unit.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.gitignore +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/.gitmodules +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/CONTRIBUTING.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/LICENSE +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/README.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/Foundations/discretization.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UnderTheHood/lowering_architecture.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/08_mpcc.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/favicon.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/viser-client/index.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/viser-recordings/drone_racing.viser +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/citation.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/examples.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/index.md +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/docs/versions.json +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/_viser_embed_export.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/abstract/flappy_bird.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/abstract/hypersensitive.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/abstract/impulsive.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/abstract/stl_integer_variable.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/abstract/stl_or.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/_camera.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/_render.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/_sensor_view.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/logo.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/animations/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/arm/3_dof_arm.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/arm/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/arm/7_dof_arm_collision.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/arm/7_dof_arm_vp.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/arm/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/arm/franka_fr3v2_viewplanning_nodal.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/car/dubins_car.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/car/dubins_car_obstacle_conditional.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/car/dubins_car_obstacle_stl.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/car/dubins_car_stl_or.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/car/dubins_car_waypoint_stl.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/cinema_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/logo.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/logo_utils/openscvx_logo_single.svg +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/logo_utils/quadrotor_mesh.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/logo_utils/svg_path_utils.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/obstacle_avoidance_vmap_2d.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/drone/openscvx_logo.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mjx/cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mjx/double_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mjx/skydio_x2_mjx.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mjx/triple_cartpole_game.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mjx/triple_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mpc/double_integrator_discrete.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mpc/double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mpc/dubins_car_circle_analytical.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mpc/dubins_car_circle_discrete.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/plotting.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/plotting_viser.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/3DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/6DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/rocket/6DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/spacecraft/halo_orbit.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/spacecraft/hohmann_transfer.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/spacecraft/let_transfer.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/examples/spacecraft/relative_loitering.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/figures/video_preview.png +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/assets/stylesheets/home-hero.css +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/assets/stylesheets/home-viser.css +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/home.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/main.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/partials/home-diagram.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/partials/home-dropin-banner.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/partials/home-hero.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/partials/home-pipeline.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/material/overrides/partials/home-viser-strip.html +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/mkdocs.yml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/__main__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/optimization_results.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/scvx/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/algorithms/weights.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/config.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/discretize_linearize.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/linearize_discretize.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/sparse_utils/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/expert/byof.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/expert/lowering.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/expert/validation.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/init/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/init/inverse_kinematics.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/integrations/base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/integrations/menagerie.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/integrations/mjx.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/integrators/diffrax.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/loader.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/cvxpy_variables.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/problem.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/lowered/unified.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/plotting.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/publication.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/animated.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/coordinates.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/orbits.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/propagation/propagation.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/solvers/base.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/augmentation.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/builder.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/control.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/expr.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/parameter.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/stljax.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/time.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/variable.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lower.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/state.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/stljax.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/preprocessing.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/problem.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/symbolic/unified.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/utils/cache.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx/utils/utils.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/pyproject.toml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/setup.cfg +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/_marks.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/_iteration_helpers.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/test_iteration_fn_jit.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/test_iteration_fn_parity.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/algorithms/test_make_solve_loop.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/conftest.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/expr/test_gmsr.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/hohmann_analytical.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/integrations/test_mjx.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/integrations/test_mjx_dynamics.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_iteration_callback_moreau.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/solvers/test_iteration_callback_qpax.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_math.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_stl.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_variable.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/test_augmentation.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/test_preprocessing.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/symbolic/test_unified.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_autotuning.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_discretization.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_examples.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_expert.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_impulsive.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_init.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_integrators.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_loader.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_optimization_results.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_plotting.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_propagation.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_solve_jax_bare_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_solve_jax_jit_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_solve_jax_vmap_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev19 → openscvx-0.5.3.dev20}/tests/test_solve_jax_vmap_converged_no_drift.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.dev20
|
|
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
|
|
@@ -49,6 +49,81 @@ Python loop) are exactly the failure class the two-method design exists to
|
|
|
49
49
|
prevent. See `plans/jax-pure-solve.md`'s Decision Log for the longer
|
|
50
50
|
discussion.
|
|
51
51
|
|
|
52
|
+
## Batched, disk-cached solves with `solve_batched()`
|
|
53
|
+
|
|
54
|
+
`jax.vmap(solve_jax)` and `solve_batched()` both run `B` SCP solves over
|
|
55
|
+
stacked boundary conditions; they differ in **who owns the batch axis**, and
|
|
56
|
+
that one difference is the whole point.
|
|
57
|
+
|
|
58
|
+
* `jax.vmap(solve_jax)` — *the caller* owns the axis. Maximally idiomatic,
|
|
59
|
+
composes with `grad` / `scan`, but **in-process JIT only**: `jax.export` has
|
|
60
|
+
no outer `vmap` rule (`call_exported` cannot be batched), so an exported
|
|
61
|
+
artifact can never be re-vmapped, and every fresh process re-traces and
|
|
62
|
+
re-compiles the entire loop.
|
|
63
|
+
* `solve_batched(x0_stack, xf_stack)` — *the library* owns the axis. The vmap
|
|
64
|
+
is applied **inside** the method, before any export, so the whole loop lowers
|
|
65
|
+
to ordinary batched XLA ops and serializes cleanly. Under
|
|
66
|
+
`save_compiled=True` it is written to the solver cache on the first run and
|
|
67
|
+
deserialized on later processes — skipping the XLA compile that
|
|
68
|
+
`jax.vmap(solve_jax)` pays on every launch.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
problem = ox.Problem(..., solver={"backend": "qpax"})
|
|
72
|
+
problem.settings.sim.save_compiled = True
|
|
73
|
+
problem.initialize()
|
|
74
|
+
|
|
75
|
+
# First process: traces, exports, writes <cache>/compiled_solve_batched_<hash>.jax
|
|
76
|
+
# Later processes: deserialize-and-.call, no compile.
|
|
77
|
+
batched = problem.solve_batched(x0_stack, xf_stack, params)
|
|
78
|
+
# batched.x.shape == (B, N, n_states) — same pytree jax.vmap(solve_jax) returns.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Pick `solve_batched` when **cross-process cold-start dominates** — short-lived
|
|
82
|
+
processes (CI sweeps, notebook restarts, deployed workers) that otherwise pay
|
|
83
|
+
the compile every launch. Pick `jax.vmap(solve_jax)` for everything in-program,
|
|
84
|
+
where you own the transforms and want `grad` / `scan` composition. With
|
|
85
|
+
`save_compiled=False`, `solve_batched` is just an in-process batched wrapper and
|
|
86
|
+
the two produce identical results.
|
|
87
|
+
|
|
88
|
+
The exported artifact is closed and deployable, not composable: it is the
|
|
89
|
+
`call_exported`-has-no-outer-`vmap`-rule wall again. Use `solve_jax` for
|
|
90
|
+
user-driven `jax.vmap` / `jax.grad`; `solve_batched` for a cached batched solve.
|
|
91
|
+
|
|
92
|
+
### CVXPy cannot be exported
|
|
93
|
+
|
|
94
|
+
Export requires every op in the loop — the backend solve included — to lower to
|
|
95
|
+
serializable XLA. CVXPy's `iteration_callback` is a `jax.pure_callback`, and
|
|
96
|
+
`jax.export` cannot serialize host callbacks. So `PTRSolver.exportable` is
|
|
97
|
+
`False` for CVXPy (and `True` for QPAX / Moreau), and
|
|
98
|
+
`solve_batched(save_compiled=True)` on the CVXPy backend **raises a teaching
|
|
99
|
+
error** pointing at QPAX / Moreau rather than silently degrading to an uncached
|
|
100
|
+
in-process solve. With `save_compiled=False`, CVXPy still runs `B` sequential
|
|
101
|
+
in-process solves.
|
|
102
|
+
|
|
103
|
+
### Cache invalidation is correctness, not just freshness
|
|
104
|
+
|
|
105
|
+
The exported loop closes over far more than the symbolic problem does. Caching
|
|
106
|
+
on the symbolic hash alone would silently deserialize a **stale artifact** when
|
|
107
|
+
any of that extra state changes — a wrong-answer bug, not a cache miss. So
|
|
108
|
+
`get_solve_batched_cache_path` (`openscvx/utils/caching.py`) extends the
|
|
109
|
+
symbolic hash with everything additionally baked into the loop, each runtime
|
|
110
|
+
object contributing through its own `_hash_into` (the same protocol the symbolic
|
|
111
|
+
layer uses):
|
|
112
|
+
|
|
113
|
+
* the convex backend class + its `solver_args`,
|
|
114
|
+
* the algorithm + autotuner + convergence thresholds + initial weights,
|
|
115
|
+
* the discretizer scheme (class + hold type + ODE solver),
|
|
116
|
+
* the state/control scaling matrices `inv_S_x` / `inv_S_u`,
|
|
117
|
+
* the fixed batch size `B` (the artifact is exported at one `B`), and
|
|
118
|
+
* the JAX version (exported artifacts are not guaranteed to survive an
|
|
119
|
+
incompatible jax bump).
|
|
120
|
+
|
|
121
|
+
Change any of these — backend, a tolerance, `k_max`, a state bound — and you get
|
|
122
|
+
a different cache path, so a stale artifact is never reused.
|
|
123
|
+
|
|
124
|
+
`examples/abstract/brachistochrone_batched.py` shows both paths side by side;
|
|
125
|
+
re-running it is itself the cross-process demo.
|
|
126
|
+
|
|
52
127
|
## What `solve_jax()` returns
|
|
53
128
|
|
|
54
129
|
`solve_jax()` returns the same `OptimizationResults` type as `solve()`,
|
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
"""Batched brachistochrone
|
|
1
|
+
"""Batched brachistochrone — two ways to solve four problems at once.
|
|
2
2
|
|
|
3
3
|
Solves four brachistochrone problems in parallel — each with a different
|
|
4
|
-
starting x-coordinate —
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
starting x-coordinate — and contrasts the two batching entry points:
|
|
5
|
+
|
|
6
|
+
* ``jax.vmap(problem.solve_jax)`` — the caller owns the batch axis. Maximally
|
|
7
|
+
idiomatic, composes with ``grad`` / ``scan``, but in-process JIT only: every
|
|
8
|
+
fresh process re-traces and re-compiles the whole SCP loop.
|
|
9
|
+
* ``problem.solve_batched(x0_stack, xf_stack)`` — the library owns the batch
|
|
10
|
+
axis, so the whole vmapped loop is a single function that ``jax.export`` can
|
|
11
|
+
serialize. Under ``save_compiled=True`` it is written to the solver cache on
|
|
12
|
+
the first run and deserialized on later runs, skipping that compile. Reach
|
|
13
|
+
for it when cross-process cold-start dominates (CI sweeps, short-lived
|
|
14
|
+
workers); reach for ``jax.vmap(solve_jax)`` when you stay inside one program.
|
|
15
|
+
|
|
16
|
+
Run with ``python examples/abstract/brachistochrone_batched.py``. Re-running is
|
|
17
|
+
itself the cross-process demo: the second invocation deserializes the cached
|
|
18
|
+
``solve_batched`` artifact instead of compiling it.
|
|
11
19
|
|
|
12
20
|
Notes:
|
|
13
21
|
|
|
14
22
|
* Under the QPAX backend used here, vmap'd subproblem solves run in
|
|
15
23
|
parallel (no host callback). The CVXPy backend is single-threaded under
|
|
16
24
|
vmap because :func:`jax.pure_callback` uses ``vmap_method="sequential"``
|
|
17
|
-
for thread safety — see :meth:`solve_jax`'s docstring.
|
|
25
|
+
for thread safety — see :meth:`solve_jax`'s docstring. That same host
|
|
26
|
+
callback is why ``solve_batched(save_compiled=True)`` refuses CVXPy: it
|
|
27
|
+
cannot be exported.
|
|
18
28
|
* The Moreau warm-start is bypassed on the JAX-pure path (its carry is
|
|
19
29
|
host-side state that ``lax.while_loop`` doesn't thread). Use QPAX or
|
|
20
30
|
CVXPy when you want batched solves.
|
|
@@ -113,14 +123,33 @@ if __name__ == "__main__":
|
|
|
113
123
|
shifts = jnp.array([0.0, 0.3, 0.6, 0.9])
|
|
114
124
|
x_initial_stack = jnp.stack([default_pin.at[0].set(default_pin[0] + s) for s in shifts])
|
|
115
125
|
|
|
116
|
-
#
|
|
126
|
+
# --- caller owns the batch axis: jax.vmap(solve_jax) ---------------------
|
|
127
|
+
# Bare ``jax.vmap`` — composes like any JAX transform, in-process only.
|
|
117
128
|
batched_solve = jax.vmap(problem.solve_jax, in_axes=(0, None, None))
|
|
118
129
|
results = batched_solve(x_initial_stack, None, None)
|
|
119
130
|
|
|
120
131
|
# ``results`` is a batched ``OptimizationResults`` pytree — every child
|
|
121
132
|
# (``X``, ``U``, ``t_final``, ``converged``, ...) has a leading batch axis.
|
|
122
|
-
print(f"
|
|
133
|
+
print(f"jax.vmap(solve_jax) over {x_initial_stack.shape[0]} initial conditions:")
|
|
123
134
|
print(f" result.x.shape: {results.x.shape}")
|
|
124
135
|
print(f" result.u.shape: {results.u.shape}")
|
|
125
136
|
print(f" result.t_final: {np.asarray(results.t_final).reshape(-1)}")
|
|
126
137
|
print(f" result.converged: {np.asarray(results.converged)}")
|
|
138
|
+
|
|
139
|
+
# --- library owns the batch axis: solve_batched, disk-cached -------------
|
|
140
|
+
# The same batched solve, but ``solve_batched`` owns the vmap so the whole
|
|
141
|
+
# loop is one exportable artifact. Under ``save_compiled=True`` it lands in
|
|
142
|
+
# the solver cache on the first run; re-run this script (a fresh process) to
|
|
143
|
+
# see it deserialized instead of recompiled. ``xf`` is shared across the
|
|
144
|
+
# batch, so broadcast the default terminal pin to the same leading axis.
|
|
145
|
+
export_problem = build_problem()
|
|
146
|
+
export_problem.settings.sim.save_compiled = True
|
|
147
|
+
export_problem.initialize()
|
|
148
|
+
|
|
149
|
+
x_term_stack = jnp.broadcast_to(export_problem.state.x_term_pin, x_initial_stack.shape)
|
|
150
|
+
batched = export_problem.solve_batched(x_initial_stack, x_term_stack)
|
|
151
|
+
|
|
152
|
+
print(f"\nsolve_batched (save_compiled=True) over {x_initial_stack.shape[0]} ICs:")
|
|
153
|
+
print(f" result.x.shape: {batched.x.shape}")
|
|
154
|
+
print(f" result.t_final: {np.asarray(batched.t_final).reshape(-1)}")
|
|
155
|
+
print(f" result.converged: {np.asarray(batched.converged)}")
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Batched 2D obstacle avoidance: solve over B random initial positions.
|
|
2
|
+
|
|
3
|
+
Same planar double-integrator + CTCS obstacle-avoidance problem as
|
|
4
|
+
``2d_obstacle_avoidance.py``, but ``solve_batched`` runs all B solves in one
|
|
5
|
+
compiled dispatch (Moreau backend). The initial position is a ``Parameter``
|
|
6
|
+
pinned via ``(position == initial_position).convex().at([0])``. Pass
|
|
7
|
+
``parameters={"initial_position": ic_batch}`` (shape ``(B, 2)``) to pin each
|
|
8
|
+
IC at node 0. Per-batch ``x_guess`` stacks
|
|
9
|
+
(a position linspace from each IC to the shared target) seed the SCP iterate
|
|
10
|
+
independently. ``post_process_batched`` then propagates
|
|
11
|
+
every converged solution through the full nonlinear dynamics.
|
|
12
|
+
|
|
13
|
+
All B trajectories are visualised together with Plotly — converged runs in
|
|
14
|
+
blue, diverged in orange — on top of the obstacle field.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
23
|
+
grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
|
|
24
|
+
sys.path.append(grandparent_dir)
|
|
25
|
+
|
|
26
|
+
import openscvx as ox
|
|
27
|
+
from openscvx import Problem
|
|
28
|
+
|
|
29
|
+
# ── Batch configuration ────────────────────────────────────────────────────────
|
|
30
|
+
N_BATCH = 200 # number of initial conditions to solve simultaneously
|
|
31
|
+
N = 40 # SCP discretisation nodes
|
|
32
|
+
TOTAL_TIME = 120.0
|
|
33
|
+
|
|
34
|
+
# ── Obstacle field (identical to 2d_obstacle_avoidance.py) ────────────────────
|
|
35
|
+
obstacle_radius_min, obstacle_radius_max = 1.0, 2.5
|
|
36
|
+
|
|
37
|
+
np.random.seed(42)
|
|
38
|
+
obstacle_centers_list = []
|
|
39
|
+
n_rows, n_cols = 20, 20
|
|
40
|
+
for i in range(n_rows):
|
|
41
|
+
for j in range(n_cols):
|
|
42
|
+
x = -6.0 + i * 6.0
|
|
43
|
+
y = -7.5 + j * 5.0
|
|
44
|
+
x += np.random.uniform(-1.0, 1.0)
|
|
45
|
+
y += np.random.uniform(-1.0, 1.0)
|
|
46
|
+
obstacle_centers_list.append([x, y])
|
|
47
|
+
|
|
48
|
+
obstacle_centers = np.array(obstacle_centers_list) # (n_obs, 2)
|
|
49
|
+
obstacle_radii = np.random.uniform(
|
|
50
|
+
obstacle_radius_min, obstacle_radius_max, size=len(obstacle_centers_list)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# ── States ─────────────────────────────────────────────────────────────────────
|
|
54
|
+
_default_ic = np.array([-10.0, -10.0]) # used for guess; actual IC set by ZeroConeConstraint
|
|
55
|
+
|
|
56
|
+
position = ox.State("position", shape=(2,))
|
|
57
|
+
position.max = np.array([150.0, 150.0])
|
|
58
|
+
position.min = np.array([-15.0, -15.0])
|
|
59
|
+
position.initial = [ox.Free(-10.0), ox.Free(-10.0)]
|
|
60
|
+
position.final = np.array([100.0, 100.0])
|
|
61
|
+
|
|
62
|
+
velocity = ox.State("velocity", shape=(2,))
|
|
63
|
+
velocity.max = np.array([10.0, 10.0])
|
|
64
|
+
velocity.min = np.array([-10.0, -10.0])
|
|
65
|
+
velocity.initial = np.array([0.0, 0.0])
|
|
66
|
+
velocity.final = [("free", 0.0), ("free", 0.0)]
|
|
67
|
+
|
|
68
|
+
# ── Controls ───────────────────────────────────────────────────────────────────
|
|
69
|
+
force = ox.Control("force", shape=(2,))
|
|
70
|
+
a_max = 20.0
|
|
71
|
+
force.max = np.array([a_max, a_max])
|
|
72
|
+
force.min = np.array([-a_max, -a_max])
|
|
73
|
+
|
|
74
|
+
m = 1.0
|
|
75
|
+
|
|
76
|
+
# ── Batched parameter: initial position ───────────────────────────────────────
|
|
77
|
+
initial_position = ox.Parameter("initial_position", shape=(2,), value=_default_ic)
|
|
78
|
+
|
|
79
|
+
# ── Dynamics ───────────────────────────────────────────────────────────────────
|
|
80
|
+
dynamics = {
|
|
81
|
+
"position": velocity,
|
|
82
|
+
"velocity": (1.0 / m) * force,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# ── Constraints ────────────────────────────────────────────────────────────────
|
|
86
|
+
states = [position, velocity]
|
|
87
|
+
controls = [force]
|
|
88
|
+
constraints = []
|
|
89
|
+
|
|
90
|
+
for state in states:
|
|
91
|
+
constraints.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
|
|
92
|
+
|
|
93
|
+
constraints.extend([force <= force.max, force.min <= force])
|
|
94
|
+
|
|
95
|
+
obstacle_avoidance = ox.ctcs(
|
|
96
|
+
obstacle_radii
|
|
97
|
+
<= ox.Vmap(
|
|
98
|
+
lambda obs_center: ox.linalg.Norm(position - obs_center),
|
|
99
|
+
batch=obstacle_centers,
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
constraints.append(obstacle_avoidance)
|
|
103
|
+
|
|
104
|
+
# ── Constraints ── initial position pinned via convex equality at node 0 ───────
|
|
105
|
+
constraints.append((position == initial_position).convex().at([0]))
|
|
106
|
+
|
|
107
|
+
# ── Initial guesses ────────────────────────────────────────────────────────────
|
|
108
|
+
position.guess = np.linspace(_default_ic, position.final, N)
|
|
109
|
+
velocity.guess = np.zeros((N, 2))
|
|
110
|
+
force.guess = np.zeros((N, 2))
|
|
111
|
+
|
|
112
|
+
# ── Time ───────────────────────────────────────────────────────────────────────
|
|
113
|
+
time_var = ox.Time(
|
|
114
|
+
initial=0.0,
|
|
115
|
+
final=("minimize", TOTAL_TIME),
|
|
116
|
+
min=0.0,
|
|
117
|
+
max=TOTAL_TIME,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# ── Problem ────────────────────────────────────────────────────────────────────
|
|
121
|
+
# Moreau backend: JAX-native, exportable, required for solve_batched.
|
|
122
|
+
problem = Problem(
|
|
123
|
+
dynamics=dynamics,
|
|
124
|
+
states=states,
|
|
125
|
+
controls=controls,
|
|
126
|
+
time=time_var,
|
|
127
|
+
constraints=constraints,
|
|
128
|
+
N=N,
|
|
129
|
+
algorithm={
|
|
130
|
+
"autotuner": ox.RampProximalWeight(ramp_factor=1.1, lam_prox_max=1e3),
|
|
131
|
+
"lam_cost": 1e-2,
|
|
132
|
+
"lam_vc": 1e1,
|
|
133
|
+
},
|
|
134
|
+
solver=ox.MoreauPTRSolver(),
|
|
135
|
+
float_dtype="float64",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ── Helper: sample valid initial positions ─────────────────────────────────────
|
|
140
|
+
def sample_initial_positions(
|
|
141
|
+
B: int,
|
|
142
|
+
seed: int = 0,
|
|
143
|
+
x_range: tuple[float, float] = (-14.0, 20.0),
|
|
144
|
+
y_range: tuple[float, float] = (-14.0, 20.0),
|
|
145
|
+
safety_margin: float = 0.5,
|
|
146
|
+
) -> np.ndarray:
|
|
147
|
+
"""Sample B positions in [x_range × y_range] that lie outside all obstacles.
|
|
148
|
+
|
|
149
|
+
The default range covers the lower-left quadrant of the obstacle field,
|
|
150
|
+
giving varied starting positions from far-corner approaches to positions
|
|
151
|
+
already adjacent to the first few rows of obstacles.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
B: Number of positions to sample.
|
|
155
|
+
seed: RNG seed.
|
|
156
|
+
x_range: (min, max) for the x coordinate.
|
|
157
|
+
y_range: (min, max) for the y coordinate.
|
|
158
|
+
safety_margin: Extra clearance beyond the obstacle radius.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Array of shape ``(B, 2)``.
|
|
162
|
+
"""
|
|
163
|
+
rng = np.random.default_rng(seed)
|
|
164
|
+
positions: list[np.ndarray] = []
|
|
165
|
+
while len(positions) < B:
|
|
166
|
+
p = rng.uniform(
|
|
167
|
+
[x_range[0], y_range[0]],
|
|
168
|
+
[x_range[1], y_range[1]],
|
|
169
|
+
)
|
|
170
|
+
dists = np.linalg.norm(obstacle_centers - p, axis=1)
|
|
171
|
+
if np.all(dists > obstacle_radii + safety_margin):
|
|
172
|
+
positions.append(p)
|
|
173
|
+
return np.array(positions)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def build_batched_x_guess(problem, ic_batch: np.ndarray) -> np.ndarray:
|
|
177
|
+
"""Build per-batch state guesses with position linspace IC → target."""
|
|
178
|
+
base_x = np.asarray(problem.state.x) # (N, n_x)
|
|
179
|
+
pos_sl = position._slice
|
|
180
|
+
final_pos = np.asarray(position.final)
|
|
181
|
+
B = ic_batch.shape[0]
|
|
182
|
+
x_guess_batch = np.broadcast_to(base_x, (B,) + base_x.shape).copy()
|
|
183
|
+
t = np.linspace(0.0, 1.0, N)
|
|
184
|
+
x_guess_batch[:, :, pos_sl] = (
|
|
185
|
+
ic_batch[:, None, :] * (1.0 - t[None, :, None])
|
|
186
|
+
+ final_pos[None, None, :] * t[None, :, None]
|
|
187
|
+
)
|
|
188
|
+
return x_guess_batch
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ── Visualisation ──────────────────────────────────────────────────────────────
|
|
192
|
+
def plot_batched_2d_trajectories(results, ic_batch: np.ndarray):
|
|
193
|
+
"""Plotly figure overlaying all B propagated trajectories on the obstacle field.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
results: :class:`~openscvx.algorithms.OptimizationResults` returned by
|
|
197
|
+
:meth:`~openscvx.problem.Problem.post_process_batched`. Must have
|
|
198
|
+
``trajectory["position"]`` of shape ``(B, T, 2)``.
|
|
199
|
+
ic_batch: ``(B, 2)`` array of initial positions used for the batch.
|
|
200
|
+
"""
|
|
201
|
+
import plotly.graph_objects as go
|
|
202
|
+
|
|
203
|
+
converged = np.asarray(results.converged, dtype=bool).reshape(-1)
|
|
204
|
+
B = len(converged)
|
|
205
|
+
|
|
206
|
+
# Extract propagated positions: (B, T, 2)
|
|
207
|
+
pos_all = np.asarray(results.trajectory["position"], dtype=np.float64)
|
|
208
|
+
if pos_all.ndim == 2:
|
|
209
|
+
# Fallback: single-batch dim was squeezed
|
|
210
|
+
pos_all = pos_all[np.newaxis]
|
|
211
|
+
|
|
212
|
+
fig = go.Figure()
|
|
213
|
+
|
|
214
|
+
# ── Background obstacles ──────────────────────────────────────────────────
|
|
215
|
+
for center, radius in zip(obstacle_centers, obstacle_radii):
|
|
216
|
+
xc, yc = center
|
|
217
|
+
fig.add_shape(
|
|
218
|
+
type="circle",
|
|
219
|
+
x0=xc - radius,
|
|
220
|
+
y0=yc - radius,
|
|
221
|
+
x1=xc + radius,
|
|
222
|
+
y1=yc + radius,
|
|
223
|
+
fillcolor="rgba(220, 180, 180, 0.30)",
|
|
224
|
+
line={"color": "rgba(200, 120, 120, 0.45)", "width": 1},
|
|
225
|
+
layer="below",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# ── Trajectories ──────────────────────────────────────────────────────────
|
|
229
|
+
# Draw diverged runs first so converged runs render on top.
|
|
230
|
+
for b in range(B):
|
|
231
|
+
ok = bool(converged[b])
|
|
232
|
+
color = "rgba(30, 130, 220, 0.65)" if ok else "rgba(220, 100, 30, 0.50)"
|
|
233
|
+
pos = pos_all[b] # (T, 2)
|
|
234
|
+
fig.add_trace(
|
|
235
|
+
go.Scatter(
|
|
236
|
+
x=pos[:, 0],
|
|
237
|
+
y=pos[:, 1],
|
|
238
|
+
mode="lines",
|
|
239
|
+
line={"color": color, "width": 1.5 if ok else 1.0},
|
|
240
|
+
showlegend=False,
|
|
241
|
+
hovertemplate=f"run {b} {'✓' if ok else '✗'}<extra></extra>",
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# ── Start markers (one per batch element) ─────────────────────────────────
|
|
246
|
+
starts_ok = ic_batch[converged]
|
|
247
|
+
starts_bad = ic_batch[~converged]
|
|
248
|
+
if len(starts_ok):
|
|
249
|
+
fig.add_trace(
|
|
250
|
+
go.Scatter(
|
|
251
|
+
x=starts_ok[:, 0],
|
|
252
|
+
y=starts_ok[:, 1],
|
|
253
|
+
mode="markers",
|
|
254
|
+
name="Start (converged)",
|
|
255
|
+
marker={"color": "rgba(20, 100, 200, 0.9)", "size": 7, "symbol": "circle"},
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
if len(starts_bad):
|
|
259
|
+
fig.add_trace(
|
|
260
|
+
go.Scatter(
|
|
261
|
+
x=starts_bad[:, 0],
|
|
262
|
+
y=starts_bad[:, 1],
|
|
263
|
+
mode="markers",
|
|
264
|
+
name="Start (diverged)",
|
|
265
|
+
marker={"color": "rgba(200, 80, 20, 0.9)", "size": 7, "symbol": "circle-open"},
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# ── Target ────────────────────────────────────────────────────────────────
|
|
270
|
+
tgt = position.final
|
|
271
|
+
fig.add_trace(
|
|
272
|
+
go.Scatter(
|
|
273
|
+
x=[tgt[0]],
|
|
274
|
+
y=[tgt[1]],
|
|
275
|
+
mode="markers",
|
|
276
|
+
name="Target",
|
|
277
|
+
marker={"color": "black", "size": 12, "symbol": "star"},
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# ── Layout ────────────────────────────────────────────────────────────────
|
|
282
|
+
n_ok = int(converged.sum())
|
|
283
|
+
fig.update_layout(
|
|
284
|
+
title=f"Batched 2-D obstacle avoidance — {B} ICs | {n_ok}/{B} converged",
|
|
285
|
+
xaxis_title="x [m]",
|
|
286
|
+
yaxis_title="y [m]",
|
|
287
|
+
yaxis={"scaleanchor": "x", "scaleratio": 1},
|
|
288
|
+
template="simple_white",
|
|
289
|
+
paper_bgcolor="white",
|
|
290
|
+
plot_bgcolor="white",
|
|
291
|
+
autosize=False,
|
|
292
|
+
width=720,
|
|
293
|
+
height=720,
|
|
294
|
+
margin={"l": 60, "r": 30, "t": 50, "b": 60},
|
|
295
|
+
)
|
|
296
|
+
return fig
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# ── Main ──────────────────────────────────────────────────────────────────────
|
|
300
|
+
if __name__ == "__main__":
|
|
301
|
+
print(f"Initializing 2-D obstacle avoidance problem (Moreau, B={N_BATCH}) ...")
|
|
302
|
+
problem.initialize()
|
|
303
|
+
|
|
304
|
+
# Sample B valid initial positions
|
|
305
|
+
ic_batch = sample_initial_positions(N_BATCH)
|
|
306
|
+
print(f"Sampled {N_BATCH} initial positions in x∈[-14,20], y∈[-14,20]")
|
|
307
|
+
print(f" ic_batch.shape = {ic_batch.shape}")
|
|
308
|
+
|
|
309
|
+
# ── Compile + solve ───────────────────────────────────────────────────────
|
|
310
|
+
# B is inferred from ic_batch.shape[0]. solve_batched vmaps over
|
|
311
|
+
# initial_position (shape (B, 2)) and the per-batch x_guess stack.
|
|
312
|
+
x_guess_batch = build_batched_x_guess(problem, ic_batch)
|
|
313
|
+
print("Compiling and running solve_batched …")
|
|
314
|
+
results = problem.solve_batched(
|
|
315
|
+
parameters={"initial_position": ic_batch},
|
|
316
|
+
x_guess=x_guess_batch,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# ── Post-process ──────────────────────────────────────────────────────────
|
|
320
|
+
print("Post-processing (nonlinear propagation) …")
|
|
321
|
+
results = problem.post_process_batched(results)
|
|
322
|
+
|
|
323
|
+
# ── Plot ──────────────────────────────────────────────────────────────────
|
|
324
|
+
print("Plotting …")
|
|
325
|
+
fig = plot_batched_2d_trajectories(results, ic_batch)
|
|
326
|
+
fig.show()
|