openscvx 0.5.3.dev38__tar.gz → 0.5.3.dev39__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.dev38/openscvx.egg-info → openscvx-0.5.3.dev39}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/__init__.py +3 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/_version.py +3 -3
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/problem.py +106 -1
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/expr.py +19 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lower.py +42 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/__init__.py +49 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/_lowerer.py +263 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/_registry.py +82 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/arithmetic.py +58 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/array.py +62 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/constraint.py +73 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/control.py +14 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/expr.py +27 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/formulation.py +465 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/lie.py +97 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/linalg.py +55 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/logic.py +78 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/math.py +182 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/spatial.py +41 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/state.py +31 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/stl.py +120 -0
- openscvx-0.5.3.dev39/openscvx/symbolic/lowerers/latex/vmap.py +37 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/SOURCES.txt +19 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/scm_file_list.json +19 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/scm_version.json +2 -2
- openscvx-0.5.3.dev39/tests/symbolic/test_latex_formulation.py +314 -0
- openscvx-0.5.3.dev39/tests/symbolic/test_lower_latex.py +786 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/_extra.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/docs.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/lint.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/release.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-cvxpygen.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-examples.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-lie.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-mjx.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests-qpax.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.github/workflows/tests.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.gitignore +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/.gitmodules +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/CONTRIBUTING.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/LICENSE +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/README.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/discretization.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/batching_jit_grad.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/custom_algorithms_autotuners.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/lowering_architecture.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/08_mpcc.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/favicon.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/viser-client/index.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/viser-recordings/drone_racing.viser +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/citation.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/examples.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/index.md +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/docs/versions.json +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/_viser_embed_export.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/brachistochrone_batched.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/flappy_bird.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/hypersensitive.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/impulsive.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/stl_integer_variable.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/abstract/stl_or.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/aircraft/dynamic_soaring.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/aircraft/supersonic_time_to_climb.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/_camera.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/_render.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/_sensor_view.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/logo.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/animations/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/3_dof_arm.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/7_dof_arm_collision.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/7_dof_arm_vp.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/arm/franka_fr3v2_viewplanning_nodal.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_obstacle_conditional.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_obstacle_stl.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_stl_or.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/car/dubins_car_waypoint_stl.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/double_integrator/_plotting.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/double_integrator/moving_safe_zones.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/2d_obstacle_avoidance_batched_ic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/cinema_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/drone_racing_batched_gates.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/openscvx_logo_single.svg +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/quadrotor_mesh.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/logo_utils/svg_path_utils.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/multi_agent_circle_swap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/obstacle_avoidance_vmap_2d.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/drone/openscvx_logo.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax_pick_place.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax_viewplanning.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/frax/panda_frax_waypoint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/double_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/skydio_x2_mjx.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/triple_cartpole_game.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mjx/triple_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/double_integrator_discrete.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/dubins_car_circle_analytical.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/dubins_car_circle_discrete.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/plotting.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/plotting_viser.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/3DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/6DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/6DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/6DoF_pdg_batched_ic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/rocket/ascent_launch_vehicle.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/halo_orbit.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/hohmann_transfer.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/let_transfer.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/examples/spacecraft/relative_loitering.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/figures/video_preview.png +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/home-hero.css +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/assets/stylesheets/home-viser.css +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/home.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/main.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-diagram.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-dropin-banner.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-hero.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-pipeline.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/material/overrides/partials/home-viser-strip.html +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/mkdocs.yml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/__main__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/acceptance_ratio.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/history.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/hyperparams.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/loop.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/optimization_results.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/penalty.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/scvx/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/scvx/iteration.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/state.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/algorithms/weights.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/config.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/discretize_linearize.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/linearize_discretize.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/sparse_utils/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/byof.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/lowering.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/expert/validation.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/init/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/init/inverse_kinematics.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/_utils.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/frax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/menagerie.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrations/mjx.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrators/diffrax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/loader.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/cvxpy_variables.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/problem.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/lowered/unified.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/plotting.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/publication.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/animated.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/coordinates.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/orbits.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/propagation/post_processing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/propagation/propagation.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/base.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/cones.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/ptr_solver.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/solvers/qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/affine.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/augmentation.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/builder.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/canonicalize.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/control.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/parameter.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/stljax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/time.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/variable.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/state.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/stljax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/preprocessing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/problem.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/symbolic/unified.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/cache.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/caching.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/printing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx/utils/utils.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/pyproject.toml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/setup.cfg +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/_iteration_helpers.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/test_defaults_agreement.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/test_hyper_overrides.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_iteration_fn_jit.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_iteration_fn_parity.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/algorithms/test_make_solve_loop.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/conftest.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_impulsive.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_multishot_propagation.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_batched_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_batched_cvxpy_export_errors.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_batched_export_roundtrip.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_bare_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_jit_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_vmap_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/e2e/test_solve_jax_vmap_converged_no_drift.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/hohmann_analytical.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/integrations/test_mjx.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/integrations/test_mjx_dynamics.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/_iteration_callback_helpers.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_moreau.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_qpax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_iteration_callback_vmap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/solvers/test_subproblem_pytree.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_math.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_stl.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_variable.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_augmentation.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_gmsr.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_preprocessing.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/symbolic/test_unified.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_autotuning.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_discretization.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_examples.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_expert.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_init.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_integrators.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_loader.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_optimization_results.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_plotting.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_post_process_batched_tau_clamp.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_propagation.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/tests/test_results_parameters.py +0 -0
- {openscvx-0.5.3.dev38 → openscvx-0.5.3.dev39}/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.dev39
|
|
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
|
|
@@ -90,6 +90,7 @@ from openscvx.symbolic.expr import (
|
|
|
90
90
|
ctcs,
|
|
91
91
|
)
|
|
92
92
|
from openscvx.symbolic.expr.time import Time
|
|
93
|
+
from openscvx.symbolic.lower import to_latex
|
|
93
94
|
from openscvx.utils.cache import clear_cache, get_cache_dir, get_cache_size
|
|
94
95
|
|
|
95
96
|
load_results = OptimizationResults.load
|
|
@@ -186,6 +187,8 @@ __all__ = [
|
|
|
186
187
|
"ctcs",
|
|
187
188
|
# Data parallelism
|
|
188
189
|
"Vmap",
|
|
190
|
+
# LaTeX rendering
|
|
191
|
+
"to_latex",
|
|
189
192
|
# Submodules
|
|
190
193
|
"init",
|
|
191
194
|
"stl",
|
|
@@ -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.dev39'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 3, 'dev39')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g56a68269b'
|
|
@@ -26,7 +26,7 @@ import time
|
|
|
26
26
|
import warnings
|
|
27
27
|
from dataclasses import fields as dc_fields
|
|
28
28
|
from dataclasses import replace as dc_replace
|
|
29
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
29
|
+
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
|
|
30
30
|
|
|
31
31
|
import jax
|
|
32
32
|
import numpy as np
|
|
@@ -525,6 +525,10 @@ class Problem:
|
|
|
525
525
|
dynamics, adapter_byof = dynamics.expand()
|
|
526
526
|
byof = _merge_byof(byof, adapter_byof)
|
|
527
527
|
|
|
528
|
+
# Stash the user-authored dynamics (pre-augmentation) for `to_latex`.
|
|
529
|
+
# Shallow-copy so later user mutation of their dict doesn't alias.
|
|
530
|
+
self._dynamics_dict: dict = dict(dynamics)
|
|
531
|
+
|
|
528
532
|
# Resolve byof: dict → ByofSpec (validates keys and nested specs)
|
|
529
533
|
if byof is not None:
|
|
530
534
|
byof = ByofSpec.model_validate(byof)
|
|
@@ -678,6 +682,107 @@ class Problem:
|
|
|
678
682
|
|
|
679
683
|
# SCP algorithm (resolved from `algorithm` parameter above)
|
|
680
684
|
|
|
685
|
+
@property
|
|
686
|
+
def dynamics(self) -> dict:
|
|
687
|
+
"""The user-authored dynamics dict, ``{state_name: expr}``.
|
|
688
|
+
|
|
689
|
+
This is the problem as written, *before* the solver augments it with a
|
|
690
|
+
time-dilation control and per-CTCS penalty states. Handy for rendering
|
|
691
|
+
or inspecting a single equation, e.g. ``ox.to_latex(problem.dynamics["v"])``.
|
|
692
|
+
|
|
693
|
+
Returns:
|
|
694
|
+
A dict mapping each user state name to its symbolic time derivative.
|
|
695
|
+
"""
|
|
696
|
+
return self._dynamics_dict
|
|
697
|
+
|
|
698
|
+
def to_latex(
|
|
699
|
+
self,
|
|
700
|
+
*,
|
|
701
|
+
dynamics: Literal["inline", "separate"] = "separate",
|
|
702
|
+
constraints: Literal["inline", "symbolic", "separate"] = "inline",
|
|
703
|
+
weights: Literal["symbolic", "numeric"] = "symbolic",
|
|
704
|
+
) -> str:
|
|
705
|
+
"""Render the problem as a Mayer-form LaTeX formulation.
|
|
706
|
+
|
|
707
|
+
The rendered problem is the one you wrote — pre-augmentation, so no
|
|
708
|
+
time-dilation control or CTCS penalty states appear; CTCS constraints
|
|
709
|
+
show up as continuous-time path constraints. The result is a complete
|
|
710
|
+
display-math fragment — a ``subequations`` + ``align`` block (with
|
|
711
|
+
``"separate"`` definitions as bare ``align`` blocks) that pastes into a
|
|
712
|
+
paper as-is, no ``$$`` wrapping.
|
|
713
|
+
|
|
714
|
+
Dynamics render at one of two detail levels:
|
|
715
|
+
|
|
716
|
+
- ``"separate"`` (default): the formulation carries ``\\dot{x} = f(x, u)``
|
|
717
|
+
and the per-state ``\\dot{x}_{i} = \\ldots`` definitions follow as
|
|
718
|
+
their own ``align`` block.
|
|
719
|
+
- ``"inline"``: one ``\\dot{x}_{i} = \\ldots`` row per equation inside
|
|
720
|
+
the formulation.
|
|
721
|
+
|
|
722
|
+
Constraints add a middle level:
|
|
723
|
+
|
|
724
|
+
- ``"inline"`` (default): full constraint bodies with annotations.
|
|
725
|
+
- ``"symbolic"``: numbered ``g_i(x, u) \\le 0`` / ``h_j(x, u) = 0``
|
|
726
|
+
references only (with their ``\\forall t`` / node annotations).
|
|
727
|
+
- ``"separate"``: symbolic references plus an appended definition block.
|
|
728
|
+
|
|
729
|
+
Args:
|
|
730
|
+
dynamics: Detail level for the dynamics section.
|
|
731
|
+
constraints: Detail level for the constraint section.
|
|
732
|
+
weights: How objective coefficients render. ``"symbolic"`` (the
|
|
733
|
+
default) shows each as a ``\\lambda`` subscripted by the state's
|
|
734
|
+
symbol and element (``\\lambda_{t}``, ``\\lambda_{\\mathrm{v},1}``);
|
|
735
|
+
``"numeric"`` substitutes the tuned ``lam_cost`` values (``%g``,
|
|
736
|
+
omitted when equal to 1).
|
|
737
|
+
|
|
738
|
+
Returns:
|
|
739
|
+
The formulation as a single LaTeX string; ``"separate"`` modes append
|
|
740
|
+
the definition blocks after it.
|
|
741
|
+
|
|
742
|
+
Example:
|
|
743
|
+
Default (separate dynamics, inline constraints, symbolic weights)::
|
|
744
|
+
|
|
745
|
+
print(problem.to_latex())
|
|
746
|
+
|
|
747
|
+
Everything expanded in a single block::
|
|
748
|
+
|
|
749
|
+
print(problem.to_latex(dynamics="inline"))
|
|
750
|
+
|
|
751
|
+
Substitute the tuned objective weights::
|
|
752
|
+
|
|
753
|
+
print(problem.to_latex(weights="numeric"))
|
|
754
|
+
"""
|
|
755
|
+
from openscvx.symbolic.lowerers.latex import problem_to_latex
|
|
756
|
+
|
|
757
|
+
return problem_to_latex(
|
|
758
|
+
self.symbolic,
|
|
759
|
+
self._dynamics_dict,
|
|
760
|
+
self.algorithm.lam_cost,
|
|
761
|
+
dynamics=dynamics,
|
|
762
|
+
constraints=constraints,
|
|
763
|
+
weights=weights,
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
def _repr_latex_(self) -> str:
|
|
767
|
+
"""Render as display math in Jupyter (default detail levels, in ``$$``).
|
|
768
|
+
|
|
769
|
+
Uses the builder's ``aligned`` envelope rather than the ``subequations``
|
|
770
|
+
+ ``align`` form ``to_latex`` returns: MathJax/KaTeX (the notebook and
|
|
771
|
+
docs renderers) do not implement ``subequations``, and ``align`` cannot
|
|
772
|
+
nest inside ``$$``, whereas ``aligned`` can.
|
|
773
|
+
"""
|
|
774
|
+
from openscvx.symbolic.lowerers.latex import problem_to_latex
|
|
775
|
+
|
|
776
|
+
body = problem_to_latex(
|
|
777
|
+
self.symbolic,
|
|
778
|
+
self._dynamics_dict,
|
|
779
|
+
self.algorithm.lam_cost,
|
|
780
|
+
dynamics="separate",
|
|
781
|
+
constraints="inline",
|
|
782
|
+
env="aligned",
|
|
783
|
+
)
|
|
784
|
+
return f"$$\n{body}\n$$"
|
|
785
|
+
|
|
681
786
|
@property
|
|
682
787
|
def solver(self) -> ConvexSolver:
|
|
683
788
|
"""Access the convex subproblem solver instance.
|
|
@@ -420,6 +420,25 @@ class Expr:
|
|
|
420
420
|
lines.append(child.pretty(indent + 1))
|
|
421
421
|
return "\n".join(lines)
|
|
422
422
|
|
|
423
|
+
def _repr_latex_(self) -> Union[str, None]:
|
|
424
|
+
"""Render this expression as inline LaTeX for Jupyter.
|
|
425
|
+
|
|
426
|
+
Jupyter calls this hook to display an object as math. The expression
|
|
427
|
+
is lowered to LaTeX and wrapped in ``$...$``. Nodes without a
|
|
428
|
+
registered LaTeX visitor return ``None`` so Jupyter falls back to the
|
|
429
|
+
plain ``__repr__``.
|
|
430
|
+
|
|
431
|
+
Returns:
|
|
432
|
+
The inline-math string ``"$...$"``, or ``None`` when the
|
|
433
|
+
expression contains a node the LaTeX backend does not support.
|
|
434
|
+
"""
|
|
435
|
+
from openscvx.symbolic.lower import to_latex
|
|
436
|
+
|
|
437
|
+
try:
|
|
438
|
+
return f"${to_latex(self)}$"
|
|
439
|
+
except NotImplementedError:
|
|
440
|
+
return None
|
|
441
|
+
|
|
423
442
|
def _hash_into(self, hasher: "hashlib._Hash") -> None:
|
|
424
443
|
"""Contribute this expression's structural identity to a hash.
|
|
425
444
|
|
|
@@ -84,6 +84,7 @@ if TYPE_CHECKING:
|
|
|
84
84
|
__all__ = [
|
|
85
85
|
"lower",
|
|
86
86
|
"lower_to_jax",
|
|
87
|
+
"to_latex",
|
|
87
88
|
"lower_cvxpy_constraints",
|
|
88
89
|
"create_cvxpy_variables",
|
|
89
90
|
"lower_symbolic_problem",
|
|
@@ -169,6 +170,47 @@ def lower_to_jax(exprs: Union[Expr, Sequence[Expr]]) -> Union[callable, list[cal
|
|
|
169
170
|
return fns
|
|
170
171
|
|
|
171
172
|
|
|
173
|
+
def to_latex(exprs: Union[Expr, Sequence[Expr]]) -> Union[str, list[str]]:
|
|
174
|
+
"""Lower symbolic expression(s) to LaTeX math string(s).
|
|
175
|
+
|
|
176
|
+
Convenience wrapper that creates a LatexLowerer and renders one or more
|
|
177
|
+
symbolic expressions as LaTeX. This is the escape hatch for rendering a
|
|
178
|
+
single expression (or a handful) — e.g. one entry of a problem's dynamics
|
|
179
|
+
dict — without going through ``Problem.to_latex``.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
exprs: Single expression or sequence of expressions to lower
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
- If exprs is a single Expr: Returns a single LaTeX string.
|
|
186
|
+
- If exprs is a sequence: Returns a list of LaTeX strings in order.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
Single expression::
|
|
190
|
+
|
|
191
|
+
x = ox.State("x", shape=(3,))
|
|
192
|
+
to_latex(ox.Norm(x) - 5.0)
|
|
193
|
+
# '\\left\\| x \\right\\| - 5'
|
|
194
|
+
|
|
195
|
+
Multiple expressions::
|
|
196
|
+
|
|
197
|
+
exprs = [ox.Norm(x), ox.Sum(x)]
|
|
198
|
+
to_latex(exprs)
|
|
199
|
+
# ['\\left\\| x \\right\\|', '\\sum x']
|
|
200
|
+
|
|
201
|
+
Note:
|
|
202
|
+
The returned strings carry no ``$`` delimiters — callers add their own.
|
|
203
|
+
Nodes without a registered LaTeX visitor (STL, Lie, spatial, ...) raise
|
|
204
|
+
``NotImplementedError``.
|
|
205
|
+
"""
|
|
206
|
+
from openscvx.symbolic.lowerers.latex import LatexLowerer
|
|
207
|
+
|
|
208
|
+
ll = LatexLowerer()
|
|
209
|
+
if isinstance(exprs, Expr):
|
|
210
|
+
return lower(exprs, ll)
|
|
211
|
+
return [lower(e, ll) for e in exprs]
|
|
212
|
+
|
|
213
|
+
|
|
172
214
|
def _tile_sparsity(pattern_2d: np.ndarray, K: int):
|
|
173
215
|
"""Convert a 2-D boolean pattern to CVXPY sparsity indices tiled over *K* slices.
|
|
174
216
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""LaTeX backend for lowering symbolic expressions to math strings.
|
|
2
|
+
|
|
3
|
+
This package implements the LaTeX lowering backend that converts symbolic
|
|
4
|
+
expression AST nodes into LaTeX math strings. Like the JAX and CVXPy
|
|
5
|
+
backends, lowering uses a visitor pattern where each expression type has a
|
|
6
|
+
corresponding visitor function registered via ``@visitor``.
|
|
7
|
+
|
|
8
|
+
The visitor functions are split across submodules that mirror the
|
|
9
|
+
``openscvx.symbolic.expr`` package structure. Importing this package
|
|
10
|
+
triggers registration of all visitors, which now cover every shipped node
|
|
11
|
+
type — arithmetic, linear algebra, elementwise math, arrays, constraints,
|
|
12
|
+
logic, spatial, Lie-group, STL, and ``Vmap``. A future node added without a
|
|
13
|
+
visitor raises ``NotImplementedError`` — the designed fallback until its
|
|
14
|
+
visitor lands. (The legacy ``stljax`` operators are intentionally left
|
|
15
|
+
unregistered; the GMSR ``ox.stl`` surface is the rendered one.)
|
|
16
|
+
|
|
17
|
+
Example::
|
|
18
|
+
|
|
19
|
+
from openscvx.symbolic.lowerers.latex import LatexLowerer
|
|
20
|
+
|
|
21
|
+
lowerer = LatexLowerer()
|
|
22
|
+
s = lowerer.lower(ox.Norm(x) - 5.0) # '\\left\\| x \\right\\| - 5'
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# Import visitor modules to trigger @visitor registration.
|
|
26
|
+
# Each module populates _LATEX_VISITORS as a side effect of import.
|
|
27
|
+
from openscvx.symbolic.lowerers.latex import (
|
|
28
|
+
arithmetic, # noqa: F401
|
|
29
|
+
array, # noqa: F401
|
|
30
|
+
constraint, # noqa: F401
|
|
31
|
+
control, # noqa: F401
|
|
32
|
+
expr, # noqa: F401
|
|
33
|
+
lie, # noqa: F401
|
|
34
|
+
linalg, # noqa: F401
|
|
35
|
+
logic, # noqa: F401
|
|
36
|
+
math, # noqa: F401
|
|
37
|
+
spatial, # noqa: F401
|
|
38
|
+
state, # noqa: F401
|
|
39
|
+
stl, # noqa: F401
|
|
40
|
+
vmap, # noqa: F401
|
|
41
|
+
)
|
|
42
|
+
from openscvx.symbolic.lowerers.latex._lowerer import (
|
|
43
|
+
LatexLowerer,
|
|
44
|
+
format_constant,
|
|
45
|
+
latex_symbol,
|
|
46
|
+
)
|
|
47
|
+
from openscvx.symbolic.lowerers.latex.formulation import problem_to_latex
|
|
48
|
+
|
|
49
|
+
__all__ = ["LatexLowerer", "latex_symbol", "format_constant", "problem_to_latex"]
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""LatexLowerer class definition and shared string helpers.
|
|
2
|
+
|
|
3
|
+
The visitor methods that populate the registry live in sibling modules
|
|
4
|
+
(``arithmetic``, ``math``, ``linalg``, etc.) and are registered via
|
|
5
|
+
``@visitor`` at import time. This module also carries the pieces every
|
|
6
|
+
visitor leans on: precedence-aware parenthesization (:data:`_PRECEDENCE`,
|
|
7
|
+
:func:`wrap`), symbol rendering (:func:`latex_symbol`), and constant rendering
|
|
8
|
+
(:func:`format_constant`).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from openscvx.symbolic.expr import Expr
|
|
14
|
+
from openscvx.symbolic.expr.arithmetic import Add, MatMul, Mul, Neg, Power, Sub
|
|
15
|
+
from openscvx.symbolic.lowerers.latex._registry import dispatch
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LatexLowerer:
|
|
19
|
+
"""LaTeX backend for lowering symbolic expressions to math strings.
|
|
20
|
+
|
|
21
|
+
This class implements the visitor pattern for converting symbolic
|
|
22
|
+
expression AST nodes to LaTeX. Each expression type has a corresponding
|
|
23
|
+
visitor function decorated with ``@visitor`` that returns a ``str``.
|
|
24
|
+
|
|
25
|
+
The lowering is recursive: each visitor lowers its children first, then
|
|
26
|
+
composes them into a LaTeX fragment, calling :func:`wrap` on children that
|
|
27
|
+
may need parentheses for the surrounding precedence.
|
|
28
|
+
|
|
29
|
+
Unlike :class:`~openscvx.symbolic.lowerers.jax.JaxLowerer`, the LaTeX
|
|
30
|
+
lowerer is stateless — rendering is pure string assembly with no trace,
|
|
31
|
+
node cache, or memoization.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
Lower an expression to a LaTeX string::
|
|
35
|
+
|
|
36
|
+
lowerer = LatexLowerer()
|
|
37
|
+
s = lowerer.lower(ox.Norm(x) - 5.0)
|
|
38
|
+
# '\\left\\| x \\right\\| - 5'
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def lower(self, expr: Expr) -> str:
|
|
42
|
+
"""Lower a symbolic expression to a LaTeX string.
|
|
43
|
+
|
|
44
|
+
Main entry point for lowering. Delegates to :func:`dispatch`, which
|
|
45
|
+
looks up the appropriate visitor based on the expression type.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
expr: Symbolic expression to lower (any Expr subclass)
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
LaTeX math string (no ``$`` delimiters — callers add their own)
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
NotImplementedError: If no visitor exists for the expression type
|
|
55
|
+
"""
|
|
56
|
+
return dispatch(self, expr)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Precedence-aware parenthesization: visitors call wrap() on children, passing
|
|
60
|
+
# the precedence of the surrounding context. Leaves and function-style nodes
|
|
61
|
+
# (Norm, Sin, Div-as-\frac, ...) are self-delimiting and default to 10.
|
|
62
|
+
_PRECEDENCE = {Add: 1, Sub: 1, Mul: 2, MatMul: 2, Neg: 3, Power: 4}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def wrap(lowerer: LatexLowerer, child: Expr, parent_precedence: int) -> str:
|
|
66
|
+
"""Lower ``child`` and parenthesize it if the parent binds tighter.
|
|
67
|
+
|
|
68
|
+
A child whose precedence is strictly lower than ``parent_precedence`` is
|
|
69
|
+
wrapped in ``\\left( ... \\right)``. Leaves and function-style nodes
|
|
70
|
+
default to precedence 10 and are never wrapped.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
lowerer: The LatexLowerer performing the lowering.
|
|
74
|
+
child: The child expression to render.
|
|
75
|
+
parent_precedence: Precedence of the surrounding operator; pass a value
|
|
76
|
+
one higher than the operator's own precedence for the right operand
|
|
77
|
+
of a non-associative operator (e.g. ``Sub``) so equal-precedence
|
|
78
|
+
children are still parenthesized.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
The child's LaTeX, parenthesized when required.
|
|
82
|
+
"""
|
|
83
|
+
s = lowerer.lower(child)
|
|
84
|
+
if _PRECEDENCE.get(type(child), 10) < parent_precedence:
|
|
85
|
+
return rf"\left( {s} \right)"
|
|
86
|
+
return s
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# Greek-word names that map to a LaTeX command of the same name.
|
|
90
|
+
_GREEK = {
|
|
91
|
+
"alpha",
|
|
92
|
+
"beta",
|
|
93
|
+
"gamma",
|
|
94
|
+
"delta",
|
|
95
|
+
"epsilon",
|
|
96
|
+
"zeta",
|
|
97
|
+
"eta",
|
|
98
|
+
"theta",
|
|
99
|
+
"iota",
|
|
100
|
+
"kappa",
|
|
101
|
+
"lambda",
|
|
102
|
+
"mu",
|
|
103
|
+
"nu",
|
|
104
|
+
"xi",
|
|
105
|
+
"pi",
|
|
106
|
+
"rho",
|
|
107
|
+
"sigma",
|
|
108
|
+
"tau",
|
|
109
|
+
"upsilon",
|
|
110
|
+
"phi",
|
|
111
|
+
"chi",
|
|
112
|
+
"psi",
|
|
113
|
+
"omega",
|
|
114
|
+
"Gamma",
|
|
115
|
+
"Delta",
|
|
116
|
+
"Theta",
|
|
117
|
+
"Lambda",
|
|
118
|
+
"Xi",
|
|
119
|
+
"Pi",
|
|
120
|
+
"Sigma",
|
|
121
|
+
"Upsilon",
|
|
122
|
+
"Phi",
|
|
123
|
+
"Psi",
|
|
124
|
+
"Omega",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def latex_symbol(name: str) -> str:
|
|
129
|
+
"""Render a variable name as a LaTeX symbol.
|
|
130
|
+
|
|
131
|
+
Rules, in order:
|
|
132
|
+
|
|
133
|
+
- A Greek-word name renders as its command: ``'alpha'`` -> ``'\\alpha'``.
|
|
134
|
+
- A single character renders bare: ``'x'`` -> ``'x'``.
|
|
135
|
+
- A ``base_sub`` name splits on the first underscore into a subscript:
|
|
136
|
+
``'x_pos'`` -> ``'x_{\\mathrm{pos}}'`` (base and subscript are rendered
|
|
137
|
+
recursively).
|
|
138
|
+
- Any other multi-letter name renders upright: ``'position'`` ->
|
|
139
|
+
``'\\mathrm{position}'``, with underscores escaped.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
name: The variable name to render.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
The LaTeX fragment for the name.
|
|
146
|
+
"""
|
|
147
|
+
if name in _GREEK:
|
|
148
|
+
return rf"\{name}"
|
|
149
|
+
if len(name) == 1:
|
|
150
|
+
return name
|
|
151
|
+
# base_sub -> subscript, when the split yields non-empty halves.
|
|
152
|
+
if "_" in name and not name.startswith("_") and not name.endswith("_"):
|
|
153
|
+
base, sub = name.split("_", 1)
|
|
154
|
+
return rf"{latex_symbol(base)}_{{{latex_symbol(sub)}}}"
|
|
155
|
+
escaped = name.replace("_", r"\_")
|
|
156
|
+
return rf"\mathrm{{{escaped}}}"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def merge_subscript(base: str, sub) -> str:
|
|
160
|
+
"""Append a subscript to a symbol, merging into an existing subscript group.
|
|
161
|
+
|
|
162
|
+
If ``base`` already ends in a ``_{...}`` group, ``sub`` is spliced in before
|
|
163
|
+
the closing brace as ``,<sub>`` (``x_{\\mathrm{velocity}}`` + ``0`` ->
|
|
164
|
+
``x_{\\mathrm{velocity},0}``); otherwise ``_{<sub>}`` is appended
|
|
165
|
+
(``\\theta`` + ``0`` -> ``\\theta_{0}``). This never emits a double
|
|
166
|
+
subscript like ``x_{a}_{b}``, which is invalid LaTeX.
|
|
167
|
+
|
|
168
|
+
The trailing group is matched by depth-scanning braces (not a naive
|
|
169
|
+
``[^{}]`` regex), so nested groups like ``\\mathrm{velocity}`` inside the
|
|
170
|
+
subscript are handled, and a non-subscript trailing group such as the
|
|
171
|
+
``\\dot{x}`` accent or ``\\mathrm{position}`` correctly appends instead of
|
|
172
|
+
splicing.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
base: The symbol to subscript (already-rendered LaTeX).
|
|
176
|
+
sub: The subscript to add (stringified).
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
``base`` with ``sub`` merged into its subscript.
|
|
180
|
+
"""
|
|
181
|
+
sub = str(sub)
|
|
182
|
+
if base.endswith("}"):
|
|
183
|
+
depth = 0
|
|
184
|
+
open_idx = None
|
|
185
|
+
for i in range(len(base) - 1, -1, -1):
|
|
186
|
+
if base[i] == "}":
|
|
187
|
+
depth += 1
|
|
188
|
+
elif base[i] == "{":
|
|
189
|
+
depth -= 1
|
|
190
|
+
if depth == 0:
|
|
191
|
+
open_idx = i
|
|
192
|
+
break
|
|
193
|
+
# A trailing group is a subscript group only when its `{` follows `_`.
|
|
194
|
+
if open_idx is not None and open_idx >= 1 and base[open_idx - 1] == "_":
|
|
195
|
+
return f"{base[:-1]},{sub}}}"
|
|
196
|
+
return rf"{base}_{{{sub}}}"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def state_symbol(name: str) -> str:
|
|
200
|
+
"""Render a state's role-prefixed symbol: ``x_{<sym>}``.
|
|
201
|
+
|
|
202
|
+
States are grounded in the skeleton's ``f(x, u)`` by prefixing the role
|
|
203
|
+
letter ``x``; ``<sym>`` is the :func:`latex_symbol` rendering of the name
|
|
204
|
+
(``x_{\\mathrm{velocity}}``, ``x_{\\theta}``). A state literally named
|
|
205
|
+
``x`` renders bare as ``x``.
|
|
206
|
+
"""
|
|
207
|
+
if name == "x":
|
|
208
|
+
return "x"
|
|
209
|
+
return merge_subscript("x", latex_symbol(name))
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def control_symbol(name: str) -> str:
|
|
213
|
+
"""Render a control's role-prefixed symbol: ``u_{<sym>}``.
|
|
214
|
+
|
|
215
|
+
Mirrors :func:`state_symbol` with the role letter ``u``; a control literally
|
|
216
|
+
named ``u`` renders bare as ``u``.
|
|
217
|
+
"""
|
|
218
|
+
if name == "u":
|
|
219
|
+
return "u"
|
|
220
|
+
return merge_subscript("u", latex_symbol(name))
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _format_scalar(value: float) -> str:
|
|
224
|
+
"""Render a scalar with ``%g`` (compact, no trailing zeros)."""
|
|
225
|
+
return "%g" % value
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def format_constant(value: np.ndarray) -> str:
|
|
229
|
+
"""Render a numeric constant as LaTeX.
|
|
230
|
+
|
|
231
|
+
Scalars render via ``%g``. Vectors render as a column ``bmatrix`` and
|
|
232
|
+
matrices as a 2-D ``bmatrix``, up to 6 entries per axis. Larger arrays
|
|
233
|
+
render as a placeholder ``\\mathrm{const} \\in \\mathbb{R}^{...}`` rather
|
|
234
|
+
than an unreadable wall of numbers.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
value: The constant value (scalar, vector, matrix, or higher-rank
|
|
238
|
+
array).
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
The LaTeX fragment for the constant.
|
|
242
|
+
"""
|
|
243
|
+
arr = np.asarray(value)
|
|
244
|
+
|
|
245
|
+
if arr.ndim == 0:
|
|
246
|
+
return _format_scalar(arr.item())
|
|
247
|
+
|
|
248
|
+
if arr.ndim == 1:
|
|
249
|
+
if arr.shape[0] > 6:
|
|
250
|
+
return rf"\mathrm{{const}} \in \mathbb{{R}}^{{{arr.shape[0]}}}"
|
|
251
|
+
body = r" \\ ".join(_format_scalar(v) for v in arr)
|
|
252
|
+
return rf"\begin{{bmatrix}} {body} \end{{bmatrix}}"
|
|
253
|
+
|
|
254
|
+
if arr.ndim == 2:
|
|
255
|
+
m, n = arr.shape
|
|
256
|
+
if m > 6 or n > 6:
|
|
257
|
+
return rf"\mathrm{{const}} \in \mathbb{{R}}^{{{m} \times {n}}}"
|
|
258
|
+
rows = [" & ".join(_format_scalar(v) for v in row) for row in arr]
|
|
259
|
+
body = r" \\ ".join(rows)
|
|
260
|
+
return rf"\begin{{bmatrix}} {body} \end{{bmatrix}}"
|
|
261
|
+
|
|
262
|
+
shape = r" \times ".join(str(d) for d in arr.shape)
|
|
263
|
+
return rf"\mathrm{{const}} \in \mathbb{{R}}^{{{shape}}}"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Visitor registry for the LaTeX lowerer.
|
|
2
|
+
|
|
3
|
+
This module holds the shared visitor dictionary, the ``@visitor`` decorator,
|
|
4
|
+
and the ``dispatch`` function. It is deliberately kept free of heavyweight
|
|
5
|
+
imports (no expression subclasses beyond the base ``Expr``) so that the visitor
|
|
6
|
+
modules can import from it without circular-dependency issues.
|
|
7
|
+
|
|
8
|
+
The registry is independent of the JAX and CVXPy registries. It covers every
|
|
9
|
+
shipped node type; a future node added without a registered LaTeX visitor (or a
|
|
10
|
+
deliberately-unregistered legacy node like the ``stljax`` operators) raises
|
|
11
|
+
``NotImplementedError`` on dispatch — the designed fallback.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Any, Callable, Dict, Type
|
|
15
|
+
|
|
16
|
+
from openscvx.symbolic.expr import Expr
|
|
17
|
+
|
|
18
|
+
_LATEX_VISITORS: Dict[Type[Expr], Callable] = {}
|
|
19
|
+
"""Registry mapping expression types to their visitor functions."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def visitor(expr_cls: Type[Expr]) -> Callable[[Callable], Callable]:
|
|
23
|
+
"""Decorator to register a visitor function for an expression type.
|
|
24
|
+
|
|
25
|
+
This decorator registers a visitor method to handle a specific expression
|
|
26
|
+
type during LaTeX lowering. The decorated function is stored in
|
|
27
|
+
_LATEX_VISITORS and will be called by dispatch() when lowering that
|
|
28
|
+
expression type.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
expr_cls: The Expr subclass this visitor handles (e.g., Add, Mul, Norm)
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Decorator function that registers the visitor and returns it unchanged
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
Register a visitor function for the Add expression::
|
|
38
|
+
|
|
39
|
+
@visitor(Add)
|
|
40
|
+
def _visit_add(lowerer, node: Add):
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
Note:
|
|
44
|
+
Dispatch is exact-type, so shared behaviour must be registered per
|
|
45
|
+
concrete class. Multiple types can share a visitor by stacking
|
|
46
|
+
decorators::
|
|
47
|
+
|
|
48
|
+
@visitor(Equality)
|
|
49
|
+
@visitor(Inequality)
|
|
50
|
+
def _visit_constraint(lowerer, node: Constraint):
|
|
51
|
+
...
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def register(fn: Callable[[Any, Expr], str]):
|
|
55
|
+
_LATEX_VISITORS[expr_cls] = fn
|
|
56
|
+
return fn
|
|
57
|
+
|
|
58
|
+
return register
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def dispatch(lowerer: Any, expr: Expr) -> str:
|
|
62
|
+
"""Dispatch an expression to its registered visitor function.
|
|
63
|
+
|
|
64
|
+
Looks up the visitor function for the expression's type and calls it.
|
|
65
|
+
This is the core of the visitor pattern implementation.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
lowerer: The LatexLowerer instance (provides context for visitor methods)
|
|
69
|
+
expr: The expression node to lower
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
The result of calling the visitor function (a LaTeX string)
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
NotImplementedError: If no visitor is registered for the expression type
|
|
76
|
+
"""
|
|
77
|
+
fn = _LATEX_VISITORS.get(type(expr))
|
|
78
|
+
if fn is None:
|
|
79
|
+
raise NotImplementedError(
|
|
80
|
+
f"{lowerer.__class__.__name__!r} has no visitor for {type(expr).__name__}"
|
|
81
|
+
)
|
|
82
|
+
return fn(lowerer, expr)
|