openscvx 0.5.3.dev49__tar.gz → 0.5.3.dev51__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.dev49/openscvx.egg-info → openscvx-0.5.3.dev51}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/Foundations/discretization.md +38 -1
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/01_hello_world_brachistochrone.md +29 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/_version.py +3 -3
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/problem.py +5 -1
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/builder.py +18 -2
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/__init__.py +10 -0
- openscvx-0.5.3.dev51/openscvx/symbolic/expr/autodiff.py +159 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/expr.py +41 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/time.py +72 -15
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/variable.py +74 -12
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/__init__.py +1 -0
- openscvx-0.5.3.dev51/openscvx/symbolic/lowerers/cvxpy/autodiff.py +36 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/__init__.py +1 -0
- openscvx-0.5.3.dev51/openscvx/symbolic/lowerers/jax/autodiff.py +82 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/state.py +20 -2
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/__init__.py +1 -0
- openscvx-0.5.3.dev51/openscvx/symbolic/lowerers/latex/autodiff.py +17 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/preprocessing.py +159 -4
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx.egg-info/SOURCES.txt +6 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx.egg-info/scm_file_list.json +6 -0
- openscvx-0.5.3.dev51/openscvx.egg-info/scm_version.json +8 -0
- openscvx-0.5.3.dev51/tests/symbolic/expr/test_autodiff.py +450 -0
- openscvx-0.5.3.dev51/tests/symbolic/expr/test_guess_expr.py +502 -0
- openscvx-0.5.3.dev49/openscvx.egg-info/scm_version.json +0 -8
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/_extra.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/docs.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/lint.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/release.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/tests-cvxpygen.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/tests-examples.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/tests-lie.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/tests-mjx.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/tests-qpax.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.github/workflows/tests.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.gitignore +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/.gitmodules +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/CITATION.cff +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/CONTRIBUTING.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/LICENSE +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/README.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UnderTheHood/batching_jit_grad.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UnderTheHood/custom_algorithms_autotuners.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UnderTheHood/lowering_architecture.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/08_mpcc.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/favicon.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/viser-client/index.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/viser-recordings/drone_racing.viser +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/changelog.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/citation.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/examples.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/index.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/installation.md +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/llms.txt +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/docs/versions.json +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/_viser_embed_export.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/abstract/brachistochrone_batched.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/abstract/flappy_bird.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/abstract/hypersensitive.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/abstract/impulsive.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/abstract/stl_integer_variable.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/abstract/stl_or.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/aircraft/dynamic_soaring.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/aircraft/supersonic_time_to_climb.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/_camera.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/_render.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/_sensor_view.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/logo.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/animations/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/arm/3_dof_arm.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/arm/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/arm/7_dof_arm_collision.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/arm/7_dof_arm_vp.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/arm/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/arm/franka_fr3v2_viewplanning_nodal.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/car/dubins_car.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/car/dubins_car_obstacle_conditional.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/car/dubins_car_obstacle_stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/car/dubins_car_stl_or.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/car/dubins_car_waypoint_stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/double_integrator/_plotting.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/double_integrator/moving_safe_zones.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/2d_obstacle_avoidance_batched_ic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/boresight_trace_mpcc.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/cinema_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/drone_racing_batched_gates.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/logo.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/logo_utils/circle.svg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/logo_utils/openscvx_logo_single.svg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/logo_utils/quadrotor_mesh.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/logo_utils/svg_path_utils.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/multi_agent_circle_swap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/obstacle_avoidance_vmap_2d.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/drone/openscvx_logo.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/frax/panda_frax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/frax/panda_frax_pick_place.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/frax/panda_frax_viewplanning.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/frax/panda_frax_waypoint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/frax/ur5e_assets/ur5e.urdf +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/frax/ur5e_pen_trace.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/frax/ur5e_pen_trace_mpcc.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mjx/cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mjx/double_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mjx/skydio_x2_mjx.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mjx/triple_cartpole_game.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mjx/triple_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mpc/double_integrator_discrete.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mpc/double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mpc/dubins_car_circle_analytical.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mpc/dubins_car_circle_discrete.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/plotting.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/plotting_viser.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/3DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/6DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/rocket/6DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/rocket/6DoF_pdg_batched_ic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/rocket/ascent_launch_vehicle.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/spacecraft/halo_orbit.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/spacecraft/hohmann_transfer.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/spacecraft/let_transfer.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/examples/spacecraft/relative_loitering.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/figures/video_preview.png +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/assets/stylesheets/home-hero.css +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/assets/stylesheets/home-viser.css +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/home.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/main.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/partials/home-diagram.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/partials/home-dropin-banner.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/partials/home-hero.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/partials/home-pipeline.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/partials/home-viser-strip.html +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/material/overrides/sitemap.xml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/mkdocs.yml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/__main__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/autotuner/acceptance_ratio.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/autotuner/base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/history.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/hyperparams.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/loop.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/optimization_results.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/penalty.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/scvx/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/scvx/iteration.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/state.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/algorithms/weights.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/config.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/discretize_linearize.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/linearize_discretize.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/sparse_utils/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/expert/byof.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/expert/lowering.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/expert/validation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/init/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/init/inverse_kinematics.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrations/_utils.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrations/base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrations/frax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrations/menagerie.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrations/mjx.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrators/diffrax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/loader.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/cvxpy_variables.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/problem.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/lowered/unified.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/plotting.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/publication.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/animated.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/coordinates.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/orbits.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/propagation/post_processing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/propagation/propagation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/solvers/base.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/solvers/cones.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/solvers/moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/solvers/ptr_solver.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/solvers/qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/affine.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/augmentation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/canonicalize.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/control.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/parameter.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/stljax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lower.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/_lowerer.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/_registry.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/arithmetic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/array.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/constraint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/control.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/expr.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/formulation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/lie.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/linalg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/logic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/math.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/spatial.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/state.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/lowerers/latex/vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/stljax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/problem.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/symbolic/unified.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/utils/cache.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/utils/caching.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/utils/printing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx/utils/utils.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/pyproject.toml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/scripts/mkdocs_seo_hook.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/setup.cfg +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/_iteration_helpers.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/autotuner/test_defaults_agreement.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/autotuner/test_hyper_overrides.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/test_iteration_fn_jit.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/test_iteration_fn_parity.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/algorithms/test_make_solve_loop.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/conftest.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_impulsive.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_multishot_propagation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_solve_batched_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_solve_batched_cvxpy_export_errors.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_solve_batched_export_roundtrip.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_solve_jax_bare_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_solve_jax_jit_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_solve_jax_vmap_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/e2e/test_solve_jax_vmap_converged_no_drift.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/hohmann_analytical.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/integrations/test_mjx.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/integrations/test_mjx_dynamics.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/_iteration_callback_helpers.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_iteration_callback_moreau.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_iteration_callback_qpax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_iteration_callback_vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/solvers/test_subproblem_pytree.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_math.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_variable.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_augmentation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_gmsr.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_latex_formulation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_lower_latex.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_preprocessing.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/symbolic/test_unified.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_autotuning.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_discretization.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_examples.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_expert.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_init.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_initialize_syncs_guesses.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_integrators.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_loader.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_optimization_results.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_plotting.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_post_process_batched_tau_clamp.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_propagation.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/tests/test_results_parameters.py +0 -0
- {openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/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.dev51
|
|
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
|
|
@@ -91,4 +91,41 @@ def dVdt(self, tau: float, V: jnp.ndarray, u_cur: np.ndarray, u_next: np.ndarray
|
|
|
91
91
|
)
|
|
92
92
|
return dVdt.flatten()
|
|
93
93
|
```
|
|
94
|
-
|
|
94
|
+
|
|
95
|
+
## Supplying your own Jacobian
|
|
96
|
+
|
|
97
|
+
The state and control Jacobians above (`dfdx`, `dfdu`) come from automatic
|
|
98
|
+
differentiation of the dynamics you wrote. Occasionally the exact Jacobian is
|
|
99
|
+
not the one you want: a drag law, a contact model, or a lookup table can be
|
|
100
|
+
smooth enough to integrate while its true derivative makes the convex
|
|
101
|
+
subproblem badly conditioned, so the trust region collapses and the SCP loop
|
|
102
|
+
crawls. The standard remedy is to linearize with a deliberately simplified
|
|
103
|
+
(inexact) Jacobian.
|
|
104
|
+
|
|
105
|
+
`Expr.with_jacobian` applies that remedy to a single term:
|
|
106
|
+
|
|
107
|
+
``` py
|
|
108
|
+
drag = -0.5 * rho * ox.Norm(vel) * vel
|
|
109
|
+
J_drag = -0.5 * rho * ox.Norm(vel) * np.eye(3) # drops the d||v||/dv term
|
|
110
|
+
|
|
111
|
+
dynamics = {"pos": vel, "vel": thrust / m + drag.with_jacobian({vel: J_drag})}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The keys are the `State` or `Control` objects the derivative is taken with
|
|
115
|
+
respect to, and each value is an expression (or constant array) of shape
|
|
116
|
+
`(*term.shape, *variable.shape)`. Directions you do not name are still
|
|
117
|
+
differentiated automatically, so the example above hands over `∂/∂vel` while
|
|
118
|
+
`∂/∂thrust` still comes from autodiff.
|
|
119
|
+
|
|
120
|
+
The *value* of the term is untouched — only the search direction changes — so a
|
|
121
|
+
converged trajectory still satisfies the original nonlinear dynamics; the
|
|
122
|
+
defect and the propagated solution are computed with the term as written. An
|
|
123
|
+
inexact Jacobian trades linearization accuracy (and hence convergence rate) for
|
|
124
|
+
conditioning, so reach for it when a term is a known source of stiffness, not
|
|
125
|
+
as a default.
|
|
126
|
+
|
|
127
|
+
The override lives inside the lowered JAX function, so every downstream
|
|
128
|
+
derivative picks it up: the discretizer's `A`/`B`, nonconvex constraint
|
|
129
|
+
linearization, and the sparsity pattern used for coloring. It has no meaning
|
|
130
|
+
inside a constraint that is handed to the convex solver as written, and lowering
|
|
131
|
+
one there raises.
|
{openscvx-0.5.3.dev49 → openscvx-0.5.3.dev51}/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.dev51'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 3, 'dev51')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g21d5581a4'
|
|
@@ -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}
|
|
@@ -33,6 +33,11 @@ Module Organization:
|
|
|
33
33
|
Array manipulation operations including `Index`, `Concat`, `Stack`, `Hstack`,
|
|
34
34
|
and `Vstack` for indexing, slicing, and combining arrays.
|
|
35
35
|
|
|
36
|
+
User-Supplied Jacobians (autodiff.py):
|
|
37
|
+
`WithJacobian`, built by `Expr.with_jacobian`, replaces a subexpression's
|
|
38
|
+
derivative with respect to chosen variables — the inexact-Jacobian knob for
|
|
39
|
+
SCP conditioning.
|
|
40
|
+
|
|
36
41
|
Constraints (constraint.py):
|
|
37
42
|
Constraint types including `Constraint`, `Equality`, `Inequality`,
|
|
38
43
|
`NodalConstraint`, and `CTCS` (Continuous-Time Constraint Satisfaction).
|
|
@@ -80,6 +85,9 @@ from .arithmetic import Add, Div, MatMul, Mul, Neg, Power, Sub
|
|
|
80
85
|
# Array operations
|
|
81
86
|
from .array import Block, Concat, Hstack, Index, Stack, Vstack
|
|
82
87
|
|
|
88
|
+
# User-supplied Jacobians
|
|
89
|
+
from .autodiff import WithJacobian
|
|
90
|
+
|
|
83
91
|
# Specialized constraints
|
|
84
92
|
from .constraint import (
|
|
85
93
|
CTCS,
|
|
@@ -258,4 +266,6 @@ __all__ = [
|
|
|
258
266
|
"ctcs",
|
|
259
267
|
# Vmap
|
|
260
268
|
"Vmap",
|
|
269
|
+
# User-supplied Jacobians
|
|
270
|
+
"WithJacobian",
|
|
261
271
|
]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""User-supplied Jacobians for a subexpression.
|
|
2
|
+
|
|
3
|
+
Successive convexification linearizes the dynamics and constraints at every
|
|
4
|
+
iteration, so the *Jacobian* — not the value — is what shapes each convex
|
|
5
|
+
subproblem. Occasionally the exact Jacobian is the wrong one to hand the
|
|
6
|
+
solver: an aerodynamic drag term, a contact model, or a lookup table can be
|
|
7
|
+
smooth enough to integrate but stiff enough that its true derivative wrecks the
|
|
8
|
+
conditioning of the subproblem. The standard remedy is a deliberately
|
|
9
|
+
simplified (inexact) Jacobian — the trajectory still converges to a solution of
|
|
10
|
+
the *nonlinear* problem, because the value used for the defect is untouched;
|
|
11
|
+
only the search direction changes.
|
|
12
|
+
|
|
13
|
+
:class:`WithJacobian` is that remedy, written at sub-expression granularity::
|
|
14
|
+
|
|
15
|
+
drag = -0.5 * rho * ox.Norm(vel) * vel
|
|
16
|
+
accel = thrust / m + drag.with_jacobian({vel: J_drag})
|
|
17
|
+
|
|
18
|
+
The node wraps a subexpression and carries an override Jacobian per variable.
|
|
19
|
+
Lowering to JAX emits a :func:`jax.custom_jvp` rule around the wrapped
|
|
20
|
+
subexpression, so every downstream differentiation — the discretizer's
|
|
21
|
+
``jacfwd``, constraint linearization, sparse coloring — sees the user's
|
|
22
|
+
Jacobian in the overridden directions and autodiff everywhere else, with no
|
|
23
|
+
further special-casing. The primal value is always the wrapped expression
|
|
24
|
+
itself.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import hashlib
|
|
28
|
+
from typing import Dict, List, Tuple
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
|
|
32
|
+
from .expr import Expr, to_expr
|
|
33
|
+
from .variable import Variable
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _without_unit_axes(shape: Tuple[int, ...]) -> Tuple[int, ...]:
|
|
37
|
+
"""Drop length-1 axes, the way :class:`Constant` squeezes a wrapped array."""
|
|
38
|
+
return tuple(d for d in shape if d != 1)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class WithJacobian(Expr):
|
|
42
|
+
"""A subexpression whose derivative w.r.t. chosen variables is user-supplied.
|
|
43
|
+
|
|
44
|
+
Construct this through :meth:`Expr.with_jacobian` rather than directly. The
|
|
45
|
+
node evaluates exactly like the expression it wraps; only differentiation
|
|
46
|
+
differs, and only in the overridden directions (see the module docstring for
|
|
47
|
+
the motivation).
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
expr: The wrapped expression, and the node's value.
|
|
51
|
+
overrides: ``(variable, jacobian)`` pairs in the order given, each
|
|
52
|
+
Jacobian shaped ``(*expr.shape, *variable.shape)``.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, expr: Expr, jacobians: Dict[Variable, Expr]):
|
|
56
|
+
"""Wrap ``expr`` with an override Jacobian per variable.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
expr: Expression whose derivative is being overridden.
|
|
60
|
+
jacobians: Map from a ``State`` or ``Control`` to the Jacobian of
|
|
61
|
+
``expr`` with respect to it. Values are coerced with
|
|
62
|
+
:func:`to_expr`, so constant matrices are accepted.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
TypeError: If a key is not a decision variable.
|
|
66
|
+
ValueError: If no Jacobian is given.
|
|
67
|
+
"""
|
|
68
|
+
if not jacobians:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
"with_jacobian needs at least one {variable: jacobian} entry; an empty "
|
|
71
|
+
"mapping would wrap the expression without changing anything."
|
|
72
|
+
)
|
|
73
|
+
self.expr = to_expr(expr)
|
|
74
|
+
overrides = []
|
|
75
|
+
for var, jac in jacobians.items():
|
|
76
|
+
if not isinstance(var, Variable):
|
|
77
|
+
raise TypeError(
|
|
78
|
+
f"with_jacobian keys must be the State or Control the derivative is "
|
|
79
|
+
f"taken with respect to, got {type(var).__name__}. Write "
|
|
80
|
+
f"`expr.with_jacobian({{vel: J_vel}})` with `vel` the variable itself."
|
|
81
|
+
)
|
|
82
|
+
overrides.append((var, to_expr(jac)))
|
|
83
|
+
self.overrides: List[Tuple[Variable, Expr]] = overrides
|
|
84
|
+
|
|
85
|
+
def children(self) -> List[Expr]:
|
|
86
|
+
"""Return the wrapped expression followed by each override Jacobian.
|
|
87
|
+
|
|
88
|
+
The overridden variables are not children: they identify *directions*,
|
|
89
|
+
not operands, and appear as leaves inside the Jacobian expressions only
|
|
90
|
+
if the user put them there.
|
|
91
|
+
"""
|
|
92
|
+
return [self.expr, *(jac for _, jac in self.overrides)]
|
|
93
|
+
|
|
94
|
+
def canonicalize(self) -> Expr:
|
|
95
|
+
"""Canonicalize the wrapped expression and every override Jacobian."""
|
|
96
|
+
return WithJacobian(
|
|
97
|
+
self.expr.canonicalize(),
|
|
98
|
+
{var: jac.canonicalize() for var, jac in self.overrides},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def check_shape(self) -> Tuple[int, ...]:
|
|
102
|
+
"""Return the wrapped expression's shape, validating each Jacobian.
|
|
103
|
+
|
|
104
|
+
A Jacobian must be shaped ``(*expr.shape, *var.shape)``. Unit axes are
|
|
105
|
+
ignored in the comparison: a constant is squeezed when it is wrapped, so
|
|
106
|
+
an honest ``(3, 1)`` Jacobian arrives as ``(3,)``. The JAX lowering
|
|
107
|
+
reshapes it back before contracting.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
The shape of the wrapped expression — the wrapper is value-transparent.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValueError: If a Jacobian's shape is not ``(*expr.shape, *var.shape)``.
|
|
114
|
+
"""
|
|
115
|
+
shape = self.expr.check_shape()
|
|
116
|
+
for var, jac in self.overrides:
|
|
117
|
+
expected = (*shape, *var.shape)
|
|
118
|
+
got = jac.check_shape()
|
|
119
|
+
if _without_unit_axes(got) != _without_unit_axes(expected):
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"Jacobian for {var.__class__.__name__} '{var.name}' has shape {got}, "
|
|
122
|
+
f"expected {expected}: the derivative of an expression of shape "
|
|
123
|
+
f"{shape} with respect to a variable of shape {var.shape}."
|
|
124
|
+
)
|
|
125
|
+
return shape
|
|
126
|
+
|
|
127
|
+
def sparsity(self, n_x: int, n_u: int) -> Tuple[np.ndarray, np.ndarray]:
|
|
128
|
+
"""Conservative pattern: the children's, widened to the overridden columns.
|
|
129
|
+
|
|
130
|
+
The default union over children is not enough here. A user Jacobian may
|
|
131
|
+
be a constant (no dependence of its own) yet still couple outputs to
|
|
132
|
+
every entry of the overridden variable, so those columns are marked
|
|
133
|
+
dense on top of the children's pattern.
|
|
134
|
+
"""
|
|
135
|
+
from .control import Control
|
|
136
|
+
|
|
137
|
+
S_x, S_u = super().sparsity(n_x, n_u)
|
|
138
|
+
for var, _ in self.overrides:
|
|
139
|
+
if var._slice is None:
|
|
140
|
+
continue
|
|
141
|
+
S = S_u if isinstance(var, Control) else S_x
|
|
142
|
+
S[:, var._slice] = True
|
|
143
|
+
return S_x, S_u
|
|
144
|
+
|
|
145
|
+
def _hash_into(self, hasher: "hashlib._Hash") -> None:
|
|
146
|
+
"""Hash the wrapper, the wrapped expression, and each override in order.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
hasher: A hashlib hash object to update
|
|
150
|
+
"""
|
|
151
|
+
hasher.update(b"WithJacobian")
|
|
152
|
+
self.expr._hash_into(hasher)
|
|
153
|
+
for var, jac in self.overrides:
|
|
154
|
+
var._hash_into(hasher)
|
|
155
|
+
jac._hash_into(hasher)
|
|
156
|
+
|
|
157
|
+
def __repr__(self) -> str:
|
|
158
|
+
vars_ = ", ".join(var.name for var, _ in self.overrides)
|
|
159
|
+
return f"{self.expr!r}.with_jacobian({{{vars_}}})"
|
|
@@ -308,6 +308,47 @@ class Expr:
|
|
|
308
308
|
"""
|
|
309
309
|
return NodeReference(self, k)
|
|
310
310
|
|
|
311
|
+
def with_jacobian(self, jacobians: dict) -> "Expr":
|
|
312
|
+
"""Override this expression's derivative w.r.t. chosen variables.
|
|
313
|
+
|
|
314
|
+
Successive convexification linearizes at every iteration, so the Jacobian
|
|
315
|
+
of a term — not its value — is what conditions the convex subproblem. A
|
|
316
|
+
deliberately simplified (inexact) Jacobian is a standard remedy for a term
|
|
317
|
+
that is smooth enough to integrate but whose true derivative makes the
|
|
318
|
+
subproblem badly conditioned. The value of the expression is untouched, so
|
|
319
|
+
the converged trajectory still satisfies the original nonlinear problem;
|
|
320
|
+
only the search direction changes.
|
|
321
|
+
|
|
322
|
+
Directions that are not overridden are still differentiated automatically,
|
|
323
|
+
so a single term can hand-specify its state Jacobian and let the control
|
|
324
|
+
Jacobian fall out of autodiff.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
jacobians: Map from a ``State`` or ``Control`` to the Jacobian of this
|
|
328
|
+
expression with respect to it, shaped ``(*self.shape, *var.shape)``
|
|
329
|
+
(a variable of shape ``(n,)`` and a scalar expression give shape
|
|
330
|
+
``(n,)``). Constant matrices are accepted alongside expressions.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Expr: A ``WithJacobian`` node wrapping this expression.
|
|
334
|
+
|
|
335
|
+
Example:
|
|
336
|
+
Damp the drag Jacobian without changing the drag force:
|
|
337
|
+
|
|
338
|
+
drag = -0.5 * rho * ox.Norm(vel) * vel
|
|
339
|
+
J_drag = -0.5 * rho * ox.Norm(vel) * np.eye(3) # drops the d|v|/dv term
|
|
340
|
+
|
|
341
|
+
dynamics = {"vel": thrust / m + drag.with_jacobian({vel: J_drag})}
|
|
342
|
+
|
|
343
|
+
Note:
|
|
344
|
+
The override applies on the JAX path — dynamics linearization and
|
|
345
|
+
constraint linearization. Inside a constraint lowered to CVXPy the
|
|
346
|
+
wrapper has no meaning and raises.
|
|
347
|
+
"""
|
|
348
|
+
from .autodiff import WithJacobian
|
|
349
|
+
|
|
350
|
+
return WithJacobian(self, jacobians)
|
|
351
|
+
|
|
311
352
|
def children(self) -> List["Expr"]:
|
|
312
353
|
"""Return the child expressions of this node.
|
|
313
354
|
|
|
@@ -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
|