openscvx 0.5.3.dev48__tar.gz → 0.5.3.dev50__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.dev48/openscvx.egg-info → openscvx-0.5.3.dev50}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/01_hello_world_brachistochrone.md +29 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/_version.py +3 -3
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/problem.py +5 -1
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/builder.py +18 -2
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/time.py +72 -15
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/variable.py +68 -12
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/state.py +20 -2
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/preprocessing.py +159 -4
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx.egg-info/SOURCES.txt +1 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx.egg-info/scm_file_list.json +1 -0
- openscvx-0.5.3.dev50/openscvx.egg-info/scm_version.json +8 -0
- openscvx-0.5.3.dev50/tests/symbolic/expr/test_guess_expr.py +502 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_math.py +4 -2
- openscvx-0.5.3.dev48/openscvx.egg-info/scm_version.json +0 -8
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/_extra.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/docs.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/lint.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/release.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/tests-cvxpygen.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/tests-examples.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/tests-lie.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/tests-mjx.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/tests-qpax.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.github/workflows/tests.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.gitignore +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/.gitmodules +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/CITATION.cff +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/CONTRIBUTING.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/LICENSE +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/README.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/Foundations/discretization.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UnderTheHood/batching_jit_grad.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UnderTheHood/custom_algorithms_autotuners.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UnderTheHood/lowering_architecture.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/08_mpcc.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/favicon.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/viser-client/index.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/viser-recordings/drone_racing.viser +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/changelog.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/citation.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/examples.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/index.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/installation.md +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/llms.txt +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/versions.json +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/_viser_embed_export.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/abstract/brachistochrone_batched.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/abstract/flappy_bird.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/abstract/hypersensitive.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/abstract/impulsive.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/abstract/stl_integer_variable.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/abstract/stl_or.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/aircraft/dynamic_soaring.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/aircraft/supersonic_time_to_climb.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/_camera.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/_render.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/_sensor_view.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/logo.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/animations/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/arm/3_dof_arm.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/arm/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/arm/7_dof_arm_collision.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/arm/7_dof_arm_vp.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/arm/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/arm/franka_fr3v2_viewplanning_nodal.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/car/dubins_car.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/car/dubins_car_obstacle_conditional.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/car/dubins_car_obstacle_stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/car/dubins_car_stl_or.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/car/dubins_car_waypoint_stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/double_integrator/_plotting.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/double_integrator/moving_safe_zones.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/2d_obstacle_avoidance_batched_ic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/boresight_trace_mpcc.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/cinema_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/drone_racing_batched_gates.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/logo.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/logo_utils/circle.svg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/logo_utils/openscvx_logo_single.svg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/logo_utils/quadrotor_mesh.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/logo_utils/svg_path_utils.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/multi_agent_circle_swap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/obstacle_avoidance_vmap_2d.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/drone/openscvx_logo.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/frax/panda_frax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/frax/panda_frax_pick_place.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/frax/panda_frax_viewplanning.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/frax/panda_frax_waypoint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/frax/ur5e_assets/ur5e.urdf +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/frax/ur5e_pen_trace.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/frax/ur5e_pen_trace_mpcc.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mjx/cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mjx/double_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mjx/skydio_x2_mjx.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mjx/triple_cartpole_game.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mjx/triple_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mpc/double_integrator_discrete.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mpc/double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mpc/dubins_car_circle_analytical.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mpc/dubins_car_circle_discrete.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/plotting.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/plotting_viser.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/3DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/6DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/rocket/6DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/rocket/6DoF_pdg_batched_ic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/rocket/ascent_launch_vehicle.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/spacecraft/halo_orbit.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/spacecraft/hohmann_transfer.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/spacecraft/let_transfer.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/examples/spacecraft/relative_loitering.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/figures/video_preview.png +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/assets/stylesheets/home-hero.css +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/assets/stylesheets/home-viser.css +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/home.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/main.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/partials/home-diagram.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/partials/home-dropin-banner.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/partials/home-hero.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/partials/home-pipeline.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/partials/home-viser-strip.html +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/material/overrides/sitemap.xml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/mkdocs.yml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/__main__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/autotuner/acceptance_ratio.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/autotuner/base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/history.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/hyperparams.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/loop.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/optimization_results.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/penalty.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/scvx/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/scvx/iteration.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/state.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/algorithms/weights.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/config.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/discretize_linearize.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/linearize_discretize.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/sparse_utils/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/expert/byof.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/expert/lowering.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/expert/validation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/init/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/init/inverse_kinematics.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrations/_utils.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrations/base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrations/frax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrations/menagerie.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrations/mjx.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrators/diffrax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/loader.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/cvxpy_variables.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/problem.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/lowered/unified.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/plotting.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/publication.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/animated.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/coordinates.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/orbits.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/propagation/post_processing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/propagation/propagation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/solvers/base.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/solvers/cones.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/solvers/moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/solvers/ptr_solver.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/solvers/qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/affine.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/augmentation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/canonicalize.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/control.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/expr.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/parameter.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/stljax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lower.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/_lowerer.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/_registry.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/arithmetic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/array.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/constraint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/control.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/expr.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/formulation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/lie.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/linalg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/logic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/math.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/spatial.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/state.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/lowerers/latex/vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/stljax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/problem.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/symbolic/unified.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/utils/cache.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/utils/caching.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/utils/printing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx/utils/utils.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/pyproject.toml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/scripts/mkdocs_seo_hook.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/setup.cfg +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/_iteration_helpers.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/autotuner/test_defaults_agreement.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/autotuner/test_hyper_overrides.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/test_iteration_fn_jit.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/test_iteration_fn_parity.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/algorithms/test_make_solve_loop.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/conftest.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_impulsive.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_multishot_propagation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_solve_batched_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_solve_batched_cvxpy_export_errors.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_solve_batched_export_roundtrip.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_solve_jax_bare_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_solve_jax_jit_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_solve_jax_vmap_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/e2e/test_solve_jax_vmap_converged_no_drift.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/hohmann_analytical.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/integrations/test_mjx.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/integrations/test_mjx_dynamics.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/_iteration_callback_helpers.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_iteration_callback_moreau.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_iteration_callback_qpax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_iteration_callback_vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/solvers/test_subproblem_pytree.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_variable.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_augmentation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_gmsr.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_latex_formulation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_lower_latex.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_preprocessing.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/symbolic/test_unified.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_autotuning.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_discretization.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_examples.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_expert.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_init.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_initialize_syncs_guesses.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_integrators.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_loader.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_optimization_results.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_plotting.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_post_process_batched_tau_clamp.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_propagation.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_results_parameters.py +0 -0
- {openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/tests/test_solve_batched_inference.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.dev50
|
|
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
|
{openscvx-0.5.3.dev48 → openscvx-0.5.3.dev50}/docs/UsersGuide/01_hello_world_brachistochrone.md
RENAMED
|
@@ -164,6 +164,35 @@ Here, we do _not_ specify an initial or final value for the control but we _do_
|
|
|
164
164
|
The initial guess must be of shape $(n_u \times n_{\mathrm{nodes}})$, providing an initial guess for each control at every node.
|
|
165
165
|
Technically, we can also provide a `.guess` for states. However, these default to a linear interpolation between initial and final conditions which is sufficient in most cases.
|
|
166
166
|
|
|
167
|
+
#### Symbolic initial guesses
|
|
168
|
+
|
|
169
|
+
`.guess` accepts three forms, on both states and controls:
|
|
170
|
+
|
|
171
|
+
- an array of shape $(n_{\mathrm{nodes}} \times n)$, as above;
|
|
172
|
+
- a **symbolic expression** in other states and controls;
|
|
173
|
+
- a **callable of `tau`**, the normalized node coordinate, returning a symbolic expression.
|
|
174
|
+
|
|
175
|
+
A guess expression is evaluated at every node, with each referenced variable at its guess value and `tau` running $0 \to 1$ across the grid — the same per-node mental model as dynamics and constraints.
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# Cross-variable: the speed guess follows whatever velocity's guess is
|
|
179
|
+
speed.guess = ox.linalg.Norm(velocity)
|
|
180
|
+
|
|
181
|
+
# Along the grid: a straight line from p0 to pf
|
|
182
|
+
position.guess = lambda tau: p0 + (pf - p0) * tau
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
References between guesses are the dependency graph: `speed` above is resolved after `velocity`, and a circular reference raises. Everything is evaluated once, when the `Problem` is built (and again on `reset()` if you assign a new expression between solves), so the solver only ever sees ordinary arrays.
|
|
186
|
+
|
|
187
|
+
Because evaluation is per node, guess expressions cannot express trajectory-level constructions such as finite differences or cumulative sums. Build those eagerly with NumPy instead:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
velocity.guess = np.gradient(position.guess, axis=0)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
!!! Note
|
|
194
|
+
`time.guess` accepts all three forms, but `time_dilation_guess` remains array-only.
|
|
195
|
+
|
|
167
196
|
### Dynamics
|
|
168
197
|
|
|
169
198
|
Dynamics are defined as a dictionary mapping state names to their time derivatives using symbolic expressions:
|
|
@@ -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.dev50'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 3, 'dev50')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g7ab51dee3'
|
|
@@ -75,6 +75,7 @@ from openscvx.symbolic.expr.control import Control
|
|
|
75
75
|
from openscvx.symbolic.expr.state import State
|
|
76
76
|
from openscvx.symbolic.expr.time import Time
|
|
77
77
|
from openscvx.symbolic.lower import lower_symbolic_problem
|
|
78
|
+
from openscvx.symbolic.preprocessing import resolve_guess_exprs
|
|
78
79
|
from openscvx.symbolic.problem import SymbolicProblem
|
|
79
80
|
from openscvx.utils import printing, profiling
|
|
80
81
|
from openscvx.utils.caching import (
|
|
@@ -958,7 +959,8 @@ class Problem:
|
|
|
958
959
|
This method reads the current `.guess` values from the original State and
|
|
959
960
|
Control objects and updates the unified representations. This enables warm-starting
|
|
960
961
|
workflows where the initial trajectory guess is updated between solves (e.g., shifting
|
|
961
|
-
the previous solution).
|
|
962
|
+
the previous solution). Guesses re-assigned symbolically between solves are
|
|
963
|
+
evaluated on the node grid first.
|
|
962
964
|
|
|
963
965
|
Note:
|
|
964
966
|
This only updates the unified representation. The AlgorithmState is
|
|
@@ -968,6 +970,8 @@ class Problem:
|
|
|
968
970
|
if self._lowered is None:
|
|
969
971
|
return
|
|
970
972
|
|
|
973
|
+
resolve_guess_exprs(self.symbolic.states, self.symbolic.controls, self.symbolic.N)
|
|
974
|
+
|
|
971
975
|
# Sync optimization state guesses
|
|
972
976
|
for state in self.symbolic.states:
|
|
973
977
|
if state.guess is not None:
|
|
@@ -48,6 +48,7 @@ from openscvx.symbolic.preprocessing import (
|
|
|
48
48
|
collect_and_assign_slices,
|
|
49
49
|
convert_dynamics_dict_to_expr,
|
|
50
50
|
fill_default_guesses,
|
|
51
|
+
resolve_guess_exprs,
|
|
51
52
|
validate_and_normalize_constraint_nodes,
|
|
52
53
|
validate_boundary_conditions,
|
|
53
54
|
validate_bounds,
|
|
@@ -220,8 +221,9 @@ def preprocess_symbolic_problem(
|
|
|
220
221
|
states = list(states) + [time]
|
|
221
222
|
time_state = time
|
|
222
223
|
|
|
223
|
-
# Fill in default guess if needed (for Time instances)
|
|
224
|
-
|
|
224
|
+
# Fill in default guess if needed (for Time instances); a symbolic time guess
|
|
225
|
+
# is resolved in Phase 2, so it must not be overwritten here.
|
|
226
|
+
if isinstance(time_state, Time) and time_state.guess is None and time_state._guess_expr is None:
|
|
225
227
|
time_state.guess = time_state._generate_default_guess(N)
|
|
226
228
|
|
|
227
229
|
# Add CTCS constraints for time bounds
|
|
@@ -287,6 +289,9 @@ def preprocess_symbolic_problem(
|
|
|
287
289
|
# ==================== PHASE 2: Expression Validation ====================
|
|
288
290
|
# Validate all expressions (use unsorted constraints)
|
|
289
291
|
collect_and_assign_slices(states, controls)
|
|
292
|
+
# Evaluate symbolic guesses now that slices exist and time has been added, and
|
|
293
|
+
# before augmentation derives the time-dilation guess from time's guess numerically.
|
|
294
|
+
resolve_guess_exprs(states, controls, N)
|
|
290
295
|
validate_constraints_at_root(constraints.unsorted)
|
|
291
296
|
validate_and_normalize_constraint_nodes(constraints.unsorted, N)
|
|
292
297
|
validate_dynamics_dimension(dynamics_concat, states)
|
|
@@ -521,6 +526,17 @@ def add_propagation_states(
|
|
|
521
526
|
|
|
522
527
|
# ==================== PHASE 1: Validate Extra States ====================
|
|
523
528
|
|
|
529
|
+
# Propagation-only states are integrated from their initial condition, never
|
|
530
|
+
# seeded from a guess trajectory, so a symbolic guess has nothing to resolve on.
|
|
531
|
+
for state in states_extra:
|
|
532
|
+
if state._guess_expr is not None:
|
|
533
|
+
raise ValueError(
|
|
534
|
+
f"State '{state.name}': symbolic initial guesses are not supported for "
|
|
535
|
+
f"propagation-only states. These states are integrated forward from "
|
|
536
|
+
f"their initial condition after the solve, so they carry no guess "
|
|
537
|
+
f"trajectory."
|
|
538
|
+
)
|
|
539
|
+
|
|
524
540
|
# Validate that extra states don't conflict with optimization state names
|
|
525
541
|
opt_state_names = {s.name for s in states_opt}
|
|
526
542
|
extra_state_names = {s.name for s in states_extra}
|
|
@@ -1,11 +1,57 @@
|
|
|
1
|
-
from typing import Any, List, Optional, Union
|
|
1
|
+
from typing import Any, List, Optional, Tuple, Union
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
from pydantic import ConfigDict, field_validator
|
|
5
5
|
|
|
6
|
+
from openscvx.symbolic.expr.expr import Expr, Leaf
|
|
6
7
|
from openscvx.symbolic.expr.state import State, StateSpec
|
|
7
8
|
from openscvx.symbolic.expr.variable import Variable
|
|
8
9
|
|
|
10
|
+
#: Key under which the normalized node grid is passed to a lowered guess expression.
|
|
11
|
+
TAU_PARAM = "__tau__"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Tau(Leaf):
|
|
15
|
+
"""Normalized node coordinate, running 0 at the first node to 1 at the last.
|
|
16
|
+
|
|
17
|
+
``Tau`` is internal plumbing for initial guesses, not part of the user-facing
|
|
18
|
+
vocabulary. A user who needs the node coordinate writes their guess as a
|
|
19
|
+
callable of one argument::
|
|
20
|
+
|
|
21
|
+
pos.guess = lambda tau: p0 + (pf - p0) * tau
|
|
22
|
+
|
|
23
|
+
and the library applies that callable once, at assignment, with a ``Tau``
|
|
24
|
+
instance. The result is an ordinary expression that the guess-resolution pass
|
|
25
|
+
evaluates node by node (see
|
|
26
|
+
:func:`openscvx.symbolic.preprocessing.resolve_guess_exprs`).
|
|
27
|
+
|
|
28
|
+
Physical time is a ``Time`` state; ``Tau`` is the dimensionless grid
|
|
29
|
+
coordinate the trajectory is discretized on, and is meaningful only while
|
|
30
|
+
building a guess.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
super().__init__("tau", shape=())
|
|
35
|
+
|
|
36
|
+
def check_shape(self) -> Tuple[int, ...]:
|
|
37
|
+
"""Reject tau outside of an initial-guess expression.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
ValueError: Always. Dynamics, constraints, and costs are shape-checked
|
|
41
|
+
eagerly, so this fires exactly when a captured ``tau`` has leaked
|
|
42
|
+
out of the guess it was created for.
|
|
43
|
+
"""
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"tau is only meaningful inside initial-guess expressions. It was received "
|
|
46
|
+
"by a `guess = lambda tau: ...` callable and then used in dynamics, a "
|
|
47
|
+
"constraint, or a cost, where there is no node grid to evaluate it on. "
|
|
48
|
+
"Use the `time` state for physical time, or index nodes explicitly with "
|
|
49
|
+
"`.at(k)`."
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def __repr__(self) -> str:
|
|
53
|
+
return "Tau()"
|
|
54
|
+
|
|
9
55
|
|
|
10
56
|
class Time(State):
|
|
11
57
|
"""Time state variable for trajectory optimization.
|
|
@@ -92,15 +138,17 @@ class Time(State):
|
|
|
92
138
|
min: Minimum time bound.
|
|
93
139
|
max: Maximum time bound.
|
|
94
140
|
guess: Initial guess for the time trajectory. 1D array of shape
|
|
95
|
-
(N,)
|
|
141
|
+
(N,), 2D of shape (N, 1), or any symbolic form accepted by
|
|
142
|
+
:attr:`Variable.guess`. If not provided, a linear
|
|
96
143
|
interpolation from initial to final is generated.
|
|
97
144
|
time_dilation_min: Absolute minimum bound for time dilation.
|
|
98
145
|
Defaults to `0.3 * time_final` if not set.
|
|
99
146
|
time_dilation_max: Absolute maximum bound for time dilation.
|
|
100
147
|
Defaults to `3.0 * time_final` if not set.
|
|
101
148
|
time_dilation_guess: Initial guess for time dilation.
|
|
102
|
-
1D array of shape (N,) or 2D of shape (N, 1)
|
|
103
|
-
|
|
149
|
+
1D array of shape (N,) or 2D of shape (N, 1) — arrays only,
|
|
150
|
+
symbolic guesses are not accepted here. Overrides the default
|
|
151
|
+
finite-difference computation.
|
|
104
152
|
uniform_time_grid: If True, constrain the time dilation to be
|
|
105
153
|
the same across all nodes (uniform time steps). Defaults
|
|
106
154
|
to False.
|
|
@@ -125,11 +173,7 @@ class Time(State):
|
|
|
125
173
|
|
|
126
174
|
super().__init__("time", shape=(1,), min=min, max=max, initial=initial, final=final)
|
|
127
175
|
|
|
128
|
-
# guess: normalize 1D → 2D before assigning
|
|
129
176
|
if guess is not None:
|
|
130
|
-
guess = np.asarray(guess, dtype=float)
|
|
131
|
-
if guess.ndim == 1:
|
|
132
|
-
guess = guess.reshape(-1, 1)
|
|
133
177
|
self.guess = guess
|
|
134
178
|
if time_dilation_min is not None:
|
|
135
179
|
self.time_dilation_min = time_dilation_min
|
|
@@ -167,12 +211,25 @@ class Time(State):
|
|
|
167
211
|
State.final.fset(self, val)
|
|
168
212
|
|
|
169
213
|
@Variable.guess.setter
|
|
170
|
-
def guess(self,
|
|
171
|
-
"""Set the time guess.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
214
|
+
def guess(self, value):
|
|
215
|
+
"""Set the time guess.
|
|
216
|
+
|
|
217
|
+
Accepts everything :attr:`Variable.guess` accepts — an array, a symbolic
|
|
218
|
+
expression, or a callable of tau — and additionally reshapes a 1D ``(N,)``
|
|
219
|
+
array to ``(N, 1)`` since Time is always shape ``(1,)``. A symbolic guess
|
|
220
|
+
resolving to one value per node is reshaped the same way at build time::
|
|
221
|
+
|
|
222
|
+
time.guess = lambda tau: t0 + (tf - t0) * tau**2
|
|
223
|
+
|
|
224
|
+
Note:
|
|
225
|
+
``time_dilation_guess`` is a separate, array-only setting; it is not
|
|
226
|
+
resolved from expressions.
|
|
227
|
+
"""
|
|
228
|
+
if not isinstance(value, Expr) and not callable(value):
|
|
229
|
+
value = np.asarray(value, dtype=float)
|
|
230
|
+
if value.ndim == 1:
|
|
231
|
+
value = value.reshape(-1, 1)
|
|
232
|
+
Variable.guess.fset(self, value)
|
|
176
233
|
|
|
177
234
|
@property
|
|
178
235
|
def time_dilation_min(self) -> Optional[float]:
|
|
@@ -199,7 +256,7 @@ class Time(State):
|
|
|
199
256
|
|
|
200
257
|
@property
|
|
201
258
|
def time_dilation_guess(self) -> Optional[np.ndarray]:
|
|
202
|
-
"""Initial guess for time dilation, shape (N, 1)."""
|
|
259
|
+
"""Initial guess for time dilation, shape (N, 1). Arrays only."""
|
|
203
260
|
return self._time_dilation_guess
|
|
204
261
|
|
|
205
262
|
@time_dilation_guess.setter
|
|
@@ -4,7 +4,7 @@ from typing import List, Optional, Tuple
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from pydantic import BaseModel, ConfigDict
|
|
6
6
|
|
|
7
|
-
from .expr import Leaf
|
|
7
|
+
from .expr import Expr, Leaf
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class Variable(Leaf):
|
|
@@ -32,6 +32,8 @@ class Variable(Leaf):
|
|
|
32
32
|
_min (np.ndarray | None): Minimum bounds for each element of the variable
|
|
33
33
|
_max (np.ndarray | None): Maximum bounds for each element of the variable
|
|
34
34
|
_guess (np.ndarray | None): Initial guess for the variable trajectory (n_points, n_vars)
|
|
35
|
+
_guess_expr (Expr | None): Symbolic initial guess awaiting resolution to an
|
|
36
|
+
array at problem-build time; None once resolved
|
|
35
37
|
|
|
36
38
|
Example:
|
|
37
39
|
# Typically, use State or Control instead of Variable directly:
|
|
@@ -51,6 +53,7 @@ class Variable(Leaf):
|
|
|
51
53
|
self._min = None
|
|
52
54
|
self._max = None
|
|
53
55
|
self._guess = None
|
|
56
|
+
self._guess_expr = None
|
|
54
57
|
|
|
55
58
|
def __repr__(self) -> str:
|
|
56
59
|
return f"Var({self.name!r})"
|
|
@@ -189,14 +192,16 @@ class Variable(Leaf):
|
|
|
189
192
|
|
|
190
193
|
@property
|
|
191
194
|
def guess(self) -> Optional[np.ndarray]:
|
|
192
|
-
"""Get the initial guess for the variable trajectory.
|
|
195
|
+
"""Get the initial guess for the variable trajectory, as an array.
|
|
193
196
|
|
|
194
197
|
The guess provides a starting point for the optimizer. A good initial guess
|
|
195
198
|
can significantly improve convergence speed and help avoid local minima.
|
|
196
199
|
|
|
197
200
|
Returns:
|
|
198
201
|
2D array of shape (n_points, n_vars) representing the variable trajectory
|
|
199
|
-
over time, or None if no guess is provided.
|
|
202
|
+
over time, or None if no guess is provided. A guess assigned symbolically
|
|
203
|
+
also reads back as None until the problem is built and the expression has
|
|
204
|
+
been evaluated on the node grid.
|
|
200
205
|
|
|
201
206
|
Example:
|
|
202
207
|
x = Variable("x", shape=(2,))
|
|
@@ -207,20 +212,36 @@ class Variable(Leaf):
|
|
|
207
212
|
return self._guess
|
|
208
213
|
|
|
209
214
|
@guess.setter
|
|
210
|
-
def guess(self,
|
|
215
|
+
def guess(self, value):
|
|
211
216
|
"""Set the initial guess for the variable trajectory.
|
|
212
217
|
|
|
213
|
-
|
|
214
|
-
|
|
218
|
+
Three forms are accepted:
|
|
219
|
+
|
|
220
|
+
- **Array** — a 2D ``(N, n)`` array, one row per trajectory node, validated
|
|
221
|
+
immediately.
|
|
222
|
+
- **Expression** — a symbolic expression in other states and controls. It is
|
|
223
|
+
evaluated once per node at build time, with every referenced variable at its
|
|
224
|
+
own guess value, so ``speed.guess = ox.linalg.Norm(vel)`` gives the per-node speed
|
|
225
|
+
of the velocity guess. The expression itself is the dependency graph:
|
|
226
|
+
referenced guesses are resolved first, and circular references raise.
|
|
227
|
+
- **Callable of tau** — a one-argument function returning an expression, called
|
|
228
|
+
once here with a symbolic normalized node coordinate running 0 → 1, for
|
|
229
|
+
guesses shaped along the grid rather than by other variables:
|
|
230
|
+
``pos.guess = lambda tau: p0 + (pf - p0) * tau``.
|
|
231
|
+
|
|
232
|
+
Symbolic forms are evaluated node-by-node, so they cannot express
|
|
233
|
+
trajectory-level constructions (finite differences, cumulative sums). Build
|
|
234
|
+
those eagerly with numpy instead, e.g. ``vel.guess = np.gradient(pos.guess,
|
|
235
|
+
axis=0)``.
|
|
215
236
|
|
|
216
237
|
Args:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
the solver will interpolate as needed.
|
|
238
|
+
value: An ``(N, n)`` array, an ``Expr``, or a callable taking tau and
|
|
239
|
+
returning an ``Expr``.
|
|
220
240
|
|
|
221
241
|
Raises:
|
|
222
|
-
ValueError: If
|
|
223
|
-
match the variable dimension
|
|
242
|
+
ValueError: If an array guess is not 2D or its second dimension doesn't
|
|
243
|
+
match the variable dimension, or if a callable returns a non-Expr.
|
|
244
|
+
Shape errors in symbolic guesses surface when the problem is built.
|
|
224
245
|
|
|
225
246
|
Example:
|
|
226
247
|
pos = Variable("pos", shape=(3,))
|
|
@@ -228,7 +249,29 @@ class Variable(Leaf):
|
|
|
228
249
|
n_points = 50
|
|
229
250
|
pos.guess = np.linspace([0, 0, 0], [10, 5, 3], n_points)
|
|
230
251
|
"""
|
|
231
|
-
|
|
252
|
+
if callable(value):
|
|
253
|
+
from .time import Tau
|
|
254
|
+
|
|
255
|
+
expr = value(Tau())
|
|
256
|
+
if not isinstance(expr, Expr):
|
|
257
|
+
raise ValueError(
|
|
258
|
+
f"{self.__class__.__name__} '{self.name}': guess callable returned "
|
|
259
|
+
f"{type(expr).__name__}, not a symbolic expression. The callable "
|
|
260
|
+
f"receives the normalized node coordinate as an Expr and must return "
|
|
261
|
+
f"one built from openscvx operations, e.g. "
|
|
262
|
+
f"lambda tau: p0 + (pf - p0) * tau. Computing on tau with numpy or "
|
|
263
|
+
f"jax.numpy will not work — the guess is composed symbolically and "
|
|
264
|
+
f"only evaluated on the node grid later. For a purely numeric guess, "
|
|
265
|
+
f"assign the array itself."
|
|
266
|
+
)
|
|
267
|
+
value = expr
|
|
268
|
+
|
|
269
|
+
if isinstance(value, Expr):
|
|
270
|
+
self._guess_expr = value
|
|
271
|
+
self._guess = None
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
arr = np.asarray(value, dtype=float)
|
|
232
275
|
if arr.ndim != 2:
|
|
233
276
|
raise ValueError(
|
|
234
277
|
f"{self.__class__.__name__} '{self.name}': guess expected 2D array of shape"
|
|
@@ -240,6 +283,7 @@ class Variable(Leaf):
|
|
|
240
283
|
f" {self.shape[0]}, got {arr.shape[1]}"
|
|
241
284
|
)
|
|
242
285
|
self._guess = arr
|
|
286
|
+
self._guess_expr = None
|
|
243
287
|
|
|
244
288
|
def append(
|
|
245
289
|
self,
|
|
@@ -267,6 +311,9 @@ class Variable(Leaf):
|
|
|
267
311
|
guess: Initial guess value for the new dimension (only used if other is None).
|
|
268
312
|
Defaults to 0.0.
|
|
269
313
|
|
|
314
|
+
Raises:
|
|
315
|
+
ValueError: If either variable still carries an unresolved symbolic guess.
|
|
316
|
+
|
|
270
317
|
Example:
|
|
271
318
|
Create a 2D variable and extend it to 3D:
|
|
272
319
|
|
|
@@ -285,6 +332,15 @@ class Variable(Leaf):
|
|
|
285
332
|
pos.append(vel) # Now pos has shape (6,)
|
|
286
333
|
"""
|
|
287
334
|
|
|
335
|
+
for var in (self, other):
|
|
336
|
+
if isinstance(var, Variable) and var._guess_expr is not None:
|
|
337
|
+
raise ValueError(
|
|
338
|
+
f"{var.__class__.__name__} '{var.name}' has a symbolic initial guess "
|
|
339
|
+
f"that has not been evaluated yet, so it cannot be concatenated. "
|
|
340
|
+
f"Symbolic guesses are resolved when the problem is built; append "
|
|
341
|
+
f"dimensions first, or assign an array guess."
|
|
342
|
+
)
|
|
343
|
+
|
|
288
344
|
def process_array(val, is_guess=False):
|
|
289
345
|
"""Process input array to ensure correct shape and type.
|
|
290
346
|
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
"""JAX visitors for state/time expressions.
|
|
2
2
|
|
|
3
|
-
Visitors: State, Time
|
|
3
|
+
Visitors: State, Time, Tau
|
|
4
4
|
|
|
5
5
|
Lowers a ``State`` leaf to a JAX function that slices the corresponding entries
|
|
6
6
|
out of the unified state vector ``x``, using the slice assigned during
|
|
7
7
|
unification. ``Time`` is a ``State`` subclass and shares the same visitor;
|
|
8
8
|
lowering a state whose slice is still unset is a usage error and raises.
|
|
9
|
+
``Tau`` — the normalized node coordinate used by initial-guess expressions —
|
|
10
|
+
reads its value off the node grid carried in ``params``.
|
|
9
11
|
"""
|
|
10
12
|
|
|
11
13
|
# Expression types to handle — uncomment as you paste visitors:
|
|
12
14
|
from openscvx.symbolic.expr.state import State
|
|
13
|
-
from openscvx.symbolic.expr.time import Time
|
|
15
|
+
from openscvx.symbolic.expr.time import TAU_PARAM, Tau, Time
|
|
14
16
|
from openscvx.symbolic.lowerers.jax._registry import visitor # noqa: F401
|
|
15
17
|
|
|
16
18
|
|
|
@@ -35,3 +37,19 @@ def _visit_state(lowerer, node: State):
|
|
|
35
37
|
if sl is None:
|
|
36
38
|
raise ValueError(f"State {node.name!r} has no slice assigned")
|
|
37
39
|
return lambda x, u, node, params: x[sl]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@visitor(Tau)
|
|
43
|
+
def _visit_tau(lowerer, node: Tau):
|
|
44
|
+
"""Lower the normalized node coordinate to a JAX function.
|
|
45
|
+
|
|
46
|
+
The tau grid is supplied by the guess-resolution pass under the reserved
|
|
47
|
+
``params`` key, so tau is read at the node currently being evaluated.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
node: Tau expression node
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Function (x, u, node, params) -> tau at that node
|
|
54
|
+
"""
|
|
55
|
+
return lambda x, u, node, params: params[TAU_PARAM][node]
|
|
@@ -687,7 +687,8 @@ def fill_default_guesses(states: List[State], N: int) -> None:
|
|
|
687
687
|
"""Fill in default linspace guesses for states with guess=None.
|
|
688
688
|
|
|
689
689
|
For states with both initial and final conditions set, generates a linear
|
|
690
|
-
interpolation from initial to final values.
|
|
690
|
+
interpolation from initial to final values. States carrying a symbolic guess
|
|
691
|
+
are left alone — theirs is filled by :func:`resolve_guess_exprs`.
|
|
691
692
|
|
|
692
693
|
This function modifies states in-place.
|
|
693
694
|
|
|
@@ -698,6 +699,8 @@ def fill_default_guesses(states: List[State], N: int) -> None:
|
|
|
698
699
|
from openscvx.init import linspace
|
|
699
700
|
|
|
700
701
|
for state in states:
|
|
702
|
+
if state._guess_expr is not None:
|
|
703
|
+
continue
|
|
701
704
|
if state.guess is None and state.initial is not None and state.final is not None:
|
|
702
705
|
# state.initial and state.final are already numpy arrays of values
|
|
703
706
|
# (the setter handles parsing tuples like ("free", value))
|
|
@@ -707,6 +710,151 @@ def fill_default_guesses(states: List[State], N: int) -> None:
|
|
|
707
710
|
)
|
|
708
711
|
|
|
709
712
|
|
|
713
|
+
def resolve_guess_exprs(states: List[State], controls: List[Control], N: int) -> None:
|
|
714
|
+
"""Evaluate symbolic initial guesses on the node grid, in dependency order.
|
|
715
|
+
|
|
716
|
+
A symbolic guess (``speed.guess = ox.linalg.Norm(vel)``, ``pos.guess = lambda tau: ...``)
|
|
717
|
+
defines the guess *per node*: the expression is evaluated at every node with each
|
|
718
|
+
referenced variable at its own guess value and tau running 0 → 1 across the grid —
|
|
719
|
+
the same mental model as dynamics and constraints. Guesses that reference other
|
|
720
|
+
symbolic guesses are resolved first; the expressions themselves are the dependency
|
|
721
|
+
graph.
|
|
722
|
+
|
|
723
|
+
Each resolved variable has its ``_guess`` array filled and its pending expression
|
|
724
|
+
cleared, so everything downstream sees an ordinary eager guess. Variables without
|
|
725
|
+
a symbolic guess are untouched, and the function returns immediately when there
|
|
726
|
+
are none.
|
|
727
|
+
|
|
728
|
+
This function modifies variables in-place, and requires slices to have been
|
|
729
|
+
assigned (see :func:`collect_and_assign_slices`).
|
|
730
|
+
|
|
731
|
+
Args:
|
|
732
|
+
states: State objects whose guesses may be referenced or resolved
|
|
733
|
+
controls: Control objects whose guesses may be referenced or resolved
|
|
734
|
+
N: Number of discretization nodes
|
|
735
|
+
|
|
736
|
+
Raises:
|
|
737
|
+
ValueError: If a guess references a variable outside this problem, if the
|
|
738
|
+
guesses reference each other circularly, or if an evaluated guess does
|
|
739
|
+
not have shape ``(N, n)``.
|
|
740
|
+
"""
|
|
741
|
+
import jax
|
|
742
|
+
import jax.numpy as jnp
|
|
743
|
+
|
|
744
|
+
from openscvx.symbolic.expr.parameter import Parameter
|
|
745
|
+
from openscvx.symbolic.expr.time import TAU_PARAM
|
|
746
|
+
from openscvx.symbolic.lower import lower_to_jax
|
|
747
|
+
|
|
748
|
+
variables = list(states) + list(controls)
|
|
749
|
+
pending = [var for var in variables if var._guess_expr is not None]
|
|
750
|
+
if not pending:
|
|
751
|
+
return
|
|
752
|
+
|
|
753
|
+
known = {id(var) for var in variables}
|
|
754
|
+
dependencies = {id(var): _guess_dependencies(var, known) for var in pending}
|
|
755
|
+
|
|
756
|
+
# Kahn's algorithm over the pending variables: a guess is ready once every
|
|
757
|
+
# pending variable it references has been resolved.
|
|
758
|
+
order, resolved, unordered = [], set(), list(pending)
|
|
759
|
+
while unordered:
|
|
760
|
+
ready = [
|
|
761
|
+
var
|
|
762
|
+
for var in unordered
|
|
763
|
+
if all(
|
|
764
|
+
id(dep) not in dependencies or id(dep) in resolved for dep in dependencies[id(var)]
|
|
765
|
+
)
|
|
766
|
+
]
|
|
767
|
+
if not ready:
|
|
768
|
+
raise ValueError(
|
|
769
|
+
f"Circular initial-guess dependency among "
|
|
770
|
+
f"{sorted(var.name for var in unordered)}. Each guess expression is "
|
|
771
|
+
f"evaluated from the guesses it references, so the references cannot "
|
|
772
|
+
f"form a cycle — break it by giving one of them an array guess."
|
|
773
|
+
)
|
|
774
|
+
order.extend(ready)
|
|
775
|
+
resolved.update(id(var) for var in ready)
|
|
776
|
+
unordered = [var for var in unordered if id(var) not in resolved]
|
|
777
|
+
|
|
778
|
+
tau = jnp.linspace(0.0, 1.0, N)
|
|
779
|
+
nodes = jnp.arange(N)
|
|
780
|
+
for var in order:
|
|
781
|
+
expr = var._guess_expr
|
|
782
|
+
params = {TAU_PARAM: tau}
|
|
783
|
+
|
|
784
|
+
def bind_parameter(node, params=params):
|
|
785
|
+
if isinstance(node, Parameter):
|
|
786
|
+
params[node.name] = node.value
|
|
787
|
+
|
|
788
|
+
traverse(expr, bind_parameter)
|
|
789
|
+
fn = lower_to_jax(expr)
|
|
790
|
+
values = jax.vmap(fn, in_axes=(0, 0, 0, None))(
|
|
791
|
+
_guess_trajectory(states, N), _guess_trajectory(controls, N), nodes, params
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
guess = np.asarray(values, dtype=float)
|
|
795
|
+
if guess.ndim == 1 and var.shape[0] == 1:
|
|
796
|
+
guess = guess.reshape(-1, 1)
|
|
797
|
+
if guess.shape != (N, var.shape[0]):
|
|
798
|
+
raise ValueError(
|
|
799
|
+
f"{var.__class__.__name__} '{var.name}': guess expression evaluated to "
|
|
800
|
+
f"shape {guess.shape}, expected ({N}, {var.shape[0]}). The expression is "
|
|
801
|
+
f"evaluated once per node, so it must produce one row of "
|
|
802
|
+
f"{var.shape[0]} value(s) per node."
|
|
803
|
+
)
|
|
804
|
+
var._guess = guess
|
|
805
|
+
var._guess_expr = None
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _guess_dependencies(var: Variable, known: Set[int]) -> List[Variable]:
|
|
809
|
+
"""Collect the variables a symbolic guess reads, in first-seen order.
|
|
810
|
+
|
|
811
|
+
Args:
|
|
812
|
+
var: Variable carrying the pending guess expression
|
|
813
|
+
known: Ids of the variables belonging to this problem
|
|
814
|
+
|
|
815
|
+
Raises:
|
|
816
|
+
ValueError: If the expression references a variable outside the problem
|
|
817
|
+
"""
|
|
818
|
+
dependencies = {}
|
|
819
|
+
|
|
820
|
+
def visit(node):
|
|
821
|
+
if not isinstance(node, Variable):
|
|
822
|
+
return
|
|
823
|
+
if id(node) not in known:
|
|
824
|
+
raise ValueError(
|
|
825
|
+
f"{var.__class__.__name__} '{var.name}': guess expression references "
|
|
826
|
+
f"{node.__class__.__name__} '{node.name}', which is not one of this "
|
|
827
|
+
f"problem's states or controls. Guess expressions may only reference "
|
|
828
|
+
f"variables passed to Problem."
|
|
829
|
+
)
|
|
830
|
+
dependencies.setdefault(id(node), node)
|
|
831
|
+
|
|
832
|
+
traverse(var._guess_expr, visit)
|
|
833
|
+
return list(dependencies.values())
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
def _guess_trajectory(variables: List[Variable], N: int) -> np.ndarray:
|
|
837
|
+
"""Assemble the (N, n) guess trajectory for a set of states or controls.
|
|
838
|
+
|
|
839
|
+
Columns of variables whose guess is not yet resolved are left at zero;
|
|
840
|
+
dependency ordering guarantees the columns a guess expression reads are filled.
|
|
841
|
+
"""
|
|
842
|
+
n = max((var._slice.stop for var in variables), default=0)
|
|
843
|
+
trajectory = np.zeros((N, n))
|
|
844
|
+
for var in variables:
|
|
845
|
+
if var._guess is None:
|
|
846
|
+
continue
|
|
847
|
+
if var._guess.shape[0] != N:
|
|
848
|
+
raise ValueError(
|
|
849
|
+
f"{var.__class__.__name__} '{var.name}': guess spans "
|
|
850
|
+
f"{var._guess.shape[0]} nodes but the problem has N={N}. Guess "
|
|
851
|
+
f"expressions are evaluated node by node, so every guess they read "
|
|
852
|
+
f"must cover the whole grid."
|
|
853
|
+
)
|
|
854
|
+
trajectory[:, var._slice] = var._guess
|
|
855
|
+
return trajectory
|
|
856
|
+
|
|
857
|
+
|
|
710
858
|
def validate_boundary_conditions(states: List[State]) -> None:
|
|
711
859
|
"""Validate that all states have initial and final boundary conditions set.
|
|
712
860
|
|
|
@@ -908,6 +1056,9 @@ def validate_propagation_input_types(
|
|
|
908
1056
|
def validate_guesses(variables: List[Variable]) -> None:
|
|
909
1057
|
"""Validate that all variables have initial guesses set.
|
|
910
1058
|
|
|
1059
|
+
A guess assigned symbolically counts as set even though it has not been
|
|
1060
|
+
evaluated to an array yet.
|
|
1061
|
+
|
|
911
1062
|
Args:
|
|
912
1063
|
variables: List of Variable objects (State or Control) to validate
|
|
913
1064
|
|
|
@@ -915,12 +1066,16 @@ def validate_guesses(variables: List[Variable]) -> None:
|
|
|
915
1066
|
ValueError: If any variable is missing a guess
|
|
916
1067
|
"""
|
|
917
1068
|
for var in variables:
|
|
918
|
-
if var.guess is None:
|
|
1069
|
+
if var.guess is None and var._guess_expr is None:
|
|
919
1070
|
if isinstance(var, Control):
|
|
920
1071
|
raise ValueError(
|
|
921
1072
|
f"Control '{var.name}' is missing initial guess. "
|
|
922
|
-
f"Please set {var.name}.guess
|
|
1073
|
+
f"Please set {var.name}.guess to an array, an expression in other "
|
|
1074
|
+
f"states and controls, or a callable of tau "
|
|
1075
|
+
f"(controls require explicit guesses)"
|
|
923
1076
|
)
|
|
924
1077
|
raise ValueError(
|
|
925
|
-
f"State '{var.name}' is missing initial guess. Please set {var.name}.guess"
|
|
1078
|
+
f"State '{var.name}' is missing initial guess. Please set {var.name}.guess "
|
|
1079
|
+
f"to an array, an expression in other states and controls, or a "
|
|
1080
|
+
f"callable of tau"
|
|
926
1081
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openscvx
|
|
3
|
-
Version: 0.5.3.
|
|
3
|
+
Version: 0.5.3.dev50
|
|
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
|